// // A true result indicates that a type can never be a value type. This is important when testing variance-compatibility. // private static bool ProvablyAGcReferenceType(this Type t, CoreTypes coreTypes) { if (t.IsGenericParameter) { GenericParameterAttributes attributes = t.GenericParameterAttributes; if ((attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) { return(true); // generic parameter with a "class" constraint. } } return(t.ProvablyAGcReferenceTypeHelper(coreTypes)); }
// Keep this separate from the other TypeClassification computations as it locks in the core assembly name. protected sealed override bool IsPrimitiveImpl() { CoreTypes coreTypes = Loader.GetAllFoundCoreTypes(); foreach (CoreType primitiveType in s_primitiveTypes) { if (this == coreTypes[primitiveType]) { return(true); } } return(false); }
private CustomAttributeData ComputeDllImportCustomAttributeDataIfAny() { if ((Attributes & MethodAttributes.PinvokeImpl) == 0) { return(null); } // Make sure all the necessary framework types exist in this MetadataLoadContext's core assembly. If one doesn't, skip. CoreTypes ct = Loader.GetAllFoundCoreTypes(); if (ct[CoreType.String] == null || ct[CoreType.Boolean] == null || ct[CoreType.DllImportAttribute] == null || ct[CoreType.CharSet] == null || ct[CoreType.CallingConvention] == null) { return(null); } ConstructorInfo ctor = Loader.TryGetDllImportCtor(); if (ctor == null) { return(null); } Func <CustomAttributeArguments> argumentsPromise = () => { // The expensive work goes in here. Type attributeType = ctor.DeclaringType; DllImportAttribute dia = _decoder.ComputeDllImportAttribute(); CustomAttributeTypedArgument[] cats = { new CustomAttributeTypedArgument(ct[CoreType.String], dia.Value) }; CustomAttributeNamedArgument[] cans = { attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.EntryPoint), ct[CoreType.String], dia.EntryPoint), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.CharSet), ct[CoreType.CharSet], (int)dia.CharSet), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.CallingConvention), ct[CoreType.CallingConvention], (int)dia.CallingConvention), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.ExactSpelling), ct[CoreType.Boolean], dia.ExactSpelling), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.PreserveSig), ct[CoreType.Boolean], dia.PreserveSig), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.SetLastError), ct[CoreType.Boolean], dia.SetLastError), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.BestFitMapping), ct[CoreType.Boolean], dia.BestFitMapping), attributeType.ToCustomAttributeNamedArgument(nameof(DllImportAttribute.ThrowOnUnmappableChar), ct[CoreType.Boolean], dia.ThrowOnUnmappableChar), }; return(new CustomAttributeArguments(cats, cans)); }; return(new RoPseudoCustomAttributeData(ctor, argumentsPromise)); }
private static bool CanCastTo(this Type fromTypeInfo, Type toTypeInfo, CoreTypes coreTypes) { if (fromTypeInfo.Equals(toTypeInfo)) { return(true); } if (fromTypeInfo.IsArray) { if (toTypeInfo.IsInterface) { return(fromTypeInfo.CanCastArrayToInterface(toTypeInfo, coreTypes)); } if (fromTypeInfo.IsSubclassOf(toTypeInfo)) { return(true); // T[] is castable to Array or Object. } if (!toTypeInfo.IsArray) { return(false); } int rank = fromTypeInfo.GetArrayRank(); if (rank != toTypeInfo.GetArrayRank()) { return(false); } bool fromTypeIsSzArray = fromTypeInfo.IsSZArray(); bool toTypeIsSzArray = toTypeInfo.IsSZArray(); if (fromTypeIsSzArray != toTypeIsSzArray) { // T[] is assignable to T[*] but not vice-versa. if (!(rank == 1 && !toTypeIsSzArray)) { return(false); // T[*] is not castable to T[] } } Type toElementTypeInfo = toTypeInfo.GetElementType(); Type fromElementTypeInfo = fromTypeInfo.GetElementType(); return(fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, coreTypes)); } if (fromTypeInfo.IsByRef) { if (!toTypeInfo.IsByRef) { return(false); } Type toElementTypeInfo = toTypeInfo.GetElementType(); Type fromElementTypeInfo = fromTypeInfo.GetElementType(); return(fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, coreTypes)); } if (fromTypeInfo.IsPointer) { if (toTypeInfo.Equals(coreTypes[CoreType.Object])) { return(true); // T* is castable to Object. } if (toTypeInfo.Equals(coreTypes[CoreType.UIntPtr])) { return(true); // T* is castable to UIntPtr (but not IntPtr) } if (!toTypeInfo.IsPointer) { return(false); } Type toElementTypeInfo = toTypeInfo.GetElementType(); Type fromElementTypeInfo = fromTypeInfo.GetElementType(); return(fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, coreTypes)); } if (fromTypeInfo.IsGenericParameter) { // // A generic parameter can be cast to any of its constraints, or object, if none are specified, or ValueType if the "struct" constraint is // specified. // // This has to be coded as its own case as TypeInfo.BaseType on a generic parameter doesn't always return what you'd expect. // if (toTypeInfo.Equals(coreTypes[CoreType.Object])) { return(true); } if (toTypeInfo.Equals(coreTypes[CoreType.ValueType])) { GenericParameterAttributes attributes = fromTypeInfo.GenericParameterAttributes; if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) { return(true); } } foreach (Type constraintType in fromTypeInfo.GetGenericParameterConstraints()) { if (constraintType.CanCastTo(toTypeInfo, coreTypes)) { return(true); } } return(false); } if (toTypeInfo.IsArray || toTypeInfo.IsByRef || toTypeInfo.IsPointer || toTypeInfo.IsGenericParameter) { return(false); } if (fromTypeInfo.MatchesWithVariance(toTypeInfo, coreTypes)) { return(true); } if (toTypeInfo.IsInterface) { foreach (Type ifc in fromTypeInfo.GetInterfaces()) { if (ifc.MatchesWithVariance(toTypeInfo, coreTypes)) { return(true); } } return(false); } else { // Interfaces are always castable to System.Object. The code below will not catch this as interfaces report their BaseType as null. if (toTypeInfo.Equals(coreTypes[CoreType.Object]) && fromTypeInfo.IsInterface) { return(true); } Type walk = fromTypeInfo; for (;;) { Type baseType = walk.BaseType; if (baseType == null) { return(false); } walk = baseType; if (walk.MatchesWithVariance(toTypeInfo, coreTypes)) { return(true); } } } }
// // T[] casts to IList<T>. This could be handled by the normal ancestor-walking code // but for one complication: T[] also casts to IList<U> if T[] casts to U[]. // private static bool CanCastArrayToInterface(this Type fromTypeInfo, Type toTypeInfo, CoreTypes coreTypes) { Debug.Assert(fromTypeInfo.IsArray); Debug.Assert(toTypeInfo.IsInterface); if (toTypeInfo.IsConstructedGenericType) { Type[] toTypeGenericTypeArguments = toTypeInfo.GenericTypeArguments; if (toTypeGenericTypeArguments.Length != 1) { return(false); } Type toElementTypeInfo = toTypeGenericTypeArguments[0]; Type toTypeGenericTypeDefinition = toTypeInfo.GetGenericTypeDefinition(); Type fromElementTypeInfo = fromTypeInfo.GetElementType(); foreach (Type ifc in fromTypeInfo.GetInterfaces()) { if (ifc.IsConstructedGenericType) { Type ifcGenericTypeDefinition = ifc.GetGenericTypeDefinition(); if (ifcGenericTypeDefinition.Equals(toTypeGenericTypeDefinition)) { if (fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, coreTypes)) { return(true); } } } } return(false); } else { foreach (Type ifc in fromTypeInfo.GetInterfaces()) { if (ifc.Equals(toTypeInfo)) { return(true); } } return(false); } }
// // Contra/CoVariance. // // IEnumerable<D> can cast to IEnumerable<B> if D can cast to B and if there's no possibility that D is a value type. // private static bool IsGcReferenceTypeAndCastableTo(this Type fromTypeInfo, Type toTypeInfo, CoreTypes coreTypes) { if (fromTypeInfo.Equals(toTypeInfo)) { return(true); } if (fromTypeInfo.ProvablyAGcReferenceType(coreTypes)) { return(fromTypeInfo.CanCastTo(toTypeInfo, coreTypes)); } return(false); }
// // A[] can cast to B[] if one of the following are true: // // A can cast to B under variance rules. // // A and B are both integers or enums and have the same reduced type (i.e. represent the same-sized integer, ignoring signed/unsigned differences.) // "char" is not interchangable with short/ushort. "bool" is not interchangable with byte/sbyte. // // For desktop compat, A& and A* follow the same rules. // private static bool IsElementTypeCompatibleWith(this Type fromTypeInfo, Type toTypeInfo, CoreTypes coreTypes) { if (fromTypeInfo.IsGcReferenceTypeAndCastableTo(toTypeInfo, coreTypes)) { return(true); } Type reducedFromType = fromTypeInfo.ReducedType(coreTypes); Type reducedToType = toTypeInfo.ReducedType(coreTypes); if (reducedFromType.Equals(reducedToType)) { return(true); } return(false); }
// // Check a base type or implemented interface type for equivalence (taking into account variance for generic instantiations.) // Does not check ancestors recursively. // private static bool MatchesWithVariance(this Type fromTypeInfo, Type toTypeInfo, CoreTypes coreTypes) { Debug.Assert(!(fromTypeInfo.IsArray || fromTypeInfo.IsByRef || fromTypeInfo.IsPointer || fromTypeInfo.IsGenericParameter)); Debug.Assert(!(toTypeInfo.IsArray || toTypeInfo.IsByRef || toTypeInfo.IsPointer || toTypeInfo.IsGenericParameter)); if (fromTypeInfo.Equals(toTypeInfo)) { return(true); } if (!(fromTypeInfo.IsConstructedGenericType && toTypeInfo.IsConstructedGenericType)) { return(false); } Type genericTypeDefinition = fromTypeInfo.GetGenericTypeDefinition(); if (!genericTypeDefinition.Equals(toTypeInfo.GetGenericTypeDefinition())) { return(false); } Type[] fromTypeArguments = fromTypeInfo.GenericTypeArguments; Type[] toTypeArguments = toTypeInfo.GenericTypeArguments; Type[] genericTypeParameters = genericTypeDefinition.GetGenericTypeParameters(); for (int i = 0; i < genericTypeParameters.Length; i++) { Type fromTypeArgumentInfo = fromTypeArguments[i]; Type toTypeArgumentInfo = toTypeArguments[i]; GenericParameterAttributes attributes = genericTypeParameters[i].GenericParameterAttributes; switch (attributes & GenericParameterAttributes.VarianceMask) { case GenericParameterAttributes.Covariant: if (!(fromTypeArgumentInfo.IsGcReferenceTypeAndCastableTo(toTypeArgumentInfo, coreTypes))) { return(false); } break; case GenericParameterAttributes.Contravariant: if (!(toTypeArgumentInfo.IsGcReferenceTypeAndCastableTo(fromTypeArgumentInfo, coreTypes))) { return(false); } break; case GenericParameterAttributes.None: if (!(fromTypeArgumentInfo.Equals(toTypeArgumentInfo))) { return(false); } break; default: throw new BadImageFormatException(); // Unexpected variance value in metadata. } } return(true); }
/// <summary> /// Convert MarshalAsAttribute data into CustomAttributeData form. Returns null if the core assembly cannot be loaded or if the necessary /// types aren't in the core assembly. /// </summary> public static CustomAttributeData TryComputeMarshalAsCustomAttributeData(Func <MarshalAsAttribute> marshalAsAttributeComputer, TypeLoader loader) { // Make sure all the necessary framework types exist in this TypeLoader's core assembly. If one doesn't, skip. CoreTypes ct = loader.GetAllFoundCoreTypes(); if (ct[CoreType.String] == null || ct[CoreType.Boolean] == null || ct[CoreType.UnmanagedType] == null || ct[CoreType.VarEnum] == null || ct[CoreType.Type] == null || ct[CoreType.Int16] == null || ct[CoreType.Int32] == null) { return(null); } ConstructorInfo ci = loader.TryGetMarshalAsCtor(); if (ci == null) { return(null); } Func <CustomAttributeArguments> argumentsPromise = () => { // The expensive work goes in here. It will not execute unless someone invokes the Constructor/NamedArguments properties on // the CustomAttributeData. MarshalAsAttribute ma = marshalAsAttributeComputer(); Type attributeType = ci.DeclaringType; CustomAttributeTypedArgument[] cats = { new CustomAttributeTypedArgument(ct[CoreType.UnmanagedType], (int)(ma.Value)) }; List <CustomAttributeNamedArgument> cans = new List <CustomAttributeNamedArgument>(); cans.AddRange(new CustomAttributeNamedArgument[] { attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.ArraySubType), ct[CoreType.UnmanagedType], (int)ma.ArraySubType), attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.IidParameterIndex), ct[CoreType.Int32], ma.IidParameterIndex), attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SafeArraySubType), ct[CoreType.VarEnum], (int)ma.SafeArraySubType), attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SizeConst), ct[CoreType.Int32], ma.SizeConst), attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SizeParamIndex), ct[CoreType.Int16], ma.SizeParamIndex), }); if (ma.SafeArrayUserDefinedSubType != null) { cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SafeArrayUserDefinedSubType), ct[CoreType.Type], ma.SafeArrayUserDefinedSubType)); } if (ma.MarshalType != null) { cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalType), ct[CoreType.String], ma.MarshalType)); } if (ma.MarshalTypeRef != null) { cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalTypeRef), ct[CoreType.Type], ma.MarshalTypeRef)); } if (ma.MarshalCookie != null) { cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalCookie), ct[CoreType.String], ma.MarshalCookie)); } return(new CustomAttributeArguments(cats, cans)); }; return(new RoPseudoCustomAttributeData(ci, argumentsPromise)); }