3

Using Mono.Cecil, given this method

private Instruction LoadOnStack(MetadataType type, object value)
{
    switch (type)
    {
        case MetadataType.String:
            return _processor.Create(OpCodes.Ldstr, (string) value);
        case MetadataType.Int32:
            return _processor.Create(OpCodes.Ldc_I4, (Int32) value);
        case MetadataType.Int64:
            return _processor.Create(OpCodes.Ldc_I8, (Int64) value);
        case MetadataType.Boolean:
            return _processor.Create(OpCodes.Ldc_I4, (bool) value ? 1 : 0);                
    }

    throw new NotSupportedException("Not a supported primitve parameter type: " + type);
}

How can I create an Instruction that can load value, when value is of type Type?

I notice when value is of type Type that I can test it for it like so :

if (value is TypeReference)
    return _processor.Create(???, ???);

But I can not figure out what I need to pass to Create to get the value to load correctly.

EDIT:

Using this :

if (value is TypeReference)
    return _processor.Create(OpCodes.Ldobj, type.Resolve());

Gets me one step closer. It seems to to accept the type. But then when I try to write the assembly, it errors out saying :

System.ArgumentException : Member 'System.Type' is declared in another module and needs to be imported
swestner
  • 1,881
  • 15
  • 19
  • I'm not familiar with what's available to il; is reflection an option? You could search `_processor` for a `Create` method whose second argument is of type `Type`? Alternately, everything can be boxed to an object...? – object88 Feb 05 '16 at 03:06
  • Are you trying to load an object instance of type `Type` onto the stack? – cbr Feb 05 '16 at 03:23
  • @object88 Reflection is not an option in this case. And the `OpCodes` have no entry for `Type`. I've tried `OpCodes.Ldobj`, `OpCodes.Ldind_Ref` and a few others I can't remeber – swestner Feb 05 '16 at 03:31
  • @cubrr Thats a good question. I believe it is. It is a named named argument from an `Attribute` thats being assigned using the `typeof` operator. – swestner Feb 05 '16 at 03:36
  • 2
    Try first emitting `ldtoken` with your type, then emit a call to `Type.GetTypeFromHandle` – cbr Feb 05 '16 at 03:51
  • That is, first `Create(OpCodes.Ldtoken, (Type) value)`, then immediately afterwards: `Create(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[1]{typeof(RuntimeTypeHandle)}))` – cbr Feb 05 '16 at 04:06
  • `Create` is picky about what it is passed. It will not accept `Type` or `MethodInfo` – swestner Feb 05 '16 at 05:37
  • @swestner Check out the Mono.Cecil sample [here](https://github.com/jbevain/cecil.samples/blob/master/Samples/ReplaceMethodCall/ReplaceMethodCall.Sample/Sample.cs). – cbr Feb 05 '16 at 06:50

1 Answers1

3

As @cubrr already pointed it out:

We use this code for MethodBoundaryAspect.Fody

private IList<Instruction> LoadValueOnStack(TypeReference parameterType, object value, ModuleDefinition module)
{
    if (parameterType.IsPrimitive || (parameterType.FullName == "System.String"))
        return new List<Instruction> {LoadPrimitiveConstOnStack(parameterType.MetadataType, value)};

    if (parameterType.IsValueType) // enum
    {
        var enumUnderlyingType = GetEnumUnderlyingType(parameterType.Resolve());
        return new List<Instruction> {LoadPrimitiveConstOnStack(enumUnderlyingType.MetadataType, value)};
    }

    if (parameterType.FullName == "System.Type")
    {
        var typeName = value.ToString();
        var typeReference = module.GetType(typeName, true);

        var typeTypeRef = _referenceFinder.GetTypeReference(typeof (Type));
        var methodReference = _referenceFinder.GetMethodReference(typeTypeRef, md => md.Name == "GetTypeFromHandle");

        var instructions = new List<Instruction>
        {
            _processor.Create(OpCodes.Ldtoken, typeReference),
            _processor.Create(OpCodes.Call, methodReference)
        };

        return instructions;
    }

    throw new NotSupportedException("Parametertype: " + parameterType);
}

private Instruction LoadPrimitiveConstOnStack(MetadataType type, object value)
{
    switch (type)
    {
        case MetadataType.String:
            return _processor.Create(OpCodes.Ldstr, (string) value);
        case MetadataType.Int32:
            return _processor.Create(OpCodes.Ldc_I4, (int) value);
        case MetadataType.Int64:
            return _processor.Create(OpCodes.Ldc_I8, (long) value);
        case MetadataType.Boolean:
            return _processor.Create(OpCodes.Ldc_I4, (bool) value ? 1 : 0);
    }

    throw new NotSupportedException("Not a supported primitive parameter type: " + type);
}

private static TypeReference GetEnumUnderlyingType(TypeDefinition self)
{
    foreach (var field in self.Fields)
    {
        if (field.Name == "value__")
            return field.FieldType;
    }

    throw new ArgumentException();
} 

where class ReferenceFinder is:

private readonly ModuleDefinition _moduleDefinition;

public ReferenceFinder(ModuleDefinition moduleDefinition)
{
    _moduleDefinition = moduleDefinition;
}

public MethodReference GetMethodReference(Type declaringType, Func<MethodDefinition, bool> predicate)
{
    return GetMethodReference(GetTypeReference(declaringType), predicate);
}

public MethodReference GetMethodReference(TypeReference typeReference, Func<MethodDefinition, bool> predicate)
{
    var typeDefinition = typeReference.Resolve();

    MethodDefinition methodDefinition;
    do
    {
        methodDefinition = typeDefinition.Methods.FirstOrDefault(predicate);
        typeDefinition = typeDefinition.BaseType == null 
            ? null 
            : typeDefinition.BaseType.Resolve();
    } while (methodDefinition == null && typeDefinition != null);

    return _moduleDefinition.Import(methodDefinition);
}

public TypeReference GetTypeReference(Type type)
{
    return _moduleDefinition.Import(type);
}
Ralf1108
  • 46
  • 1