diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Ecma/EcmaMethod.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Ecma/EcmaMethod.cs index 3c41f93d6f97fd78e50977b0940d1332ea7e0cd0..6d1aa8a59bc5d2978d85ffeaea362e379880c058 100644 --- a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Ecma/EcmaMethod.cs +++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Ecma/EcmaMethod.cs @@ -454,19 +454,48 @@ public override PInvokeMetadata GetPInvokeMethodMetadata() return default(PInvokeMetadata); MetadataReader metadataReader = MetadataReader; - MethodImport import = metadataReader.GetMethodDefinition(_handle).GetImport(); + MethodDefinition methodDef = metadataReader.GetMethodDefinition(_handle); + MethodImport import = methodDef.GetImport(); string name = metadataReader.GetString(import.Name); ModuleReference moduleRef = metadataReader.GetModuleReference(import.Module); string moduleName = metadataReader.GetString(moduleRef.Name); + MethodImportAttributes importAttributes = import.Attributes; + + // If either BestFitMapping or ThrowOnUnmappable wasn't set on the p/invoke, + // look for the value in the owning type or assembly. + if ((importAttributes & MethodImportAttributes.BestFitMappingMask) == 0 || + (importAttributes & MethodImportAttributes.ThrowOnUnmappableCharMask) == 0) + { + TypeDefinition declaringType = metadataReader.GetTypeDefinition(methodDef.GetDeclaringType()); + + // Start with owning type + MethodImportAttributes fromCA = GetImportAttributesFromBestFitMappingAttribute(declaringType.GetCustomAttributes()); + if ((importAttributes & MethodImportAttributes.BestFitMappingMask) == 0) + importAttributes |= fromCA & MethodImportAttributes.BestFitMappingMask; + if ((importAttributes & MethodImportAttributes.ThrowOnUnmappableCharMask) == 0) + importAttributes |= fromCA & MethodImportAttributes.ThrowOnUnmappableCharMask; + + // If we still don't know, check the assembly + if ((importAttributes & MethodImportAttributes.BestFitMappingMask) == 0 || + (importAttributes & MethodImportAttributes.ThrowOnUnmappableCharMask) == 0) + { + fromCA = GetImportAttributesFromBestFitMappingAttribute(metadataReader.GetAssemblyDefinition().GetCustomAttributes()); + if ((importAttributes & MethodImportAttributes.BestFitMappingMask) == 0) + importAttributes |= fromCA & MethodImportAttributes.BestFitMappingMask; + if ((importAttributes & MethodImportAttributes.ThrowOnUnmappableCharMask) == 0) + importAttributes |= fromCA & MethodImportAttributes.ThrowOnUnmappableCharMask; + } + } + // Spot check the enums match Debug.Assert((int)MethodImportAttributes.CallingConventionStdCall == (int)PInvokeAttributes.CallingConventionStdCall); Debug.Assert((int)MethodImportAttributes.CharSetAuto == (int)PInvokeAttributes.CharSetAuto); Debug.Assert((int)MethodImportAttributes.CharSetUnicode == (int)PInvokeAttributes.CharSetUnicode); Debug.Assert((int)MethodImportAttributes.SetLastError == (int)PInvokeAttributes.SetLastError); - PInvokeAttributes attributes = (PInvokeAttributes)import.Attributes; + PInvokeAttributes attributes = (PInvokeAttributes)importAttributes; if ((ImplAttributes & MethodImplAttributes.PreserveSig) != 0) attributes |= PInvokeAttributes.PreserveSig; @@ -474,6 +503,47 @@ public override PInvokeMetadata GetPInvokeMethodMetadata() return new PInvokeMetadata(moduleName, name, attributes); } + private MethodImportAttributes GetImportAttributesFromBestFitMappingAttribute(CustomAttributeHandleCollection attributeHandles) + { + // Look for the [BestFitMapping(BestFitMapping: x, ThrowOnUnmappableChar = y)] attribute and + // translate that to MethodImportAttributes + + MethodImportAttributes result = 0; + MetadataReader reader = MetadataReader; + + CustomAttributeHandle attributeHandle = reader.GetCustomAttributeHandle( + attributeHandles, "System.Runtime.InteropServices", "BestFitMappingAttribute"); + if (!attributeHandle.IsNil) + { + CustomAttribute attribute = reader.GetCustomAttribute(attributeHandle); + CustomAttributeValue decoded = attribute.DecodeValue( + new CustomAttributeTypeProvider(_type.EcmaModule)); + + if (decoded.FixedArguments.Length != 1 || !(decoded.FixedArguments[0].Value is bool)) + ThrowHelper.ThrowBadImageFormatException(); + if ((bool)decoded.FixedArguments[0].Value) + result |= MethodImportAttributes.BestFitMappingEnable; + else + result |= MethodImportAttributes.BestFitMappingDisable; + + foreach (CustomAttributeNamedArgument namedArg in decoded.NamedArguments) + { + if (namedArg.Name == "ThrowOnUnmappableChar") + { + if (!(namedArg.Value is bool)) + ThrowHelper.ThrowBadImageFormatException(); + if ((bool)namedArg.Value) + result |= MethodImportAttributes.ThrowOnUnmappableCharEnable; + else + result |= MethodImportAttributes.ThrowOnUnmappableCharDisable; + break; + } + } + } + + return result; + } + public override ParameterMetadata[] GetParameterMetadata() { MetadataReader metadataReader = MetadataReader;