protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType) { MethodDesc impl; if (declMethod.OwningType.IsInterface) { impl = implType.ResolveInterfaceMethodTarget(declMethod); if (impl != null) { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl); } } else { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); if (impl != null && (impl != declMethod)) { MethodDesc slotDefiningMethodImpl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(impl); MethodDesc slotDefiningMethodDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(declMethod); if (slotDefiningMethodImpl != slotDefiningMethodDecl) { // We cannot resolve virtual method in case the impl is a different slot from the declMethod impl = null; } } } return(impl); }
protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType) { // Quick check: if decl matches impl, we're done. if (declMethod.OwningType == implType) { return(declMethod); } MethodDesc impl; if (declMethod.OwningType.IsInterface) { impl = implType.ResolveInterfaceMethodTarget(declMethod); if (impl != null) { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl); } } else { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); } return(impl); }
protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType) { MethodDesc impl; if (declMethod.OwningType.IsInterface) { if (declMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) || implType.IsCanonicalSubtype(CanonicalFormKind.Any)) { DefType[] implTypeRuntimeInterfaces = implType.RuntimeInterfaces; int canonicallyMatchingInterfacesFound = 0; DefType canonicalInterfaceType = (DefType)declMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); for (int i = 0; i < implTypeRuntimeInterfaces.Length; i++) { DefType runtimeInterface = implTypeRuntimeInterfaces[i]; if (canonicalInterfaceType.HasSameTypeDefinition(runtimeInterface) && runtimeInterface.ConvertToCanonForm(CanonicalFormKind.Specific) == canonicalInterfaceType) { canonicallyMatchingInterfacesFound++; if (canonicallyMatchingInterfacesFound > 1) { // We cannot resolve the interface as we don't know with exact enough detail which interface // of multiple possible interfaces is being called. return(null); } } } } impl = implType.ResolveInterfaceMethodTarget(declMethod); if (impl != null) { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl); } } else { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); if (impl != null && (impl != declMethod)) { MethodDesc slotDefiningMethodImpl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(impl); MethodDesc slotDefiningMethodDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(declMethod); if (slotDefiningMethodImpl != slotDefiningMethodDecl) { // We cannot resolve virtual method in case the impl is a different slot from the declMethod impl = null; } } } return(impl); }
public override IEnumerable <CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) { Debug.Assert(EmitVirtualSlotsAndInterfaces); DefType defType = _type.GetClosestDefType(); // If we're producing a full vtable, none of the dependencies are conditional. if (!factory.VTable(defType).HasFixedSlots) { foreach (MethodDesc decl in defType.EnumAllVirtualSlots()) { // Generic virtual methods are tracked by an orthogonal mechanism. if (decl.HasInstantiation) { continue; } MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl); if (impl.OwningType == defType && !impl.IsAbstract) { MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific); yield return(new CombinedDependencyListEntry(factory.MethodEntrypoint(canonImpl, _type.IsValueType), factory.VirtualMethodUse(decl), "Virtual method")); } } Debug.Assert( _type == defType || ((System.Collections.IStructuralEquatable)defType.RuntimeInterfaces).Equals(_type.RuntimeInterfaces, EqualityComparer <DefType> .Default)); // Add conditional dependencies for interface methods the type implements. For example, if the type T implements // interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's // possible for any IFoo object to actually be an instance of T. foreach (DefType interfaceType in defType.RuntimeInterfaces) { Debug.Assert(interfaceType.IsInterface); foreach (MethodDesc interfaceMethod in interfaceType.GetAllMethods()) { if (interfaceMethod.Signature.IsStatic) { continue; } // Generic virtual methods are tracked by an orthogonal mechanism. if (interfaceMethod.HasInstantiation) { continue; } MethodDesc implMethod = defType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); if (implMethod != null) { yield return(new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.VirtualMethodUse(interfaceMethod), "Interface method")); } } } } }
protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType) { MethodDesc impl; if (declMethod.OwningType.IsInterface) { impl = implType.ResolveInterfaceMethodTarget(declMethod); if (impl != null) { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl); } } else { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); } return(impl); }
public override IEnumerable <CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) { DefType defType = _type.GetClosestDefType(); foreach (MethodDesc decl in defType.EnumAllVirtualSlots()) { MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl); if (impl.OwningType == defType && !impl.IsAbstract) { yield return(new DependencyNodeCore <NodeFactory> .CombinedDependencyListEntry(factory.MethodEntrypoint(impl, _type.IsValueType), factory.VirtualMethodUse(decl), "Virtual method")); } } Debug.Assert( _type == defType || ((System.Collections.IStructuralEquatable)defType.RuntimeInterfaces).Equals(_type.RuntimeInterfaces, EqualityComparer <DefType> .Default)); // Add conditional dependencies for interface methods the type implements. For example, if the type T implements // interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's // possible for any IFoo object to actually be an instance of T. foreach (DefType interfaceType in defType.RuntimeInterfaces) { Debug.Assert(interfaceType.IsInterface); foreach (MethodDesc interfaceMethod in interfaceType.GetAllVirtualMethods()) { MethodDesc implMethod = defType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); if (implMethod != null) { yield return(new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.ReadyToRunHelper(ReadyToRunHelperId.InterfaceDispatch, interfaceMethod), "Interface method")); yield return(new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.ReadyToRunHelper(ReadyToRunHelperId.ResolveVirtualFunction, interfaceMethod), "Interface method address")); } } } }
protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail) { devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN; MethodDesc impl; if (declMethod.OwningType.IsInterface) { if (declMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) || implType.IsCanonicalSubtype(CanonicalFormKind.Any)) { DefType[] implTypeRuntimeInterfaces = implType.RuntimeInterfaces; int canonicallyMatchingInterfacesFound = 0; DefType canonicalInterfaceType = (DefType)declMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); for (int i = 0; i < implTypeRuntimeInterfaces.Length; i++) { DefType runtimeInterface = implTypeRuntimeInterfaces[i]; if (canonicalInterfaceType.HasSameTypeDefinition(runtimeInterface) && runtimeInterface.ConvertToCanonForm(CanonicalFormKind.Specific) == canonicalInterfaceType) { canonicallyMatchingInterfacesFound++; if (canonicallyMatchingInterfacesFound > 1) { // We cannot resolve the interface as we don't know with exact enough detail which interface // of multiple possible interfaces is being called. devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_MULTIPLE_IMPL; return(null); } } } } if (!implType.CanCastTo(declMethod.OwningType)) { devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CAST; return(null); } impl = implType.ResolveInterfaceMethodTargetWithVariance(declMethod); if (impl != null) { impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl); } else { MethodDesc dimMethod = null; // This isn't the correct lookup algorithm for variant default interface methods // but as we will drop any results we find in any case, it doesn't matter much. // Non-variant dispatch can simply use ResolveInterfaceMethodToDefaultImplementationOnType // but that implementation currently cannot handle variance. MethodDesc defaultInterfaceDispatchDeclMethod = null; foreach (TypeDesc iface in implType.RuntimeInterfaces) { if (iface == declMethod.OwningType) { defaultInterfaceDispatchDeclMethod = declMethod; break; } if (iface.HasSameTypeDefinition(declMethod.OwningType) && iface.CanCastTo(declMethod.OwningType)) { defaultInterfaceDispatchDeclMethod = iface.FindMethodOnTypeWithMatchingTypicalMethod(declMethod); // Prefer to find the exact match, so don't break immediately } } if (defaultInterfaceDispatchDeclMethod != null) { switch (implType.ResolveInterfaceMethodToDefaultImplementationOnType(defaultInterfaceDispatchDeclMethod, out dimMethod)) { case DefaultInterfaceMethodResolution.Diamond: case DefaultInterfaceMethodResolution.Reabstraction: devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; return(null); case DefaultInterfaceMethodResolution.DefaultImplementation: if (dimMethod.OwningType.HasInstantiation || (declMethod != defaultInterfaceDispatchDeclMethod)) { // If we devirtualized into a default interface method on a generic type, we should actually return an // instantiating stub but this is not happening. // Making this work is tracked by https://github.com/dotnet/runtime/issues/9588 // In addition, we fail here for variant default interface dispatch devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; return(null); } else { impl = dimMethod; } break; } } } } else { // The derived class should be a subclass of the base class. // this check is perfomed via typedef checking instead of casting, as we accept canon methods calling exact types TypeDesc checkType; for (checkType = implType; checkType != null && !checkType.HasSameTypeDefinition(declMethod.OwningType); checkType = checkType.BaseType) { } if ((checkType == null) || (checkType.ConvertToCanonForm(CanonicalFormKind.Specific) != declMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific))) { // The derived class should be a subclass of the base class. devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_SUBCLASS; return(null); } else { // At this point, the decl method may be only canonically compatible, but not an exact match to a method in the type hierarchy // Convert it to an exact match. (Or if it is an exact match, the FindMethodOnTypeWithMatchingTypicalMethod will be a no-op) declMethod = checkType.FindMethodOnTypeWithMatchingTypicalMethod(declMethod); } impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); if (impl != null && (impl != declMethod)) { MethodDesc slotDefiningMethodImpl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(impl); MethodDesc slotDefiningMethodDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(declMethod); if (slotDefiningMethodImpl != slotDefiningMethodDecl) { // If the derived method's slot does not match the vtable slot, // bail on devirtualization, as the method was installed into // the vtable slot via an explicit override and even if the // method is final, the slot may not be. // // Note the jit could still safely devirtualize if it had an exact // class, but such cases are likely rare. devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_SLOT; impl = null; } } } return(impl); }
/// <summary> /// Try to resolve a virtual call to targetMethod to its implementation on instanceType. /// </summary> /// <param name="instanceType">non-interface type</param> /// <param name="targetMethod">non-generic virtual or interface method</param> /// <param name="methodAddress">function pointer resolved</param> /// <returns>true if successful</returns> public static bool TryDispatchMethodOnTarget(TypeDesc instanceType, MethodDesc targetMethod, out IntPtr methodAddress) { methodAddress = IntPtr.Zero; if (targetMethod == null) { return(false); } if (IsPregeneratedOrTemplateTypeLoaded(instanceType)) { if (targetMethod.OwningType.IsInterface) { ushort interfaceSlot; if (!TryGetInterfaceSlotNumberFromMethod(targetMethod, out interfaceSlot)) { return(false); } methodAddress = RuntimeAugments.ResolveDispatchOnType(instanceType.GetRuntimeTypeHandle(), targetMethod.OwningType.GetRuntimeTypeHandle(), interfaceSlot); Debug.Assert(methodAddress != IntPtr.Zero); // TODO! This should happen for ICastable dispatch... return(true); } else { unsafe { int vtableSlotIndex = LazyVTableResolver.VirtualMethodToSlotIndex(targetMethod); EEType *eeType = instanceType.GetRuntimeTypeHandle().ToEETypePtr(); IntPtr *vtableStart = (IntPtr *)(((byte *)eeType) + sizeof(EEType)); methodAddress = vtableStart[vtableSlotIndex]; return(true); } } } MethodDesc targetVirtualMethod = targetMethod; DefType instanceDefType = instanceType.GetClosestDefType(); // For interface resolution, its a two step process, first get the virtual slot if (targetVirtualMethod.OwningType.IsInterface) { TypeDesc instanceDefTypeToExamine; MethodDesc newlyFoundVirtualMethod = ResolveInterfaceMethodToVirtualMethod(instanceType, out instanceDefTypeToExamine, targetVirtualMethod); targetVirtualMethod = newlyFoundVirtualMethod; // The pregenerated type must be the one that implements the interface method // Call into Redhawk to deal with this. if ((newlyFoundVirtualMethod == null) && (instanceDefTypeToExamine != null)) { ushort interfaceSlot; if (!TryGetInterfaceSlotNumberFromMethod(targetMethod, out interfaceSlot)) { return(false); } methodAddress = RuntimeAugments.ResolveDispatchOnType(instanceDefTypeToExamine.GetRuntimeTypeHandle(), targetMethod.OwningType.GetRuntimeTypeHandle(), interfaceSlot); Debug.Assert(methodAddress != IntPtr.Zero); // TODO! This should happen for ICastable dispatch... return(true); } } // VirtualSlot can be null if the interface method isn't really implemented. This should never happen, but since our // type loader doesn't check all interface overloads at load time, it could happen if (targetVirtualMethod == null) { return(false); } // Resolve virtual method to exact method MethodDesc dispatchMethod = instanceDefType.FindVirtualFunctionTargetMethodOnObjectType(targetVirtualMethod); return(TryGetVTableCallableAddress(dispatchMethod, out methodAddress)); }
private bool ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericArguments, MethodNameAndSignature callingMethodNameAndSignature, out IntPtr methodPointer, out IntPtr dictionaryPointer) { if (IsPregeneratedOrTemplateRuntimeTypeHandle(targetTypeHandle)) { // If the target type isn't dynamic, or at least is template type generated, the static lookup logic is what we want. return(ResolveGenericVirtualMethodTarget_Static(targetTypeHandle, declaringTypeHandle, genericArguments, callingMethodNameAndSignature, out methodPointer, out dictionaryPointer)); } else { #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING methodPointer = IntPtr.Zero; dictionaryPointer = IntPtr.Zero; TypeSystemContext context = TypeSystemContextFactory.Create(); DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(targetTypeHandle); // Method being called... MethodDesc targetVirtualMethod = ResolveTypeHandleAndMethodNameAndSigToVirtualMethodDesc(context, declaringTypeHandle, callingMethodNameAndSignature); if (targetVirtualMethod == null) { // If we can't find the method in the type system, it must only be present in the static environment. Search there instead. TypeSystemContextFactory.Recycle(context); return(ResolveGenericVirtualMethodTarget_Static(targetTypeHandle, declaringTypeHandle, genericArguments, callingMethodNameAndSignature, out methodPointer, out dictionaryPointer)); } MethodDesc dispatchMethod = targetType.FindVirtualFunctionTargetMethodOnObjectType(targetVirtualMethod); if (dispatchMethod == null) { return(false); } Instantiation targetMethodInstantiation = context.ResolveRuntimeTypeHandles(genericArguments); MethodDesc instantiatedDispatchMethod = dispatchMethod.Context.ResolveGenericMethodInstantiation(dispatchMethod.OwningType.IsValueType /* get the unboxing stub */, dispatchMethod.OwningType.GetClosestDefType(), dispatchMethod.NameAndSignature, targetMethodInstantiation, IntPtr.Zero, false); GenericDictionaryCell cell = GenericDictionaryCell.CreateMethodCell(instantiatedDispatchMethod, false); using (LockHolder.Hold(_typeLoaderLock)) { // Now that we hold the lock, we may find that existing types can now find // their associated RuntimeTypeHandle. Flush the type builder states as a way // to force the reresolution of RuntimeTypeHandles which couldn't be found before. context.FlushTypeBuilderStates(); TypeBuilder.ResolveSingleCell(cell, out methodPointer); } TypeSystemContextFactory.Recycle(context); return(true); #else methodPointer = IntPtr.Zero; dictionaryPointer = IntPtr.Zero; Environment.FailFast("GVM Resolution for non template or pregenerated type"); return(false); #endif } }