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)); }
/// <summary> /// Constructs a new instance of <see cref="DelegateCreationInfo"/> set up to construct a delegate of type /// '<paramref name="delegateType"/>' pointing to '<paramref name="targetMethod"/>'. /// </summary> public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, NodeFactory factory, bool followVirtualDispatch) { TypeSystemContext context = delegateType.Context; DefType systemDelegate = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; int paramCountTargetMethod = targetMethod.Signature.Length; if (!targetMethod.Signature.IsStatic) { paramCountTargetMethod++; } DelegateInfo delegateInfo = context.GetDelegateInfo(delegateType.GetTypeDefinition()); int paramCountDelegateClosed = delegateInfo.Signature.Length + 1; bool closed = false; if (paramCountDelegateClosed == paramCountTargetMethod) { closed = true; } else { Debug.Assert(paramCountDelegateClosed == paramCountTargetMethod + 1); } if (targetMethod.Signature.IsStatic) { MethodDesc invokeThunk; MethodDesc initMethod; if (!closed) { // Open delegate to a static method if (targetMethod.IsNativeCallable) { // If target method is native callable, create a reverse PInvoke delegate initMethod = systemDelegate.GetKnownMethod("InitializeReversePInvokeThunk", null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ReversePinvokeThunk]; // You might hit this when the delegate is generic: you need to make the delegate non-generic. // If the code works on Project N, it's because the delegate is used in connection with // AddrOf intrinsic (please validate that). We don't have the necessary AddrOf expansion in // the codegen to make this work without actually constructing the delegate. You can't construct // the delegate if it's generic, even on Project N. // TODO: Make this throw something like "TypeSystemException.InvalidProgramException"? Debug.Assert(invokeThunk != null, "Delegate with a non-native signature for a NativeCallable method"); } else { initMethod = systemDelegate.GetKnownMethod("InitializeOpenStaticThunk", null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.OpenStaticThunk]; } } else { // Closed delegate to a static method (i.e. delegate to an extension method that locks the first parameter) invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ClosedStaticThunk]; initMethod = systemDelegate.GetKnownMethod("InitializeClosedStaticThunk", null); } var instantiatedDelegateType = delegateType as InstantiatedType; if (instantiatedDelegateType != null) { invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType); } return(new DelegateCreationInfo( factory.MethodEntrypoint(initMethod), targetMethod, TargetKind.ExactCallableAddress, factory.MethodEntrypoint(invokeThunk))); } else { if (!closed) { throw new NotImplementedException("Open instance delegates"); } string initializeMethodName = "InitializeClosedInstance"; MethodDesc targetCanonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); TargetKind kind; if (targetMethod.HasInstantiation) { if (targetMethod.IsVirtual) { initializeMethodName = "InitializeClosedInstanceWithGVMResolution"; kind = TargetKind.MethodHandle; } else { if (targetMethod != targetCanonMethod) { // Closed delegates to generic instance methods need to be constructed through a slow helper that // checks for the fat function pointer case (function pointer + instantiation argument in a single // pointer) and injects an invocation thunk to unwrap the fat function pointer as part of // the invocation if necessary. initializeMethodName = "InitializeClosedInstanceSlow"; } kind = TargetKind.ExactCallableAddress; } } else { if (followVirtualDispatch && targetMethod.IsVirtual) { if (targetMethod.OwningType.IsInterface) { kind = TargetKind.InterfaceDispatch; initializeMethodName = "InitializeClosedInstanceToInterface"; } else { kind = TargetKind.VTableLookup; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } else { kind = TargetKind.CanonicalEntrypoint; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } return(new DelegateCreationInfo( factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)), targetMethod, kind)); } }
/// <summary> /// Constructs a new instance of <see cref="DelegateCreationInfo"/> set up to construct a delegate of type /// '<paramref name="delegateType"/>' pointing to '<paramref name="targetMethod"/>'. /// </summary> public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, NodeFactory factory, bool followVirtualDispatch) { TypeSystemContext context = delegateType.Context; DefType systemDelegate = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; int paramCountTargetMethod = targetMethod.Signature.Length; if (!targetMethod.Signature.IsStatic) { paramCountTargetMethod++; } DelegateInfo delegateInfo = context.GetDelegateInfo(delegateType.GetTypeDefinition()); int paramCountDelegateClosed = delegateInfo.Signature.Length + 1; bool closed = false; if (paramCountDelegateClosed == paramCountTargetMethod) { closed = true; } else { Debug.Assert(paramCountDelegateClosed == paramCountTargetMethod + 1); } if (targetMethod.Signature.IsStatic) { MethodDesc invokeThunk; MethodDesc initMethod; if (!closed) { initMethod = systemDelegate.GetKnownMethod("InitializeOpenStaticThunk", null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.OpenStaticThunk]; } else { // Closed delegate to a static method (i.e. delegate to an extension method that locks the first parameter) invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ClosedStaticThunk]; initMethod = systemDelegate.GetKnownMethod("InitializeClosedStaticThunk", null); } var instantiatedDelegateType = delegateType as InstantiatedType; if (instantiatedDelegateType != null) { invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType); } return(new DelegateCreationInfo( factory.MethodEntrypoint(initMethod), targetMethod, TargetKind.ExactCallableAddress, factory.MethodEntrypoint(invokeThunk))); } else { if (!closed) { throw new NotImplementedException("Open instance delegates"); } string initializeMethodName = "InitializeClosedInstance"; MethodDesc targetCanonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); TargetKind kind; if (targetMethod.HasInstantiation) { if (followVirtualDispatch && targetMethod.IsVirtual) { initializeMethodName = "InitializeClosedInstanceWithGVMResolution"; kind = TargetKind.MethodHandle; } else { if (targetMethod != targetCanonMethod) { // Closed delegates to generic instance methods need to be constructed through a slow helper that // checks for the fat function pointer case (function pointer + instantiation argument in a single // pointer) and injects an invocation thunk to unwrap the fat function pointer as part of // the invocation if necessary. initializeMethodName = "InitializeClosedInstanceSlow"; } kind = TargetKind.ExactCallableAddress; } } else { if (followVirtualDispatch && targetMethod.IsVirtual) { if (targetMethod.OwningType.IsInterface) { kind = TargetKind.InterfaceDispatch; initializeMethodName = "InitializeClosedInstanceToInterface"; } else { kind = TargetKind.VTableLookup; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } else { kind = TargetKind.CanonicalEntrypoint; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } return(new DelegateCreationInfo( factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)), targetMethod, kind)); } }
/// <summary> /// Attempts to resolve constrained call to <paramref name="interfaceMethod"/> into a concrete non-unboxing /// method on <paramref name="constrainedType"/>. /// The ability to resolve constraint methods is affected by the degree of code sharing we are performing /// for generic code. /// </summary> /// <returns>The resolved method or null if the constraint couldn't be resolved.</returns> public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrainedType, TypeDesc interfaceType, MethodDesc interfaceMethod, out bool forceRuntimeLookup) { forceRuntimeLookup = false; // We can't resolve constraint calls effectively for reference types, and there's // not a lot of perf. benefit in doing it anyway. if (!constrainedType.IsValueType) { return(null); } // Interface method may or may not be fully canonicalized here. // It would be canonical on the CoreCLR side so canonicalize here to keep the algorithms similar. Instantiation methodInstantiation = interfaceMethod.Instantiation; interfaceMethod = interfaceMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); // 1. Find the (possibly generic) method that would implement the // constraint if we were making a call on a boxed value type. TypeDesc canonType = constrainedType.ConvertToCanonForm(CanonicalFormKind.Specific); TypeSystemContext context = constrainedType.Context; MethodDesc genInterfaceMethod = interfaceMethod.GetMethodDefinition(); MethodDesc method = null; if (genInterfaceMethod.OwningType.IsInterface) { // Sometimes (when compiling shared generic code) // we don't have enough exact type information at JIT time // even to decide whether we will be able to resolve to an unboxed entry point... // To cope with this case we always go via the helper function if there's any // chance of this happening by checking for all interfaces which might possibly // be compatible with the call (verification will have ensured that // at least one of them will be) // Enumerate all potential interface instantiations int potentialMatchingInterfaces = 0; foreach (DefType potentialInterfaceType in canonType.RuntimeInterfaces) { if (potentialInterfaceType.ConvertToCanonForm(CanonicalFormKind.Specific) == interfaceType.ConvertToCanonForm(CanonicalFormKind.Specific)) { potentialMatchingInterfaces++; MethodDesc potentialInterfaceMethod = genInterfaceMethod; if (potentialInterfaceMethod.OwningType != potentialInterfaceType) { potentialInterfaceMethod = context.GetMethodForInstantiatedType( potentialInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)potentialInterfaceType); } method = canonType.ResolveInterfaceMethodToVirtualMethodOnType(potentialInterfaceMethod); // See code:#TryResolveConstraintMethodApprox_DoNotReturnParentMethod if (method != null && !method.OwningType.IsValueType) { // We explicitly wouldn't want to abort if we found a default implementation. // The above resolution doesn't consider the default methods. Debug.Assert(!method.OwningType.IsInterface); return(null); } } } Debug.Assert(potentialMatchingInterfaces != 0); if (potentialMatchingInterfaces > 1) { // We have more potentially matching interfaces Debug.Assert(interfaceType.HasInstantiation); bool isExactMethodResolved = false; if (!interfaceType.IsCanonicalSubtype(CanonicalFormKind.Any) && !interfaceType.IsGenericDefinition && !constrainedType.IsCanonicalSubtype(CanonicalFormKind.Any) && !constrainedType.IsGenericDefinition) { // We have exact interface and type instantiations (no generic variables and __Canon used // anywhere) if (constrainedType.CanCastTo(interfaceType)) { // We can resolve to exact method MethodDesc exactInterfaceMethod = context.GetMethodForInstantiatedType( genInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceType); method = constrainedType.ResolveVariantInterfaceMethodToVirtualMethodOnType(exactInterfaceMethod); isExactMethodResolved = method != null; } } if (!isExactMethodResolved) { // We couldn't resolve the interface statically // Notify the caller that it should use runtime lookup // Note that we can leave pMD incorrect, because we will use runtime lookup forceRuntimeLookup = true; } } else { // If we can resolve the interface exactly then do so (e.g. when doing the exact // lookup at runtime, or when not sharing generic code). if (constrainedType.CanCastTo(interfaceType)) { MethodDesc exactInterfaceMethod = genInterfaceMethod; if (genInterfaceMethod.OwningType != interfaceType) { exactInterfaceMethod = context.GetMethodForInstantiatedType( genInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceType); } method = constrainedType.ResolveVariantInterfaceMethodToVirtualMethodOnType(exactInterfaceMethod); } } } else if (genInterfaceMethod.IsVirtual) { MethodDesc targetMethod = interfaceType.FindMethodOnTypeWithMatchingTypicalMethod(genInterfaceMethod); method = constrainedType.FindVirtualFunctionTargetMethodOnObjectType(targetMethod); } else { // The method will be null if calling a non-virtual instance // methods on System.Object, i.e. when these are used as a constraint. method = null; } if (method == null) { // Fall back to VSD return(null); } //#TryResolveConstraintMethodApprox_DoNotReturnParentMethod // Only return a method if the value type itself declares the method, // otherwise we might get a method from Object or System.ValueType if (!method.OwningType.IsValueType) { // Fall back to VSD return(null); } // We've resolved the method, ignoring its generic method arguments // If the method is a generic method then go and get the instantiated descriptor if (methodInstantiation.Length != 0) { method = method.MakeInstantiatedMethod(methodInstantiation); } Debug.Assert(method != null); return(method); }
public override IEnumerable <CombinedDependencyListEntry> SearchDynamicDependencies(List <DependencyNodeCore <NodeFactory> > markedNodes, int firstNode, NodeFactory factory) { List <CombinedDependencyListEntry> dynamicDependencies = new List <CombinedDependencyListEntry>(); TypeDesc methodOwningType = _method.OwningType; bool methodIsShared = _method.IsSharedByGenericInstantiations; TypeSystemContext context = _method.Context; for (int i = firstNode; i < markedNodes.Count; i++) { DependencyNodeCore <NodeFactory> entry = markedNodes[i]; EETypeNode entryAsEETypeNode = entry as EETypeNode; if (entryAsEETypeNode == null) { continue; } TypeDesc potentialOverrideType = entryAsEETypeNode.Type; if (!potentialOverrideType.IsDefType || potentialOverrideType.IsInterface) { continue; } // If method is canonical, don't allow using it with non-canonical types - we can wait until // we see the __Canon instantiation. If there isn't one, the canonical method wouldn't be useful anyway. if (methodIsShared && potentialOverrideType.ConvertToCanonForm(CanonicalFormKind.Specific) != potentialOverrideType) { continue; } // Similarly, if the type is canonical but this method instantiation isn't, don't mix them. if (!methodIsShared && potentialOverrideType.IsCanonicalSubtype(CanonicalFormKind.Any)) { continue; } // If this is an interface gvm, look for types that implement the interface // and other instantantiations that have the same canonical form. // This ensure the various slot numbers remain equivalent across all types where there is an equivalence // relationship in the vtable. if (methodOwningType.IsInterface) { // We go over definitions because a single canonical interface method could actually be implemented // by multiple methods - consider: // // class Foo<T, U> : IFoo<T>, IFoo<U>, IFoo<string> { } // // If we ask what implements IFoo<__Canon>.Method, the answer could be "three methods" // and that's expected. We therefore resolve IFoo<__Canon>.Method for each IFoo<!0>.Method, // IFoo<!1>.Method, and IFoo<string>.Method, adding GVMDependencies for each. TypeDesc potentialOverrideDefinition = potentialOverrideType.GetTypeDefinition(); DefType[] potentialInterfaces = potentialOverrideType.RuntimeInterfaces; DefType[] potentialDefinitionInterfaces = potentialOverrideDefinition.RuntimeInterfaces; for (int interfaceIndex = 0; interfaceIndex < potentialInterfaces.Length; interfaceIndex++) { if (potentialInterfaces[interfaceIndex].ConvertToCanonForm(CanonicalFormKind.Specific) == methodOwningType) { MethodDesc interfaceMethod = _method.GetMethodDefinition(); if (methodOwningType.HasInstantiation) { interfaceMethod = context.GetMethodForInstantiatedType( _method.GetTypicalMethodDefinition(), (InstantiatedType)potentialDefinitionInterfaces[interfaceIndex]); } MethodDesc slotDecl = potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodTarget(interfaceMethod); if (slotDecl == null) { // The method might be implemented through a default interface method var result = potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, out slotDecl); if (result != DefaultInterfaceMethodResolution.DefaultImplementation) { slotDecl = null; } } if (slotDecl != null) { TypeDesc[] openInstantiation = new TypeDesc[_method.Instantiation.Length]; for (int instArg = 0; instArg < openInstantiation.Length; instArg++) { openInstantiation[instArg] = context.GetSignatureVariable(instArg, method: true); } MethodDesc implementingMethodInstantiation = slotDecl.MakeInstantiatedMethod(openInstantiation).InstantiateSignature(potentialOverrideType.Instantiation, _method.Instantiation); dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(implementingMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific)), null, "ImplementingMethodInstantiation")); } } } } else { // This is not an interface GVM. Check whether the current type overrides the virtual method. // We might need to change what virtual method we ask about - consider: // // class Base<T> { virtual Method(); } // class Derived : Base<string> { override Method(); } // // We need to resolve Base<__Canon>.Method on Derived, but if we were to ask the virtual // method resolution algorithm, the answer would be "does not override" because Base<__Canon> // is not even in the inheritance hierarchy. // // So we need to modify the question to resolve Base<string>.Method instead and then // canonicalize the result. TypeDesc overrideTypeCur = potentialOverrideType; do { if (overrideTypeCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodOwningType) { break; } overrideTypeCur = overrideTypeCur.BaseType; }while (overrideTypeCur != null); if (overrideTypeCur == null) { continue; } MethodDesc methodToResolve; if (methodOwningType == overrideTypeCur) { methodToResolve = _method; } else { methodToResolve = context .GetMethodForInstantiatedType(_method.GetTypicalMethodDefinition(), (InstantiatedType)overrideTypeCur) .MakeInstantiatedMethod(_method.Instantiation); } MethodDesc instantiatedTargetMethod = potentialOverrideType.FindVirtualFunctionTargetMethodOnObjectType(methodToResolve) .GetCanonMethodTarget(CanonicalFormKind.Specific); if (instantiatedTargetMethod != _method) { dynamicDependencies.Add(new CombinedDependencyListEntry( factory.GVMDependencies(instantiatedTargetMethod), null, "DerivedMethodInstantiation")); } } } return(dynamicDependencies); }