c# - Es el JIT generando el código incorrecto
(2)
Recibí una actualización de MS de que esto era realmente un problema real y se ha corregido en la próxima versión del J64 x64.
Te he estado buscando algún código que no funcionaba. Todo se ve bien a excepción de la siguiente línea.
Transport = Transport?? MockITransportUtil.GetMock(true);
Antes de que se ejecute esa línea, el transporte es nulo. Veo que se ejecuta GetMock y que devuelve un objeto no nulo. Después de esa línea, el transporte sigue siendo nulo;
Miré la IL que se generó y me parece bien.
IL_0002: ldarg.0
IL_0003: ldfld class [Moq]Moq.Mock`1<class [CommLibNet]CommLibNET.ITransport> Curex.Services.Common.UnitTests.Messaging.TestIGuaranteedSubscriptionBase::Transport
IL_0008: dup
IL_0009: brtrue.s IL_0012
IL_000b: pop
IL_000c: ldc.i4.1
IL_000d: call class [Moq]Moq.Mock`1<class [CommLibNet]CommLibNET.ITransport> Curex.Services.Common.UnitTests.Mocking.MockITransportUtil::GetMock(bool)
IL_0012: stfld class [Moq]Moq.Mock`1<class [CommLibNet]CommLibNET.ITransport> Curex.Services.Common.UnitTests.Messaging.TestIGuaranteedSubscriptionBase::Transport
Vemos que la función es llamada y stfld debería tomar el valor de retorno y establecer el campo.
Entonces miré el conjunto que veo cuando se realiza la llamada, pero parece que la devolución de RAX se desvanece con la siguiente llamada y se pierde.
Transport = Transport?? MockITransportUtil.GetMock(true);
000007FE9236F776 mov rax,qword ptr [rbp+0B0h]
000007FE9236F77D mov rax,qword ptr [rax+20h]
000007FE9236F781 mov qword ptr [rbp+20h],rax
000007FE9236F785 mov rcx,qword ptr [rbp+20h]
000007FE9236F789 mov rax,qword ptr [rbp+0B0h]
000007FE9236F790 mov qword ptr [rbp+28h],rax
000007FE9236F794 test rcx,rcx
000007FE9236F797 jne 000007FE9236F7AC
000007FE9236F799 mov cl,1
000007FE9236F79B call 000007FE92290608
//var x = ReferenceEquals(null, Transport) ? MockITransportUtil.GetMock(true) : Transport;
ListerFactory = ListerFactory ?? MockIListenerUtil.GetMockSetupWithAction((a) => invokingAction = a);
000007FE9236F7A0 mov qword ptr [rbp+30h],rax
000007FE9236F7A4 mov rax,qword ptr [rbp+30h]
000007FE9236F7A8 mov qword ptr [rbp+20h],rax
000007FE9236F7AC mov rcx,qword ptr [rbp+28h]
Si utilizo una instrucción if o un operador?: todo funciona bien.
Visual Studio 2013
EDITAR
He creado una reproducción mínima de psudo.
class simple
{
public A MyA = null;
public B MyB = null;
public void SetUp()
{
MyA = MyA ?? new A();
MyB = new B();// Put breakpoint here
}
}
Si establece un punto de interrupción en la línea indicada y observa el valor de MyA en el depurador, seguirá siendo nulo (solo si se genera en x64). Si ejecuta la siguiente línea se establecerá el valor. No he podido reproducir la evaluación que no está sucediendo en absoluto. Es muy claro en el desmontaje que la ejecución de la siguiente línea ha comenzado antes de que tenga lugar la asignación.
Editar 2
Aquí hay un link al sitio ms connect
MyB = new B();// Put breakpoint here
El problema es el punto de interrupción, no la generación de código. La fluctuación de jitter x64 esto, genera información de depuración inexacta. Emite la información del número de línea de la declaración de manera incorrecta, utilizando una dirección de código que aún forma parte de la declaración anterior.
¿Se puede decir del desmontaje que publicó, el código en las direcciones F7A0 a F7A8 todavía son parte de la? declaración. La rama a F7AC es la real, ahí es donde comienza la siguiente declaración. Así que debería haber dicho que F7AC fue el comienzo de la siguiente declaración, no F7A0.
Las consecuencias de este error es que el depurador nunca puede detenerse en el punto de interrupción. Puedes ver esto por ti mismo al modificar tu código de reproducción y escribir en public A MyA = new A();
Y que si se detiene, la tarea aún no se ha ejecutado. Por lo tanto, aún verá la variable que tiene el valor anterior, nulo en su caso. Un solo paso lo resuelve, aunque depende de cómo se vea la siguiente declaración.
Tenga la seguridad de que esto solo sale mal cuando se depura, el programa aún funciona correctamente. Solo ten en cuenta esta peculiaridad, ¿no se equivoca? operador. Se puede decir que no se usa mucho :) Aunque la mayoría de los programadores solo depuran la versión de 32 bits de su programa, la configuración predeterminada del proyecto lo alienta.
El problema se está solucionando mientras hablamos, no espere que su informe Connect tenga un efecto, Microsoft está al tanto de este error. El equipo de jitter en Microsoft ha reescrito el jitter x64 completamente , actualmente se encuentra en CTP2. Estimo más o menos un año antes de su lanzamiento.