I have been looking in to you some code wasn't working. Everything looks fine except for the following line.
Transport = Transport?? MockITransportUtil.GetMock(true);
Before that line is executed Transport is null. I see the GetMock executed and that it returns a non null object. After that line Transport is still null;
I looked at the IL that was generated an it looks fine to me.
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
We see the function get called and stfld should take the return value and set the field.
So I then looked at the assembly I see the call get made but it looks like the return in RAX gets blown away by the next call and is lost.
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]
if I use an if statement or a ?: operator everyting works fine.
Visual Studio 2013
I have create a psudo minimal reproduction.
class simple
{
public A MyA = null;
public B MyB = null;
public void SetUp()
{
MyA = MyA ?? new A();
MyB = new B();// Put breakpoint here
}
}
If you set a breakpoint on the indicated line and look at the value of MyA in the debugger it will still be null(only if building in x64). if you execute the next line it will set the value. I have not been able to reproduce the assessment not happening at all. Its very clear in the disassembly the execution for the next line has begun before the assignment takes place.
Here is a link to the ms connect site
The JIT compiler reads the bytecodes in many sections (or in full, rarely) and compiles them dynamically into machine code so the program can run faster.
The JIT compiler is enabled by default, and is activated when a Java method is called. The JIT compiler compiles the bytecodes of that method into native machine code, compiling it "just in time" to run. When a method has been compiled, the JVM calls the compiled code of that method directly instead of interpreting it.
The JIT compiler converts the Microsoft Intermediate Language(MSIL) or Common Intermediate Language(CIL) into the machine code. This is done before the MSIL or CIL can be executed.
A Just-In-Time (JIT) compiler is a feature of the run-time interpreter, that instead of interpreting bytecode every time a method is invoked, will compile the bytecode into the machine code instructions of the running machine, and then invoke this object code instead.
MyB = new B();// Put breakpoint here
The problem is the breakpoint, not the code generation. The x64 jitter flubs this, it generates inaccurate debugging info. It emits line number info for the statement incorrectly, using a code address that's still part of the previous statement.
You can tell from the disassembly you posted, the code at addresses F7A0 through F7A8 are still part of the ?? statement. The branch to F7AC is the real one, that's where the next statement starts. So it should have said that F7AC was the start of the next statement, not F7A0.
The consequences of this bug is that the debugger may never stop at the breakpoint. You can see this for yourself by altering your repro code and write public A MyA = new A();
And that if it does stop then the assignment isn't executed yet. So you still see the variable having the previous value, null in your case. A single step resolves it, albeit that it depends on what the next statement looks like.
Rest assured that this only goes wrong when you debug, the program still operates correctly. Just keep this quirk in mind, afaik it only goes wrong for the ?? operator. You can tell it doesn't get used much :) Albeit that most programmers only ever debug the 32-bit version of their program, the default project settings heavily encourage it.
The problem is being addressed as we speak, don't expect your Connect report to have an affect, Microsoft is well aware of this bug. The jitter team at Microsoft has rewritten the x64 jitter completely, it is currently in CTP2. I'd estimate another year or so before it is released.
I got an update from MS that this was indeed a real problem and has been fixed in the upcoming release of the x64 jiter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With