Add a localDefinition to the current Scope

Aug 1, 2011 at 10:08 PM

Hi, I am new to the Cci and I am trying to implement a lock block around existing IL-Instructions.

My current problem is that when I try to add a new LocalVariable:

m_lockToken = new LocalDefinition();
            m_lockToken.Name = host.NameTable.GetNameFor("lockToken");
            m_lockToken.Type = host.PlatformType.SystemBoolean;
            m_lockToken.CompileTimeValue = new CompileTimeConstant() { Type = host.PlatformType.SystemBoolean, Value = false };
            generator.AddVariableToCurrentScope(m_lockToken);

I get a NullReferenceException in ILGenerator line 1100.
 public uint Length {
      get { return this.endLabel.Offset - this.startLabel.Offset; }
    }

Am I doing something wrong here?
Coordinator
Aug 1, 2011 at 11:40 PM

The null reference happens because a scope has been opened but not closed. AddVariableToCurrentScope will start a new scope if none are currently open. But scope closure will not happen automatically. This is an unfortunate inconsistency, but no nice fix springs to mind right now.

Aug 3, 2011 at 8:41 AM

Thank you very much for the quick reply.

generator.EndScope(); really did fix the problem. May be this should be included in the documentation. It would have taken me a lot of time to figure this on my own.

Aug 3, 2011 at 9:41 AM
Edited Aug 3, 2011 at 1:54 PM

The code now compiles, there is however another problem, I don't see the definition of the local variable anywhere and peverify fails with the following message:

[offset 0x00000005] Unable to resolve token. Here is the IL I am getting:

.method public hidebysig instance void  Deposit(int32 amount) cil managed
{
  // Code size       42 (0x2a)
  .maxstack  9
  .locals init (bool V_0)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldarg.0
    IL_0003:  ldloca.s   V_0
    IL_0005:  call       void [mscorlib]System.Threading.Monitor::Enter(object,
                                                                        bool&)
    IL_000a:  ldarg.0
    IL_000b:  ldarg.0
    IL_000c:  ldfld      int32 Bank.Account::balance
    IL_0011:  ldarg.1
    IL_0012:  add
    IL_0013:  stfld      int32 Bank.Account::balance
    IL_0018:  leave.s    IL_0028
  }  // end .try
  finally
  {
    IL_001a:  ldloc.s    V_0
    IL_001c:  ldc.i4.0
    IL_001d:  ceq
    IL_001f:  brtrue.s   IL_0027
    IL_0021:  ldarg.0
    IL_0022:  call       void [mscorlib]System.Threading.Monitor::Exit(object)
    IL_0027:  endfinally
  }  // end handler
  IL_0028:  nop
  IL_0029:  ret
} // end of method Account::Deposit

After I did some digging, I found our that this has something to do with the two calls I am making to System.Threading.Monitor.Enter/Exit. However I did not find anything looking at the CIL code that bothered me.

Any ideas on the situation?

Coordinator
Aug 3, 2011 at 2:28 PM

The local seems to be defined in the .locals directive. It is just named "V_0" instead of "lockToken" because ildasm is not finding the PDB file, I presume. It is a bit more difficult to speculate on why peverify is unhappy. At the very least I'd need to see the actual assembly.

Aug 3, 2011 at 2:36 PM

I have a little bit more information. My suspicion is that the current problem lies somewhere in the call of System.Threading.Monitor.Enter/Exit.

The way I was creating the call to the method is the following:

 var SystemThreadingMonitorType =
              new Microsoft.Cci.Immutable.NamespaceTypeReference(
                host, systemObject.ContainingUnitNamespace,
                nameTable.GetNameFor("Threading.Monitor"),
                0, falsefalsePrimitiveTypeCode.NotPrimitive);
 
            List<IParameterTypeInformation> pti = new List<IParameterTypeInformation>();
            pti.Add(new ParameterTypeInformation() { IsByReference = false, Type = systemObject, Index = 0 });
            pti.Add(new ParameterTypeInformation() { IsByReference = true, Type = systemBoolean, Index = 1 });
 
            this.m_monitorEnter = new Microsoft.Cci.MethodReference(
              this.host, SystemThreadingMonitorType,
              CallingConvention.Default,
              systemVoid,
              nameTable.GetNameFor("Enter"),
              0, pti);

I took this from one of the samples.

When I run the generated assembly I get the following message

Unhanded Exception: System.TypeLoadException: Could not load type 'System.Threading.Monitor' from assembly mscorlib.
Coordinator
Aug 3, 2011 at 2:45 PM

Nothing come to mind, I'm afraid. Please attach an actual generated assembly, or a source file that will generate it.

Alternatively, generate the same code from C# and closely study the two ildasm listings for differences.

Aug 3, 2011 at 2:51 PM
Edited Aug 3, 2011 at 3:10 PM

This is the generated pe-file:

http://www.atanasdimitrov.com/Downloads/ChessTest.exe.pe.zip

I compared the code with an empty lock-block implemented in C# and unfortunately there were no differences in the implementation, with the exception of a few NOPs.

Could the problem be in the fact that I am trying to get the name for Threading,Monitor, Threading is a namespace and may be it should somehow be accessed differently. I am really clueless here :(

========

I explored further. The call to Monitor Exit() succeeds. (It throws an exception in the execution but that's a different story). The only difference I see between both of them is the existence of the by reference parameter, so may be my mistake is somewhere there.

Aug 3, 2011 at 3:50 PM
Edited Aug 3, 2011 at 10:33 PM

Sorry for this whole last thing :(

I found the mistake. Nothing is wrong with the code. The only problem is that the called version of Monitor.Enter is new in .NET Framework 4. In previous versions the entire lock statement looks entirely different and calls the version of the method the only takes an object and no reference to a boolean.

However, on the other hand, the referenced assembly is

.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}

And now even with the 3.5 version of the lock implementation I get the exact same exception :(

==================

Just a little bit more info.

The

Unhanded Exception: System.TypeLoadException: Could not load type 'System.Threading.Monitor' from assembly mscorlib.

Exception remains. The assembly is has targeted framework 4 version. Could this be causing any problem. It's not the missing function stuff.
Sorry for the so scattered explaination but I am trying things all day long with a limited to 0 amount of success :(

==========================

Just tried with a .NET 2.0 Assembly. Same error:
{"Could not load type 'System.Threading.Monitor' from assembly 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.":"System.Threading.Monitor"}