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); }
private void RootMethod(IRootingServiceProvider rootProvider, MethodDesc method) { try { LibraryRootProvider.CheckCanGenerateMethod(method); // Virtual methods should be rooted as if they were called virtually if (method.IsVirtual) { MethodDesc slotMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); rootProvider.RootVirtualMethodForReflection(slotMethod, "RD.XML root"); } if (!method.IsAbstract) { rootProvider.AddCompilationRoot(method, "RD.XML root"); } } catch (TypeSystemException) { // TODO: fail compilation if a switch was passed // Individual methods can fail to load types referenced in their signatures. // Skip them in library mode since they're not going to be callable. return; // TODO: Log as a warning } }
public static MethodDesc GetDeclaringVirtualMethodAndHierarchyDistance(MethodDesc method, out int parentHierarchyDistance) { parentHierarchyDistance = 0; MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition()); TypeDesc typeOfDeclaringMethodForSlot = declaringMethodForSlot.OwningType.GetTypeDefinition(); TypeDesc currentType = method.OwningType.GetTypeDefinition(); TypeDesc containingTypeOfDeclaringMethodForSlot = method.OwningType; while (typeOfDeclaringMethodForSlot != currentType) { parentHierarchyDistance++; currentType = currentType.BaseType.GetTypeDefinition(); containingTypeOfDeclaringMethodForSlot = containingTypeOfDeclaringMethodForSlot.BaseType; } if (containingTypeOfDeclaringMethodForSlot.HasInstantiation) { declaringMethodForSlot = method.Context.GetMethodForInstantiatedType( declaringMethodForSlot.GetTypicalMethodDefinition(), (InstantiatedType)containingTypeOfDeclaringMethodForSlot); } Debug.Assert(declaringMethodForSlot != null); return(declaringMethodForSlot); }
public static void GetVirtualInvokeMapDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { if (!factory.MetadataManager.SupportsReflection) { return; } if (NeedsVirtualInvokeInfo(method)) { dependencies = dependencies ?? new DependencyList(); dependencies.Add( factory.NecessaryTypeSymbol(method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)), "Reflection virtual invoke owning type"); NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); dependencies.Add(placedNameAndSig, "Reflection virtual invoke method signature"); if (!method.HasInstantiation) { MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) { dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method"); } } } }
private DelegateCreationInfo(IMethodNode constructor, MethodDesc targetMethod, TargetKind targetKind, IMethodNode thunk = null) { Debug.Assert(targetKind != TargetKind.VTableLookup || MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod) == targetMethod); Constructor = constructor; TargetMethod = targetMethod; _targetKind = targetKind; Thunk = thunk; }
// // returns the DebugInfoBlob containing the method token to virtual method slot mapping // .mdinfo format // offset 0, 4 bytes: version number // offset 4, 4 bytes: count of assemblies // // for each assembly // offset 0, 4 bytes: count of methods with a virtual method slot // // for each method // offset 0, 4 bytes: method def token (as they are in the input assembly) // offset 4, 2 bytes: length of the per method information [ currently always 4 ] // offset 6, 4 bytes: virtual slot number // methods are sorted by their method def token internal DebugInfoBlob GetDebugMethodInfoMap(NodeFactory factory) { Dictionary <EcmaAssembly, uint> originalAssemblyOrder = new Dictionary <EcmaAssembly, uint>(); List <SortedDictionary <uint, int> > moduleMethods = new List <SortedDictionary <uint, int> >(); // re-construct orginal assembly input order foreach (MergedAssemblyRecord mergedAssembly in _mergedAssemblies.MergedAssemblies) { uint assemblyIndex = AdjustIndex(mergedAssembly.AssemblyIndex, _mergedAssemblies.CorLibIndex); originalAssemblyOrder.Add(mergedAssembly.Assembly, assemblyIndex); moduleMethods.Add(new SortedDictionary <uint, int>()); } foreach (TypeDesc type in factory.MetadataManager.GetTypesWithConstructedEETypes()) { // skip if sealed if (type.IsSealed()) { continue; } // no generic support yet if (type is EcmaType) { EcmaType ecmaType = (EcmaType)type; EcmaAssembly ecmaAssembly = (EcmaAssembly)ecmaType.EcmaModule; int assemblyIndex = (int)originalAssemblyOrder[ecmaAssembly]; SortedDictionary <uint, int> methodList = moduleMethods[assemblyIndex]; foreach (MethodDesc md in type.GetAllMethods()) { // skip non-virtual and final methods if (!md.IsVirtual || md.IsFinal) { continue; } // skip generic if (md.HasInstantiation) { continue; } // method token. EntityHandle methodHandle = ((EcmaMethod)md).Handle; uint methodToken = (uint)MetadataTokens.GetToken(methodHandle); // find virtual method slot. MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(md.GetTypicalMethodDefinition()); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, type); if (slot != -1 && !methodList.ContainsKey(methodToken)) { methodList.Add(methodToken, slot); } } } } return(ConvertToDebugInfoBlob(moduleMethods)); }
public static bool VTableMethodRequiresCallingConventionConverter(MethodDesc method) { if (!MethodSignatureHasVarsNeedingCallingConventionConverter(method.GetTypicalMethodDefinition().Signature)) { return(false); } MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).GetCanonMethodTarget(CanonicalFormKind.Specific); return(slotDecl.IsCanonicalMethod(CanonicalFormKind.Universal)); }
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); }
private void ProcessTypeDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, XElement typeElement) { var typeNameAttribute = typeElement.Attribute("Name"); if (typeNameAttribute == null) { throw new Exception(); } var dynamicDegreeAttribute = typeElement.Attribute("Dynamic"); if (dynamicDegreeAttribute != null) { if (dynamicDegreeAttribute.Value != "Required All") { throw new NotSupportedException(); } } string typeName = typeNameAttribute.Value; TypeDesc type = containingModule.GetTypeByCustomAttributeTypeName(typeName); rootProvider.AddCompilationRoot(type, "RD.XML root"); if (type.IsDefType) { foreach (var method in type.GetMethods()) { // We don't know what to instantiate generic methods over if (method.HasInstantiation) { continue; } // Virtual methods should be rooted as if they were called virtually if (method.IsVirtual) { MethodDesc slotMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); rootProvider.RootVirtualMethodUse(slotMethod, "RD.XML root"); } // Abstract methods have no entrypoints if (method.IsAbstract) { continue; } rootProvider.AddCompilationRoot(method, "RD.XML root"); } } }
public VirtualMethodUseNode(MethodDesc decl) { Debug.Assert(decl.IsVirtual); // Virtual method use always represents the slot defining method of the virtual. // Places that might see virtual methods being used through an override need to normalize // to the slot defining method. Debug.Assert(MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(decl) == decl); // Generic virtual methods are tracked by an orthogonal mechanism. Debug.Assert(!decl.HasInstantiation); _decl = decl; }
public IEnumerable <TypeGVMEntryInfo> ScanForGenericVirtualMethodEntries() { foreach (var method in _associatedType.GetMethods()) { if (!method.IsVirtual || !method.HasInstantiation) { continue; } MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); Debug.Assert(slotDecl != null); yield return(new TypeGVMEntryInfo(slotDecl, method, null)); } }
private void RootType(IRootingServiceProvider rootProvider, TypeDesc type) { rootProvider.AddCompilationRoot(type, "RD.XML root"); if (type.IsGenericDefinition) { return; } if (type.IsDefType) { foreach (var method in type.GetMethods()) { // We don't know what to instantiate generic methods over if (method.HasInstantiation) { continue; } try { LibraryRootProvider.CheckCanGenerateMethod(method); // Virtual methods should be rooted as if they were called virtually if (method.IsVirtual) { MethodDesc slotMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); rootProvider.RootVirtualMethodForReflection(slotMethod, "RD.XML root"); } if (!method.IsAbstract) { rootProvider.AddCompilationRoot(method, "RD.XML root"); } } catch (TypeSystemException) { // TODO: fail compilation if a switch was passed // Individual methods can fail to load types referenced in their signatures. // Skip them in library mode since they're not going to be callable. continue; // TODO: Log as a warning } } } }
private void LookForVirtualOverrides(EcmaMethod method) { // We don't currently attempt to handle this for non-generics. if (!method.HasInstantiation) { return; } // If this is a generic virtual method, add an edge from each of the generic parameters // of the implementation to the generic parameters of the declaration - any call to the // declaration will be modeled as if the declaration was calling into the implementation. var decl = (EcmaMethod)MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).GetTypicalMethodDefinition(); if (decl != method) { RecordBinding(this, decl.Instantiation, method.Instantiation); } else { TypeDesc methodOwningType = method.OwningType; // This is the slot definition. Does it implement an interface? // (This has obvious holes. They haven't show up as issues so far.) foreach (DefType interfaceType in methodOwningType.RuntimeInterfaces) { foreach (MethodDesc interfaceMethod in interfaceType.GetVirtualMethods()) { // Trivially reject looking at interface methods that for sure can't be implemented by // the method we're looking at. if (!interfaceMethod.IsVirtual || interfaceMethod.Instantiation.Length != method.Instantiation.Length || interfaceMethod.Signature.Length != method.Signature.Length || interfaceMethod.Signature.IsStatic != method.Signature.IsStatic) { continue; } MethodDesc impl = methodOwningType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod)?.GetMethodDefinition(); if (impl == method) { RecordBinding(this, interfaceMethod.Instantiation, method.Instantiation); // Continue the loop in case this method implements multiple interfaces } } } }
public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, TypeDesc constrainedType, bool followVirtualDispatch) { // If we're creating a delegate to a virtual method that cannot be overriden, devirtualize. // This is not just an optimization - it's required for correctness in the presence of sealed // vtable slots. if (followVirtualDispatch && (target.IsFinal || target.OwningType.IsSealed())) { followVirtualDispatch = false; } if (followVirtualDispatch) { target = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(target); } return(DelegateCreationInfo.Create(delegateType, target, constrainedType, NodeFactory, followVirtualDispatch)); }
public void RootVirtualMethodForReflection(MethodDesc method, string reason) { Debug.Assert(method.IsVirtual); // Virtual method use is tracked on the slot defining method only. MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); if (!_factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) { _graph.AddRoot(_factory.VirtualMethodUse(slotDefiningMethod), reason); } if (method.IsAbstract) { _graph.AddRoot(_factory.ReflectableMethod(method), reason); } }
public VirtualMethodUseNode(MethodDesc decl) { Debug.Assert(!decl.IsRuntimeDeterminedExactMethod); Debug.Assert(decl.IsVirtual); Debug.Assert(!decl.IsCanonicalMethod(CanonicalFormKind.Any) || decl.GetCanonMethodTarget(CanonicalFormKind.Specific) == decl); // Virtual method use always represents the slot defining method of the virtual. // Places that might see virtual methods being used through an override need to normalize // to the slot defining method. Debug.Assert(MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(decl) == decl); // Generic virtual methods are tracked by an orthogonal mechanism. Debug.Assert(!decl.HasInstantiation); _decl = decl; }
public VariantInterfaceMethodUseNode(MethodDesc decl) { Debug.Assert(!decl.IsRuntimeDeterminedExactMethod); Debug.Assert(decl.IsVirtual); Debug.Assert(decl.OwningType.IsInterface); // Virtual method use always represents the slot defining method of the virtual. // Places that might see virtual methods being used through an override need to normalize // to the slot defining method. Debug.Assert(MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(decl) == decl); // Generic virtual methods are tracked by an orthogonal mechanism. Debug.Assert(!decl.HasInstantiation); // We currently track this for definitions only Debug.Assert(decl.IsTypicalMethodDefinition); _decl = decl; }
private static void RootVirtualMethodForReflection(ref DependencyList list, NodeFactory factory, MethodDesc method, string reason) { if (method.HasInstantiation) { list.Add(factory.GVMDependencies(method), reason); } else { // Virtual method use is tracked on the slot defining method only. MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) { list.Add(factory.VirtualMethodUse(slotDefiningMethod), reason); } } if (method.IsAbstract) { list.Add(factory.ReflectableMethod(method), reason); } }
public void RootVirtualMethodForReflection(MethodDesc method, string reason) { Debug.Assert(method.IsVirtual); if (method.HasInstantiation) { _rootAdder(_factory.GVMDependencies(method), reason); } else { // Virtual method use is tracked on the slot defining method only. MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); if (!_factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) { _rootAdder(_factory.VirtualMethodUse(slotDefiningMethod), reason); } } if (method.IsAbstract) { _rootAdder(_factory.ReflectableMethod(method), reason); } }
public override IEnumerable <CombinedDependencyListEntry> SearchDynamicDependencies(List <DependencyNodeCore <NodeFactory> > markedNodes, int firstNode, NodeFactory factory) { Debug.Assert(_method.IsVirtual && _method.HasInstantiation); List <CombinedDependencyListEntry> dynamicDependencies = new List <CombinedDependencyListEntry>(); 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 is DefType)) { continue; } Debug.Assert(!potentialOverrideType.IsRuntimeDeterminedSubtype); if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && potentialOverrideType.IsInterface && (potentialOverrideType != _method.OwningType)) { if (_method.OwningType.CanCastTo(potentialOverrideType)) { // Variance expansion MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation); dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency")); } } // 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 (_method.OwningType.IsInterface) { if (potentialOverrideType.IsInterface) { continue; } foreach (DefType interfaceImpl in potentialOverrideType.RuntimeInterfaces) { if (interfaceImpl.ConvertToCanonForm(CanonicalFormKind.Specific) == _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)) { // Find if the type implements this method. (Note, do this comparision against the generic definition of the method, not the // specific method instantiation that is "method" MethodDesc genericDefinition = interfaceImpl.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); MethodDesc slotDecl = potentialOverrideType.ResolveInterfaceMethodTarget(genericDefinition); if (slotDecl != null) { CreateDependencyForMethodSlotAndInstantiation(slotDecl, dynamicDependencies, factory); } } } } else { // TODO: Ensure GVM Canon Target TypeDesc overrideTypeCanonCur = potentialOverrideType; TypeDesc methodCanonContainingType = _method.OwningType; while (overrideTypeCanonCur != null) { if (overrideTypeCanonCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodCanonContainingType.ConvertToCanonForm(CanonicalFormKind.Specific)) { MethodDesc methodDefInDerivedType = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); if (methodDefInDerivedType != null) { CreateDependencyForMethodSlotAndInstantiation(methodDefInDerivedType, dynamicDependencies, factory); } MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method); if (slotDecl != null) { CreateDependencyForMethodSlotAndInstantiation(slotDecl.GetMethodDefinition(), dynamicDependencies, factory); } } overrideTypeCanonCur = overrideTypeCanonCur.BaseType; } } } return(dynamicDependencies); }
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> /// 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)); }
private void ImportCall(ILOpcode opcode, int token) { // Strip runtime determined characteristics off of the method (because that's how RyuJIT operates) var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); MethodDesc method = runtimeDeterminedMethod; if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { method = runtimeDeterminedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } if (method.IsRawPInvoke()) { // Raw P/invokes don't have any dependencies. return; } string reason = null; switch (opcode) { case ILOpcode.newobj: reason = "newobj"; break; case ILOpcode.call: reason = "call"; break; case ILOpcode.callvirt: reason = "callvirt"; break; case ILOpcode.ldftn: reason = "ldftn"; break; case ILOpcode.ldvirtftn: reason = "ldvirtftn"; break; default: Debug.Assert(false); break; } if (opcode == ILOpcode.newobj) { TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (owningType.IsString) { // String .ctor handled specially below } else if (owningType.IsGCPointer) { if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); } if (owningType.IsMdArray) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); return; } else { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); } } if (owningType.IsDelegate) { // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) { // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) { int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); if (info.NeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } return; } } } } if (method.OwningType.IsDelegate && method.Name == "Invoke") { // TODO: might not want to do this if scanning for reflection. // This is expanded as an intrinsic, not a function call. return; } if (method.IsIntrinsic) { if (IsRuntimeHelpersInitializeArray(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsRuntimeTypeHandleGetValueInternal(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsActivatorDefaultConstructorOf(method)) { if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DefaultConstructor, runtimeDeterminedMethod.Instantiation[0]), reason); } else { MethodDesc ctor = method.Instantiation[0].GetDefaultConstructor(); if (ctor == null) { MetadataType activatorType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "Activator"); MetadataType classWithMissingCtor = activatorType.GetKnownNestedType("ClassWithMissingConstructor"); ctor = classWithMissingCtor.GetParameterlessConstructor(); } _dependencies.Add(_factory.CanonicalEntrypoint(ctor), reason); } return; } if (method.OwningType.IsByReferenceOfT && (method.IsConstructor || method.Name == "get_Value")) { return; } if (IsEETypePtrOf(method)) { if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.Instantiation[0]), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(method.Instantiation[0]), reason); } return; } } TypeDesc exactType = method.OwningType; if (method.IsNativeCallable && (opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn)) { ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNativeCallable, method); } bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (_constrained != null) { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. TypeDesc constrained = _constrained; if (constrained.IsRuntimeDeterminedSubtype) { constrained = constrained.ConvertToCanonForm(CanonicalFormKind.Specific); } MethodDesc directMethod = constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); if (directMethod == null && constrained.IsEnum) { // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrained, method); } if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; exactType = constrained; } else if (constrained.IsValueType) { // We'll need to box `this`. Note we use _constrained here, because the other one is canonical. AddBoxingDependencies(_constrained, reason); } } MethodDesc targetMethod = methodAfterConstraintResolution; bool exactContextNeedsRuntimeLookup; if (targetMethod.HasInstantiation) { exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; } else { exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); } // // Determine whether to perform direct call // bool directCall = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { directCall = true; } } bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { // Needs a single address to call this method but the method needs a hidden argument. // We need a fat function pointer for this that captures both things. if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); } } else if (directCall) { bool referencingArrayAddressMethod = false; if (targetMethod.IsIntrinsic) { // If this is an intrinsic method with a callsite-specific expansion, this will replace // the method with a method the intrinsic expands into. If it's not the special intrinsic, // method stays unchanged. targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); // Array address method requires special dependency tracking. referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); } MethodDesc concreteMethod = targetMethod; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { _dependencies.Add(_factory.StringAllocator(targetMethod), reason); } else if (exactContextNeedsRuntimeLookup) { if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = GetGenericLookupHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod); } else if (targetMethod.RequiresInstMethodTableArg()) { bool hasHiddenParameter = true; if (targetMethod.IsIntrinsic) { if (_factory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(targetMethod)) { hasHiddenParameter = false; } } if (hasHiddenParameter) { instParam = GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); } } if (instParam != null) { _dependencies.Add(instParam, reason); } _dependencies.Add(_factory.CanonicalEntrypoint(targetMethod), reason); } else { Debug.Assert(!forceUseRuntimeLookup); _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); if (targetMethod.RequiresInstMethodTableArg() && resolvedConstraint) { if (_constrained.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, _constrained), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(_constrained), reason); } } if (referencingArrayAddressMethod && !_isReadOnly) { // Address method is special - it expects an instantiation argument, unless a readonly prefix was applied. _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), reason); } } } else { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } else if (targetMethod.RequiresInstMethodTableArg() || (referencingArrayAddressMethod && !_isReadOnly)) { // Ask for a constructed type symbol because we need the vtable to get to the dictionary instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); } if (instParam != null) { _dependencies.Add(instParam, reason); if (!referencingArrayAddressMethod) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { // We don't want array Address method to be modeled in the generic dependency analysis. // The method doesn't actually have runtime determined dependencies (won't do // any generic lookups). _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } else if (targetMethod.AcquiresInstMethodTableFromThis()) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } } else if (method.HasInstantiation) { // Generic virtual method call if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.RuntimeMethodHandle(runtimeDeterminedMethod), reason); } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); } else if (method.OwningType.IsInterface) { if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.VirtualDispatchCell, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); } } else if (_compilation.HasFixedSlotVTable(method.OwningType)) { // No dependencies: virtual call through the vtable } else { MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod).Normalize(); _dependencies.Add(_factory.VirtualMethodUse(slotDefiningMethod), reason); } }
/// <summary> /// Get the virtual slot index of a given virtual method. (This is only valid for non-interface virtuals) /// </summary> /// <param name="virtualMethod">virtual method to get slot index of</param> /// <returns>slot index, or -1</returns> public static int VirtualMethodToSlotIndex(MethodDesc virtualMethod) { Debug.Assert(virtualMethod.IsVirtual); Debug.Assert(!virtualMethod.OwningType.IsInterface); MethodDesc definingMethod = virtualMethod; // If not newslot, make sure we've got the defining method here if (!definingMethod.IsNewSlot) { definingMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(definingMethod); } TypeDesc definingType = definingMethod.OwningType; // Two possible cases for determining slot index that will work // 1. The definingType is a R2R type with full metadata. Compute the MethodDesc, by scanning the list of virtuals present in metadata. Its possible to not get a slot index. In that case return -1 // 2. The definingType is pregenerated, but we can go from metadata to slot index via the runtime mapping tables. if (!IsPregeneratedOrTemplateTypeLoaded(definingType)) { // Case 1 MetadataType definingMetadataType = (MetadataType)definingType; int baseTypeSlotCount = 0; if (definingMetadataType.BaseType != null) { unsafe { if (definingMetadataType.BaseType.RetrieveRuntimeTypeHandleIfPossible()) { baseTypeSlotCount = definingMetadataType.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; } else { baseTypeSlotCount = definingMetadataType.BaseType.GetOrCreateTypeBuilderState().NumVTableSlots; } } } int currentSlot = baseTypeSlotCount; if (definingMetadataType.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Specific)) { // Deal with the space reserved for the canonical dictionary currentSlot++; } foreach (MethodDesc method in definingMetadataType.GetMethods()) { if (!MethodDefinesVTableSlot(method)) { continue; } if (method == definingMethod) { return(currentSlot); } else { currentSlot++; } } // No slot index defined. return(-1); } else { // Case 2, pregenerated type TypeSystem.NativeFormat.NativeFormatMethod definingMethodOpenType = (TypeSystem.NativeFormat.NativeFormatMethod)definingMethod.GetTypicalMethodDefinition(); MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer( definingMethodOpenType.MetadataReader, definingMethodOpenType.Handle); if (!definingType.RetrieveRuntimeTypeHandleIfPossible()) { new TypeBuilder().BuildType(definingType); } TypeSystem.NativeFormat.NativeFormatType definingNativeFormatType = (TypeSystem.NativeFormat.NativeFormatType)definingType.GetTypeDefinition(); NativeFormatModuleInfo moduleToLookIn = definingNativeFormatType.MetadataUnit.RuntimeModuleInfo; TypeLoaderEnvironment.VirtualResolveDataResult virtualSlotInfo; if (!TypeLoaderEnvironment.TryGetVirtualResolveData(moduleToLookIn, definingType.RuntimeTypeHandle, Array.Empty <RuntimeTypeHandle>(), ref methodSignatureComparer, out virtualSlotInfo)) { return(-1); } Debug.Assert(!virtualSlotInfo.IsGVM); return(virtualSlotInfo.SlotIndex); } }
private void WalkMethod(EcmaMethod method) { Instantiation typeContext = method.OwningType.Instantiation; Instantiation methodContext = method.Instantiation; MethodSignature methodSig = method.Signature; ProcessTypeReference(methodSig.ReturnType, typeContext, methodContext); foreach (TypeDesc parameterType in methodSig) { ProcessTypeReference(parameterType, typeContext, methodContext); } if (method.IsAbstract) { return; } var methodIL = EcmaMethodIL.Create(method); if (methodIL == null) { return; } // If this is a generic virtual method, add an edge from each of the generic parameters // of the implementation to the generic parameters of the declaration - any call to the // declaration will be modeled as if the declaration was calling into the implementation. if (method.IsVirtual && method.HasInstantiation) { var decl = (EcmaMethod)MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).GetTypicalMethodDefinition(); if (decl != method) { Instantiation declInstantiation = decl.Instantiation; Instantiation implInstantiation = method.Instantiation; for (int i = 0; i < declInstantiation.Length; i++) { RecordBinding( (EcmaGenericParameter)implInstantiation[i], (EcmaGenericParameter)declInstantiation[i], isProperEmbedding: false); } } } // Walk the method body looking at referenced things that have some genericness. // Nongeneric things cannot be forming cycles. // In particular, we don't care about MemberRefs to non-generic things, TypeDefs/MethodDefs/FieldDefs. // Avoid the work to even materialize type system entities for those. ILReader reader = new ILReader(methodIL.GetILBytes()); while (reader.HasNext) { ILOpcode opcode = reader.ReadILOpcode(); switch (opcode) { case ILOpcode.sizeof_: case ILOpcode.newarr: case ILOpcode.initobj: case ILOpcode.stelem: case ILOpcode.ldelem: case ILOpcode.ldelema: case ILOpcode.box: case ILOpcode.unbox: case ILOpcode.unbox_any: case ILOpcode.cpobj: case ILOpcode.ldobj: case ILOpcode.castclass: case ILOpcode.isinst: case ILOpcode.stobj: case ILOpcode.refanyval: case ILOpcode.mkrefany: case ILOpcode.constrained: EntityHandle accessedType = MetadataTokens.EntityHandle(reader.ReadILToken()); typeCase: if (accessedType.Kind == HandleKind.TypeSpecification) { var t = methodIL.GetObject(MetadataTokens.GetToken(accessedType), NotFoundBehavior.ReturnNull) as TypeDesc; if (t != null) { ProcessTypeReference(t, typeContext, methodContext); } } break; case ILOpcode.stsfld: case ILOpcode.ldsfld: case ILOpcode.ldsflda: case ILOpcode.stfld: case ILOpcode.ldfld: case ILOpcode.ldflda: EntityHandle accessedField = MetadataTokens.EntityHandle(reader.ReadILToken()); fieldCase: if (accessedField.Kind == HandleKind.MemberReference) { accessedType = _metadataReader.GetMemberReference((MemberReferenceHandle)accessedField).Parent; goto typeCase; } break; case ILOpcode.call: case ILOpcode.callvirt: case ILOpcode.newobj: case ILOpcode.ldftn: case ILOpcode.ldvirtftn: case ILOpcode.jmp: EntityHandle accessedMethod = MetadataTokens.EntityHandle(reader.ReadILToken()); methodCase: if (accessedMethod.Kind == HandleKind.MethodSpecification || (accessedMethod.Kind == HandleKind.MemberReference && _metadataReader.GetMemberReference((MemberReferenceHandle)accessedMethod).Parent.Kind == HandleKind.TypeSpecification)) { var m = methodIL.GetObject(MetadataTokens.GetToken(accessedMethod), NotFoundBehavior.ReturnNull) as MethodDesc; ProcessTypeReference(m.OwningType, typeContext, methodContext); ProcessMethodCall(m, typeContext, methodContext); } break; case ILOpcode.ldtoken: EntityHandle accessedEntity = MetadataTokens.EntityHandle(reader.ReadILToken()); if (accessedEntity.Kind == HandleKind.MethodSpecification || (accessedEntity.Kind == HandleKind.MemberReference && _metadataReader.GetMemberReference((MemberReferenceHandle)accessedEntity).GetKind() == MemberReferenceKind.Method)) { accessedMethod = accessedEntity; goto methodCase; } else if (accessedEntity.Kind == HandleKind.MemberReference) { accessedField = accessedEntity; goto fieldCase; } else if (accessedEntity.Kind == HandleKind.TypeSpecification) { accessedType = accessedEntity; goto typeCase; } break; default: reader.Skip(opcode); break; } } }
public override IEnumerable <DependencyListEntry> GetStaticDependencies(NodeFactory factory) { Debug.Assert(!factory.MetadataManager.IsReflectionBlocked(_method.GetTypicalMethodDefinition())); // Depends on static virtual method support. Turning off reflection for now. if (_method.IsVirtual && _method.Signature.IsStatic) { return(null); } DependencyList dependencies = new DependencyList(); factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _method); // No runtime artifacts needed if this is a generic definition if (_method.IsGenericMethodDefinition || _method.OwningType.IsGenericDefinition) { return(dependencies); } // Ensure we consistently apply reflectability to all methods sharing the same definition. // Different instantiations of the method have a conditional dependency on the definition node that // brings a ReflectableMethod of the instantiated method if it's necessary for it to be reflectable. MethodDesc typicalMethod = _method.GetTypicalMethodDefinition(); if (typicalMethod != _method) { dependencies.Add(factory.ReflectableMethod(typicalMethod), "Definition of the reflectable method"); } MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); if (canonMethod != _method) { dependencies.Add(factory.ReflectableMethod(canonMethod), "Canonical version of the reflectable method"); } // Make sure we generate the method body and other artifacts. if (MetadataManager.IsMethodSupportedInReflectionInvoke(_method)) { if (_method.IsVirtual) { if (_method.HasInstantiation) { dependencies.Add(factory.GVMDependencies(_method.GetCanonMethodTarget(CanonicalFormKind.Specific)), "GVM callable reflectable method"); } else { // Virtual method use is tracked on the slot defining method only. MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method); if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) { dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Virtually callable reflectable method"); } } } if (!_method.IsAbstract) { dependencies.Add(factory.MethodEntrypoint(canonMethod), "Body of a reflectable method"); if (_method.HasInstantiation && _method != canonMethod) { dependencies.Add(factory.MethodGenericDictionary(_method), "Dictionary of a reflectable method"); } } } return(dependencies); }
private void ImportCall(ILOpcode opcode, int token) { // Strip runtime determined characteristics off of the method (because that's how RyuJIT operates) var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); MethodDesc method = runtimeDeterminedMethod; if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { method = runtimeDeterminedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } if (method.IsRawPInvoke()) { // Raw P/invokes don't have any dependencies. return; } string reason = null; switch (opcode) { case ILOpcode.newobj: reason = "newobj"; break; case ILOpcode.call: reason = "call"; break; case ILOpcode.callvirt: reason = "callvirt"; break; case ILOpcode.ldftn: reason = "ldftn"; break; case ILOpcode.ldvirtftn: reason = "ldvirtftn"; break; default: Debug.Assert(false); break; } // If we're scanning the fallback body because scanning the real body failed, don't trigger cctor. // Accessing the cctor could have been a reason why we failed. if (!_isFallbackBodyCompilation) { // Do we need to run the cctor? TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (_factory.TypeSystemContext.HasLazyStaticConstructor(owningType)) { // For beforefieldinit, we can wait for field access. if (!((MetadataType)owningType).IsBeforeFieldInit) { // Accessing the static base will trigger the cctor. if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); } } } } if (opcode == ILOpcode.newobj) { TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (owningType.IsString) { // String .ctor handled specially below } else { // Nullable needs to be unwrapped. if (owningType.IsNullable) { owningType = owningType.Instantiation[0]; } if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); } if (owningType.IsMdArray) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); return; } else { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); } } if (owningType.IsDelegate) { // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) { // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) { int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); if (info.NeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } return; } } } } if (method.OwningType.IsDelegate && method.Name == "Invoke") { // TODO: might not want to do this if scanning for reflection. // This is expanded as an intrinsic, not a function call. return; } if (method.IsIntrinsic) { if (IsRuntimeHelpersInitializeArray(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsRuntimeTypeHandleGetValueInternal(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } } TypeDesc exactType = method.OwningType; bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (_constrained != null) { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. MethodDesc directMethod = _constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); if (directMethod == null && _constrained.IsEnum) { // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(_constrained, method); } if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; exactType = _constrained; } else if (_constrained.IsValueType) { // We'll need to box `this`. AddBoxingDependencies(_constrained, reason); } _constrained = null; } MethodDesc targetMethod = methodAfterConstraintResolution; bool exactContextNeedsRuntimeLookup; if (targetMethod.HasInstantiation) { exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; } else { exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); } // // Determine whether to perform direct call // bool directCall = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { directCall = true; } } bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { // Needs a single address to call this method but the method needs a hidden argument. // We need a fat function pointer for this that captures both things. if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); } } else if (directCall) { bool referencingArrayAddressMethod = false; if (targetMethod.IsIntrinsic) { // If this is an intrinsic method with a callsite-specific expansion, this will replace // the method with a method the intrinsic expands into. If it's not the special intrinsic, // method stays unchanged. targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); // Array address method requires special dependency tracking. referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); } MethodDesc concreteMethod = targetMethod; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { _dependencies.Add(_factory.StringAllocator(targetMethod), reason); } else if (exactContextNeedsRuntimeLookup) { if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) { _dependencies.Add(_factory.RuntimeDeterminedMethod(runtimeDeterminedMethod), reason); } else { Debug.Assert(!forceUseRuntimeLookup); _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); } } else { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } else if (targetMethod.RequiresInstMethodTableArg() || referencingArrayAddressMethod) { // Ask for a constructed type symbol because we need the vtable to get to the dictionary instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); } if (instParam != null) { _dependencies.Add(instParam, reason); if (!referencingArrayAddressMethod) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { // We don't want array Address method to be modeled in the generic dependency analysis. // The method doesn't actually have runtime determined dependencies (won't do // any generic lookups). _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } else if (targetMethod.AcquiresInstMethodTableFromThis()) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } } else if (method.HasInstantiation) { // Generic virtual method call if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.RuntimeMethodHandle(runtimeDeterminedMethod), reason); } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); } else { ReadyToRunHelperId helper; if (opcode == ILOpcode.ldvirtftn) { helper = ReadyToRunHelperId.ResolveVirtualFunction; } else { Debug.Assert(opcode == ILOpcode.callvirt); helper = ReadyToRunHelperId.VirtualCall; } if (exactContextNeedsRuntimeLookup && targetMethod.OwningType.IsInterface) { _dependencies.Add(GetGenericLookupHelper(helper, runtimeDeterminedMethod), reason); } else { // Get the slot defining method to make sure our virtual method use tracking gets this right. // For normal C# code the targetMethod will always be newslot. MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); _dependencies.Add(_factory.ReadyToRunHelper(helper, slotDefiningMethod), reason); } } }
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) { return(new ObjectData(Array.Empty <byte>(), Array.Empty <Relocation>(), 1, new ISymbolNode[] { this })); } // Ensure the native layout blob has been saved factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); var writer = new NativeWriter(); var typeMapHashTable = new VertexHashtable(); Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapHashTable); Dictionary <int, HashSet <TypeDesc> > methodsEmitted = new Dictionary <int, HashSet <TypeDesc> >(); // Get a list of all methods that have a method body and metadata from the metadata manager. foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory)) { MethodDesc method = mappingEntry.Entity; // The current format requires us to have an EEType for the owning type. We might want to lift this. if (!factory.MetadataManager.TypeGeneratesEEType(method.OwningType)) { continue; } // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. if (!factory.MetadataManager.IsReflectionInvokable(method)) { continue; } // Only virtual methods are interesting if (!NeedsVirtualInvokeInfo(method)) { continue; } // // When working with the ProjectN ABI, the entries in the map are based on the definition types. All // instantiations of these definition types will have the same vtable method entries. // On CoreRT, the vtable entries for each instantiated type might not necessarily exist. // Example 1: // If there's a call to Foo<string>.Method1 and a call to Foo<int>.Method2, Foo<string> will // not have Method2 in its vtable and Foo<int> will not have Method1. // Example 2: // If there's a call to Foo<string>.Method1 and a call to Foo<object>.Method2, given that both // of these instantiations share the same canonical form, Foo<__Canon> will have both method // entries, and therefore Foo<string> and Foo<object> will have both entries too. // For this reason, the entries that we write to the map in CoreRT will be based on the canonical form // of the method's containing type instead of the open type definition. // // Similarly, given that we use the open type definition for ProjectN, the slot numbers ignore dictionary // entries in the vtable, and computing the correct slot number in the presence of dictionary entries is // done at runtime. When working with the CoreRT ABI, the correct slot numbers will be written to the map, // and no adjustments will be performed at runtime. // TypeDesc containingTypeKey; if (factory.Target.Abi == TargetAbi.ProjectN) { containingTypeKey = method.OwningType.GetTypeDefinition(); } else { containingTypeKey = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); } HashSet <TypeDesc> cache; if (!methodsEmitted.TryGetValue(mappingEntry.MetadataHandle, out cache)) { methodsEmitted[mappingEntry.MetadataHandle] = cache = new HashSet <TypeDesc>(); } // Only one record is needed for any instantiation. if (!cache.Add(containingTypeKey)) { continue; } // Grammar of an entry in the hash table: // Virtual Method uses a normal slot // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1) + slot // OR // Generic Virtual Method // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1 + 1) MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition()); uint parentHierarchyDistance = 0; TypeDesc typeOfDeclaringMethodForSlot = declaringMethodForSlot.OwningType.GetTypeDefinition(); TypeDesc currentType = method.OwningType.GetTypeDefinition(); while (typeOfDeclaringMethodForSlot != currentType) { parentHierarchyDistance++; currentType = currentType.BaseType.GetTypeDefinition(); } Vertex vertex = null; ISymbolNode containingTypeKeyNode = factory.NecessaryTypeSymbol(containingTypeKey); NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); if (method.HasInstantiation) { vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset), writer.GetUnsignedConstant((parentHierarchyDistance << 1) + VirtualInvokeTableEntry.GenericVirtualMethod)); } else { // Get the declaring method for slot on the instantiated declaring type if (method.OwningType.HasInstantiation) { TypeDesc containingTypeOfDeclaringMethodForSlot = method.OwningType; for (int i = 0; i < parentHierarchyDistance; i++) { containingTypeOfDeclaringMethodForSlot = containingTypeOfDeclaringMethodForSlot.BaseType; } declaringMethodForSlot = containingTypeOfDeclaringMethodForSlot.GetMethod(declaringMethodForSlot.Name, declaringMethodForSlot.Signature); } int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, factory.Target.Abi != TargetAbi.ProjectN); if (slot == -1) { // This method doesn't have a slot. (At this time, this is only done for the Object.Finalize method) Debug.Assert(declaringMethodForSlot.Name == "Finalize"); continue; } vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset)); vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(parentHierarchyDistance << 1), writer.GetUnsignedConstant((uint)slot)); } int hashCode = containingTypeKey.GetHashCode(); typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); } byte[] hashTableBytes = writer.Save(); _endSymbol.SetSymbolOffset(hashTableBytes.Length); return(new ObjectData(hashTableBytes, Array.Empty <Relocation>(), 1, new ISymbolNode[] { this, _endSymbol })); }