.maxstack 65535

Topics: Metadata Model, PE Writer
Apr 29, 2010 at 11:00 PM
.method private hidebysig virtual instance class Alpha IWrapper<Alpha>.get_Instance() cil managed
    .override IWrapper`1<class Alpha>::get_Instance
    .maxstack 65535
    L_0000: ldnull 
    L_0001: ret 

Is this ok? Will it actually allocate a giant stack frame just to return null?

May 16, 2010 at 6:35 PM
Edited May 16, 2010 at 8:27 PM

Another example of ".maxstack 65535" generated using MutableCodeModel.


.method public hidebysig instance class [Acme.Tool.Implementation]Acme.Tool.Implementation.PropertyChange ValidateSetName(string 'value') cil managed
    .maxstack 65535
    .locals init (
        [0] class [Acme.Tool.Declaration]Acme.Tool.Declaration.ValidationResult result)
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: call class [Acme.Tool.Declaration]Acme.Tool.Declaration.ValidationResult [ToolExtension1]ToolExtension1.Part/Name::Validate(class [ToolExtension1]ToolExtension1.IPart, string)
    L_0007: stloc.0 
    L_0008: ldloc.0 
    L_0009: callvirt instance bool [Acme.Tool.Declaration]Acme.Tool.Declaration.ValidationResult::get_IsSuccessful()
    L_000e: brfalse.s L_0016
    L_0010: newobj instance void [Acme.Tool.Implementation]Acme.Tool.Implementation.TrivialViablePropertyChange::.ctor()
    L_0015: ret 
    L_0016: ldloc.0 
    L_0017: callvirt instance string [Acme.Tool.Declaration]Acme.Tool.Declaration.ValidationResult::get_Reason()
    L_001c: newobj instance void [Acme.Tool.Implementation]Acme.Tool.Implementation.NonViablePropertyChange::.ctor(string)
    L_0021: ret 


May 16, 2010 at 6:47 PM

It is not a total disaster if the stack underflows like this, but I would like to find and fix all cases where it happens.

Perhaps the easiest way to report this is to debug the CodeModelToIL visitor and set a breakpoint for this eventuality and then just tell me what I did wrong. :-)

Alternatively, if you can provide me with some sort of error repro that I can debug, I'd be very much obliged.

May 16, 2010 at 7:48 PM
Edited May 16, 2010 at 7:49 PM

Edited: well, the problem are not static calls but non-static calls.


    public override void Visit(IMethodCall methodCall) {
      if (methodCall.MethodToCall == Dummy.MethodReference) return;
      if (this.contractProvider != null && methodCall.MethodToCall.InternedKey == this.contractProvider.ContractMethods.StartContract.InternedKey) {
        IMethodContract/*?*/ methodContract = this.contractProvider.GetMethodContractFor(this.method);
        if (methodContract != null) this.Visit(methodContract);
      if (!methodCall.IsStaticCall)
      OperationCode call = OperationCode.Call;
      if (methodCall.IsVirtualCall) {
        call = OperationCode.Callvirt;
        IManagedPointerTypeReference mpt = methodCall.ThisArgument.Type as IManagedPointerTypeReference;
        if (mpt != null)
          this.generator.Emit(OperationCode.Constrained_, mpt.TargetType);
      this.generator.Emit(call, methodCall.MethodToCall);
      this.StackSize -= (ushort)IteratorHelper.EnumerableCount(methodCall.Arguments);

      // ----------------------------------------------------------------
      // NON STATIC CALL. Here StackSize is 0 but you decrement it
      // ----------------------------------------------------------------

      if (!methodCall.IsStaticCall) this.StackSize--;
      if (methodCall.Type.TypeCode != PrimitiveTypeCode.Void)



May 16, 2010 at 7:50 PM

there are two arguments for that call (methodCall.Arguments.Count == 2)

May 16, 2010 at 7:52 PM

The code seems ok, so maybe you are not incrementing it somewhere else?

May 16, 2010 at 7:59 PM
Edited May 16, 2010 at 8:13 PM

Ok. I found the problem. I was calling a static method so ThisArgument was not set but I didn't set IsStatic to true.

Now this is a bug in my code but it stresses a weak point in CCI: it's very easy to generate bad metadata with it.

Does it make any sense to generate a non static call to a static method?

Why nothing happens if an instance call has a dummy "this" instance?

If you want to make it "smart" CCI should treat a method call without "this" instance to a static method as a static call.

Otherwise - if you want it to behave in a "strict" way - it should complain or abort.

This piece of code has the responsability to emit code for both loading the "this" instance and calling the method so it has all the information to decide if something is wrong.

Another thing: maybe using a normal Int32 for the "current simulated stack size" would allow you to detect similar problems in general.

May 16, 2010 at 8:08 PM

One of the very big gaps in the current set of CCI modules is an object model verifier. One could, in principle, mandate all implementations of the object model to maintain certain invariants, such as thou shalt not call a static method with a virtual call, but that puts a large burdon on each and every implementation and it makes it difficult to construct an object model incrementally (i.e. to have states where the object model is delibaretely invalid).

My preference is to have a separate visitor that will check all of these conditions and that will report problems in a very clear manner (somewhat like peverify, but a lot a better ;-).

So far, however, none of the people that have signed up to build such an animal has been able to realize their good intentions. Nor do I have enough free time to tackle such a beast myself.