Propagate BestFitMapping/ThrowOnUnmappable from type/assembly
authorFadi Hanna <fadim@microsoft.com>
Thu, 21 Nov 2019 00:10:49 +0000 (16:10 -0800)
committerFadi Hanna <fadim@microsoft.com>
Thu, 21 Nov 2019 03:11:07 +0000 (19:11 -0800)
src/coreclr/src/tools/crossgen2/Common/TypeSystem/Ecma/EcmaMethod.cs

index 3c41f93d6f97fd78e50977b0940d1332ea7e0cd0..6d1aa8a59bc5d2978d85ffeaea362e379880c058 100644 (file)
@@ -454,19 +454,48 @@ namespace Internal.TypeSystem.Ecma
                 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 @@ namespace Internal.TypeSystem.Ecma
             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<TypeDesc> 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<TypeDesc> 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;