示例#1
0
        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);
            }
        }
示例#2
0
        private void ResolveInterfaceDispatch_ForMultiGenericTest(MetadataType type, out MethodDesc md1, out MethodDesc md2)
        {
            var algo       = new MetadataVirtualMethodAlgorithm();
            var stringType = _context.GetWellKnownType(WellKnownType.String);

            var             bang0Type     = _context.GetSignatureVariable(0, false);
            var             bang1Type     = _context.GetSignatureVariable(1, false);
            MethodSignature sigBang0Bang1 = new MethodSignature(0, 0, stringType, new TypeDesc[] { bang0Type, bang1Type });
            MethodSignature sigBang1Bang0 = new MethodSignature(0, 0, stringType, new TypeDesc[] { bang1Type, bang0Type });

            var iMultiGeneric = type.ExplicitlyImplementedInterfaces.First();
            var method0_1     = iMultiGeneric.GetMethod("Func", sigBang0Bang1);

            Assert.NotNull(method0_1);
            var method1_0 = iMultiGeneric.GetMethod("Func", sigBang1Bang0);

            Assert.NotNull(method1_0);

            md1 = algo.ResolveInterfaceMethodToVirtualMethodOnType(method0_1, type);
            md2 = algo.ResolveInterfaceMethodToVirtualMethodOnType(method1_0, type);
            Assert.NotNull(md1);
            Assert.NotNull(md2);
        }
        public static void GetVirtualInvokeMapDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            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");
                    }
                }
            }
        }
示例#4
0
        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);
                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);
        }
示例#5
0
        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);
        }
示例#6
0
        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);
        }
示例#7
0
        /// <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));
        }
示例#8
0
        /// <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);
            }
        }
示例#9
0
            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;
                    }
                }
            }
示例#10
0
        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 }));
        }
示例#11
0
        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);
        }
示例#12
0
        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);
            }
        }
        public void TestFindBaseUnificationGroup()
        {
            var algo = new MetadataVirtualMethodAlgorithm();
            var ilModule = _context.GetModuleForSimpleName("ILTestAssembly");
            MetadataType myDerived2Type = ilModule.GetType("VirtualFunctionOverride", "MyDerived2");
            Assert.NotNull(myDerived2Type);
            MethodDesc method = myDerived2Type.GetMethod("get_foo", null);
            Assert.NotNull(method);

            MethodDesc virtualMethod = algo.FindVirtualFunctionTargetMethodOnObjectType(method, myDerived2Type);
            Assert.NotNull(virtualMethod);
            Assert.Equal(method, virtualMethod);
        }
示例#14
0
        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);
                }
            }
        }