protected override void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType) { declType = declType.GetClosestDefType(); var baseType = declType.BaseType; if (baseType != null) OutputVirtualSlots(factory, ref objData, implType, baseType); // The generic dictionary pointer occupies the first slot of each type vtable slice if (declType.HasGenericDictionarySlot()) { objData.EmitPointerReloc(factory.TypeGenericDictionary(declType)); } // Actual vtable slots follow IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(declType).Slots; for (int i = 0; i < virtualSlots.Count; i++) { MethodDesc declMethod = virtualSlots[i]; MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod); if (declMethod.HasInstantiation) { // Generic virtual methods will "compile", but will fail to link. Check for it here. throw new NotImplementedException("VTable for " + _type + " has generic virtual methods."); } if (!implMethod.IsAbstract) { MethodDesc canonImplMethod = implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); objData.EmitPointerReloc(factory.MethodEntrypoint(canonImplMethod, implMethod.OwningType.IsValueType)); } else { objData.EmitZeroPointer(); } } }
protected override void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType) { declType = declType.GetClosestDefType(); var baseType = declType.BaseType; if (baseType != null) OutputVirtualSlots(factory, ref objData, implType, baseType); IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(declType).Slots; for (int i = 0; i < virtualSlots.Count; i++) { MethodDesc declMethod = virtualSlots[i]; MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod); if (declMethod.HasInstantiation) { // Generic virtual methods will "compile", but will fail to link. Check for it here. throw new NotImplementedException("VTable for " + _type + " has generic virtual methods."); } if (!implMethod.IsAbstract) objData.EmitPointerReloc(factory.MethodEntrypoint(implMethod, implMethod.OwningType.IsValueType)); else objData.EmitZeroPointer(); } }
/// <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); }
/// <summary> /// Resolve a call on the interface method targetVirtualMethod, on the type instanceDefTypeToExamine utilizing metadata to /// its associated virtual method. /// </summary> /// <param name="instanceDefTypeToExamine">(in) The class type on which the interface call is made, (out) the class type where the search may continue using non-metadata means</param> /// <param name="targetVirtualMethod">The interface method to translate into a virtual method for execution</param> /// <returns>virtual method slot which implements the interface method OR null if an implementation should fall back to non-metadata based lookup.</returns> public static MethodDesc ResolveInterfaceMethodToVirtualMethod(TypeDesc instanceType, out TypeDesc instanceDefTypeToExamine, MethodDesc targetVirtualMethod) { instanceDefTypeToExamine = instanceType.GetClosestDefType(); MethodDesc newlyFoundVirtualMethod = null; LowLevelList<MethodDesc> variantTargets = null; if (targetVirtualMethod.OwningType.HasVariance) { foreach (TypeDesc type in instanceType.RuntimeInterfaces) { if (type != targetVirtualMethod.OwningType && type.GetTypeDefinition() == targetVirtualMethod.OwningType.GetTypeDefinition()) { // Check to see if these interfaces are appropriately assignable if (RuntimeAugments.IsAssignableFrom(targetVirtualMethod.OwningType.GetRuntimeTypeHandle(), type.GetRuntimeTypeHandle())) { if (variantTargets == null) variantTargets = new LowLevelList<MethodDesc>(); MethodDesc targetVariantMatch = type.Context.GetMethodForInstantiatedType( targetVirtualMethod.GetTypicalMethodDefinition(), (InstantiatedType)type); variantTargets.Add(targetVariantMatch); } } } } do { newlyFoundVirtualMethod = instanceDefTypeToExamine.ResolveInterfaceMethodToVirtualMethodOnType(targetVirtualMethod); if (newlyFoundVirtualMethod == null && variantTargets != null) { for (int i = 0; i < variantTargets.Count; i++) { newlyFoundVirtualMethod = instanceDefTypeToExamine.ResolveInterfaceMethodToVirtualMethodOnType(variantTargets[i]); if (newlyFoundVirtualMethod != null) break; } } instanceDefTypeToExamine = instanceDefTypeToExamine.BaseType; } while ((newlyFoundVirtualMethod == null) && (instanceDefTypeToExamine != null) && !IsPregeneratedOrTemplateTypeLoaded(instanceDefTypeToExamine)); return newlyFoundVirtualMethod; }
private static bool TryGetVirtualMethodFromSlot(TypeDesc definingType, int vtableSlotIndex, out MethodDesc slotDefiningMethod) { MethodNameAndSignature methodNameAndSig; bool success = TypeLoaderEnvironment.TryGetMethodMethodNameAndSigFromVTableSlotForPregeneratedOrTemplateType (definingType.Context, definingType.GetRuntimeTypeHandle(), vtableSlotIndex, out methodNameAndSig); if (!success) { slotDefiningMethod = null; return false; } TypeSystem.NativeFormat.NativeFormatType metadataDefiningType = definingType.GetClosestDefType().GetTypeDefinition() as TypeSystem.NativeFormat.NativeFormatType; // We're working with a NoMetadataType, or an ArrayType, neither of which have full metadata if (metadataDefiningType == null) { slotDefiningMethod = null; return false; } // TryGetMethodMethodNameAndSigFromVTableSlotForPregeneratedOrTemplateType is expected to only return methodNameAndSig with NativeLayoutSignatures in them. // If we start hitting the more general case, we can improve this algorithm. Debug.Assert(methodNameAndSig.Signature.IsNativeLayoutSignature); foreach (TypeSystem.NativeFormat.NativeFormatMethod method in metadataDefiningType.GetMethods()) { if (!method.IsVirtual) continue; if (method.HasInstantiation) continue; if (!method.Name.Equals(methodNameAndSig.Name)) continue; MethodSignatureComparer sigComparer = new MethodSignatureComparer(method.MetadataReader, method.Handle); if (!sigComparer.IsMatchingNativeLayoutMethodNameAndSignature(methodNameAndSig.Name, methodNameAndSig.Signature.NativeLayoutSignature)) continue; // At this point we've matched slotDefiningMethod = method; return true; } // Didn't find the method slotDefiningMethod = null; return false; }
private static IntPtr ResolveVirtualVTableFunction(TypeDesc type, int vtableSlotIndex) { IntPtr exactResult; MethodDesc virtualFunctionDefiningSlot = ResolveVTableSlotIndexToMethodDescOrFunctionPointer(type.GetClosestDefType(), vtableSlotIndex, out exactResult); if (virtualFunctionDefiningSlot == null) return exactResult; MethodDesc virtualFunctionOverride = ((MetadataType)type.GetClosestDefType()).FindVirtualFunctionTargetMethodOnObjectType(virtualFunctionDefiningSlot); if (TryGetVTableCallableAddress(virtualFunctionOverride, out exactResult)) return exactResult; Environment.FailFast("Method address lookup failed for: " + virtualFunctionOverride.ToString()); return IntPtr.Zero; }