public override MethodIL EmitIL() { if (_owningType.BoxedValue == null) { // If this is the fake unboxing thunk for ByRef-like types, just make a method that throws. return(new ILStubMethodIL(this, new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ }, Array.Empty <LocalVariableDefinition>(), Array.Empty <object>())); } // Generate the unboxing stub. This loosely corresponds to following C#: // return BoxedValue.InstanceMethod([rest of parameters]) ILEmitter emit = new ILEmitter(); ILCodeStream codeStream = emit.NewCodeStream(); FieldDesc boxedValueField = _owningType.BoxedValue.InstantiateAsOpen(); // unbox to get a pointer to the value type codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldflda, emit.NewToken(boxedValueField)); // Load rest of the arguments for (int i = 0; i < _targetMethod.Signature.Length; i++) { codeStream.EmitLdArg(i + 1); } TypeDesc owner = _targetMethod.OwningType; MethodDesc methodToInstantiate = _targetMethod; if (owner.HasInstantiation) { MetadataType instantiatedOwner = (MetadataType)owner.InstantiateAsOpen(); methodToInstantiate = _targetMethod.Context.GetMethodForInstantiatedType(_targetMethod, (InstantiatedType)instantiatedOwner); } if (methodToInstantiate.HasInstantiation) { TypeSystemContext context = methodToInstantiate.Context; var inst = new TypeDesc[methodToInstantiate.Instantiation.Length]; for (int i = 0; i < inst.Length; i++) { inst[i] = context.GetSignatureVariable(i, true); } methodToInstantiate = context.GetInstantiatedMethod(methodToInstantiate, new Instantiation(inst)); } codeStream.Emit(ILOpcode.call, emit.NewToken(methodToInstantiate)); codeStream.Emit(ILOpcode.ret); return(emit.Link(this)); }
public static MethodDesc ToMethodDesc(this RuntimeMethodHandle rmh, TypeSystemContext typeSystemContext) { RuntimeTypeHandle declaringTypeHandle; MethodNameAndSignature nameAndSignature; RuntimeTypeHandle[] genericMethodArgs; if (!TypeLoaderEnvironment.Instance.TryGetRuntimeMethodHandleComponents(rmh, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs)) { return(null); } QMethodDefinition methodHandle; if (!TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSignature, out methodHandle)) { return(null); } TypeDesc declaringType = typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); TypeDesc declaringTypeDefinition = declaringType.GetTypeDefinition(); MethodDesc typicalMethod = null; if (methodHandle.IsNativeFormatMetadataBased) { var nativeFormatType = (NativeFormatType)declaringTypeDefinition; typicalMethod = nativeFormatType.MetadataUnit.GetMethod(methodHandle.NativeFormatHandle, nativeFormatType); } else if (methodHandle.IsEcmaFormatMetadataBased) { var ecmaFormatType = (EcmaType)declaringTypeDefinition; typicalMethod = ecmaFormatType.EcmaModule.GetMethod(methodHandle.EcmaFormatHandle); } Debug.Assert(typicalMethod != null); MethodDesc methodOnInstantiatedType = typicalMethod; if (declaringType != declaringTypeDefinition) { methodOnInstantiatedType = typeSystemContext.GetMethodForInstantiatedType(typicalMethod, (InstantiatedType)declaringType); } MethodDesc instantiatedMethod = methodOnInstantiatedType; if (genericMethodArgs != null) { Debug.Assert(genericMethodArgs.Length > 0); Instantiation genericMethodInstantiation = typeSystemContext.ResolveRuntimeTypeHandles(genericMethodArgs); typeSystemContext.GetInstantiatedMethod(methodOnInstantiatedType, genericMethodInstantiation); } return(instantiatedMethod); }
/// <summary> /// Retrieves method whose runtime handle is suitable for use with GVMLookupForSlot. /// </summary> public MethodDesc GetTargetOfGenericVirtualMethodCall(MethodDesc calledMethod) { // Should be a generic virtual method Debug.Assert(calledMethod.HasInstantiation && calledMethod.IsVirtual); // Needs to be either a concrete method, or a runtime determined form. Debug.Assert(!calledMethod.IsCanonicalMethod(CanonicalFormKind.Specific)); MethodDesc targetMethod = calledMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); MethodDesc targetMethodDefinition = targetMethod.GetMethodDefinition(); MethodDesc slotNormalizedMethodDefinition = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethodDefinition); // If the method defines the slot, we can use that. if (slotNormalizedMethodDefinition == targetMethodDefinition) { return(calledMethod); } // Normalize to the slot defining method MethodDesc slotNormalizedMethod = TypeSystemContext.GetInstantiatedMethod( slotNormalizedMethodDefinition, targetMethod.Instantiation); // Since the slot normalization logic modified what method we're looking at, we need to compute the new target of lookup. // // If we could use virtual method resolution logic with runtime determined methods, we wouldn't need what we're going // to do below. MethodDesc runtimeDeterminedSlotNormalizedMethod; if (!slotNormalizedMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) { // If the owning type is not generic, we can use it as-is, potentially only replacing the runtime-determined // method instantiation part. runtimeDeterminedSlotNormalizedMethod = slotNormalizedMethod.GetMethodDefinition(); } else { // If we need a runtime lookup but a normalization to the slot defining method happened above, we need to compute // the runtime lookup in terms of the base type that introduced the slot. // // To do that, we walk the base hierarchy of the runtime determined thing, looking for a type definition that matches // the slot-normalized virtual method. We then find the method on that type. TypeDesc runtimeDeterminedOwningType = calledMethod.OwningType; Debug.Assert(!runtimeDeterminedOwningType.IsInterface); while (!slotNormalizedMethod.OwningType.HasSameTypeDefinition(runtimeDeterminedOwningType)) { TypeDesc runtimeDeterminedBaseTypeDefinition = runtimeDeterminedOwningType.GetTypeDefinition().BaseType; if (runtimeDeterminedBaseTypeDefinition.HasInstantiation) { runtimeDeterminedOwningType = runtimeDeterminedBaseTypeDefinition.InstantiateSignature(runtimeDeterminedOwningType.Instantiation, default); } else { runtimeDeterminedOwningType = runtimeDeterminedBaseTypeDefinition; } } // Now get the method on the newly found type Debug.Assert(runtimeDeterminedOwningType.HasInstantiation); runtimeDeterminedSlotNormalizedMethod = TypeSystemContext.GetMethodForInstantiatedType( slotNormalizedMethod.GetTypicalMethodDefinition(), (InstantiatedType)runtimeDeterminedOwningType); } return(TypeSystemContext.GetInstantiatedMethod(runtimeDeterminedSlotNormalizedMethod, calledMethod.Instantiation)); }
public MethodDesc ResolveMethodID(long handle, bool throwIfNotFound = true) { lock (_lock) { MethodDescInfo minfo; if (_methods.TryGetValue(handle, out minfo)) { if (minfo.Method != null) return minfo.Method; TypeDesc owningType = ResolveTypeHandle(minfo.MethodDetailsTraceData.TypeID, throwIfNotFound); if (owningType == null) return null; MetadataType owningMDType = owningType as MetadataType; if (owningMDType == null) throw new Exception("Method not parented by MetadataType"); if ((minfo.MethodDetailsTraceData.MethodToken & 0xFF000000) != 0x06000000) { throw new Exception($"Invalid methoddef {minfo.MethodDetailsTraceData.MethodToken:4x}"); } MethodDefinitionHandle methoddef = MetadataTokens.MethodDefinitionHandle(minfo.MethodDetailsTraceData.MethodToken & 0xFFFFFF); MethodDesc uninstantiatedMethod = null; foreach (MethodDesc m in owningMDType.GetMethods()) { EcmaMethod ecmaMeth = m.GetTypicalMethodDefinition() as EcmaMethod; if (ecmaMeth == null) { continue; } if (ecmaMeth.Handle == methoddef) { uninstantiatedMethod = m; break; } } if (uninstantiatedMethod == null) { if (throwIfNotFound) { EcmaType ecmaType = owningMDType.GetTypeDefinition() as EcmaType; throw new Exception($"Unknown MethodID value finding MethodDef {minfo.MethodDetailsTraceData.MethodToken:x} on type {owningMDType} from module {ecmaType.Module.Assembly.GetName().Name}"); } return null; } // Instantiate the type if requested if (minfo.MethodDetailsTraceData.TypeParameters.Length != 0) { if (uninstantiatedMethod.Instantiation.Length != minfo.MethodDetailsTraceData.TypeParameters.Length) { throw new Exception($"Invalid TypeParameterCount {minfo.MethodDetailsTraceData.TypeParameters.Length} expected {uninstantiatedMethod.Instantiation.Length} as needed by '{uninstantiatedMethod}'"); } TypeDesc[] instantiation = new TypeDesc[minfo.MethodDetailsTraceData.TypeParameters.Length]; for (int i = 0; i < instantiation.Length; i++) { instantiation[i] = ResolveTypeHandle((long)minfo.MethodDetailsTraceData.TypeParameters[i], throwIfNotFound); if (instantiation[i] == null) return null; } minfo.Method = _context.GetInstantiatedMethod(uninstantiatedMethod, new Instantiation(instantiation)); if (minfo.Method == null) { if (throwIfNotFound) { StringBuilder s = new StringBuilder(); foreach (TypeDesc type in instantiation) { if (s.Length != 0) s.Append(','); s.Append(type); } throw new Exception("Unable to instantiate {uninstantiatedMethod} over <{s}>"); } return null; } } else { minfo.Method = uninstantiatedMethod; } if (minfo.Method == null) { if (throwIfNotFound) throw new Exception("Unknown MethodID value"); return null; } return minfo.Method; } else { if (throwIfNotFound) throw new Exception("Unknown MethodID value"); return null; } } }
/// <summary> /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// </summary> public MethodDesc GetReflectionInvokeStub(MethodDesc method) { // Methods we see here shouldn't be canonicalized, or we'll end up creating bastardized instantiations // (e.g. we instantiate over System.Object below.) Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any)); TypeSystemContext context = method.Context; var sig = method.Signature; ParameterMetadata[] paramMetadata = null; // Get a generic method that can be used to invoke method with this shape. MethodDesc thunk; var lookupSig = new DynamicInvokeMethodSignature(sig); if (!_dynamicInvokeThunks.TryGetValue(lookupSig, out thunk)) { thunk = new DynamicInvokeMethodThunk(_nodeFactory.CompilationModuleGroup.GeneratedAssembly.GetGlobalModuleType(), lookupSig); _dynamicInvokeThunks.Add(lookupSig, thunk); } // If the method has no parameters and returns void, we don't need to specialize if (sig.ReturnType.IsVoid && sig.Length == 0) { Debug.Assert(!thunk.HasInstantiation); return(thunk); } // // Instantiate the generic thunk over the parameters and the return type of the target method // TypeDesc[] instantiation = new TypeDesc[sig.ReturnType.IsVoid ? sig.Length : sig.Length + 1]; Debug.Assert(thunk.Instantiation.Length == instantiation.Length); for (int i = 0; i < sig.Length; i++) { TypeDesc parameterType = sig[i]; if (parameterType.IsByRef) { // strip ByRefType off the parameter (the method already has ByRef in the signature) parameterType = ((ByRefType)parameterType).ParameterType; } if (parameterType.IsPointer || parameterType.IsFunctionPointer) { // For pointer typed parameters, instantiate the method over IntPtr parameterType = context.GetWellKnownType(WellKnownType.IntPtr); } else if (parameterType.IsEnum) { // If the invoke method takes an enum as an input parameter and there is no default value for // that paramter, we don't need to specialize on the exact enum type (we only need to specialize // on the underlying integral type of the enum.) if (paramMetadata == null) { paramMetadata = method.GetParameterMetadata(); } bool hasDefaultValue = false; foreach (var p in paramMetadata) { // Parameter metadata indexes are 1-based (0 is reserved for return "parameter") if (p.Index == (i + 1) && p.HasDefault) { hasDefaultValue = true; break; } } if (!hasDefaultValue) { parameterType = parameterType.UnderlyingType; } } instantiation[i] = parameterType; } if (!sig.ReturnType.IsVoid) { TypeDesc returnType = sig.ReturnType; Debug.Assert(!returnType.IsByRef); // If the invoke method return an object reference, we don't need to specialize on the // exact type of the object reference, as the behavior is not different. if ((returnType.IsDefType && !returnType.IsValueType) || returnType.IsArray) { returnType = context.GetWellKnownType(WellKnownType.Object); } instantiation[sig.Length] = returnType; } return(context.GetInstantiatedMethod(thunk, new Instantiation(instantiation))); }
/// <summary> /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// </summary> public MethodDesc GetReflectionInvokeStub(MethodDesc method) { // Methods we see here shouldn't be canonicalized, or we'll end up creating bastardized instantiations // (e.g. we instantiate over System.Object below.) Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any)); TypeSystemContext context = method.Context; var sig = method.Signature; // Get a generic method that can be used to invoke method with this shape. MethodDesc thunk; var lookupSig = new DynamicInvokeMethodSignature(sig); if (!_dynamicInvokeThunks.TryGetValue(lookupSig, out thunk)) { // TODO: figure out a better owning type (for multifile) thunk = new DynamicInvokeMethodThunk(context.SystemModule.GetGlobalModuleType(), lookupSig); _dynamicInvokeThunks.Add(lookupSig, thunk); } // If the method has no parameters and returns void, we don't need to specialize if (sig.ReturnType.IsVoid && sig.Length == 0) { Debug.Assert(!thunk.HasInstantiation); return(thunk); } // // Instantiate the generic thunk over the parameters and the return type of the target method // TypeDesc[] instantiation = new TypeDesc[sig.ReturnType.IsVoid ? sig.Length : sig.Length + 1]; Debug.Assert(thunk.Instantiation.Length == instantiation.Length); for (int i = 0; i < sig.Length; i++) { TypeDesc parameterType = sig[i]; if (parameterType.IsByRef) { // strip ByRefType off the parameter (the method already has ByRef in the signature) parameterType = ((ByRefType)parameterType).ParameterType; } if (parameterType.IsPointer || parameterType.IsFunctionPointer) { // For pointer typed parameters, instantiate the method over IntPtr parameterType = context.GetWellKnownType(WellKnownType.IntPtr); } else if (parameterType.IsDefType) { // TODO: optimize enum types with no default value // DefType* paramDefType = parameterType->as<DefType> (); // // If the invoke method takes an enum as an input paramter and there is no default value for // // that paramter, we don't need to specialize on the exact enum type (we only need to specialize // // on the underlying integral type of the enum.) // if (paramDefType && (!IsPdHasDefault(methodToInvoke->Parameters()[index].Attributes())) && paramDefType->IsEnum()) // { // CorElementType underlyingElemType = paramDefType->InternalElementType(); // parameterType = paramDefType->GetLoaderContext()->GetElementType(underlyingElemType); // } } instantiation[i] = parameterType; } if (!sig.ReturnType.IsVoid) { TypeDesc returnType = sig.ReturnType; Debug.Assert(!returnType.IsByRef); // If the invoke method return an object reference, we don't need to specialize on the // exact type of the object reference, as the behavior is not different. if ((returnType.IsDefType && !returnType.IsValueType) || returnType.IsArray) { returnType = context.GetWellKnownType(WellKnownType.Object); } instantiation[sig.Length] = returnType; } return(context.GetInstantiatedMethod(thunk, new Instantiation(instantiation))); }