Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress linker warnings properly #84272

Merged
merged 7 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System.Reflection.Emit
{
// This static helper class adds common entities to a Metadata Builder.
// This static helper class adds common entities to a MetadataBuilder.
internal static class MetadataHelper
{
internal static AssemblyReferenceHandle AddAssemblyReference(Assembly assembly, MetadataBuilder metadata)
Expand Down Expand Up @@ -34,7 +34,7 @@ internal static TypeDefinitionHandle AddTypeDefinition(MetadataBuilder metadata,
// Add type metadata
return metadata.AddTypeDefinition(
attributes: typeBuilder.Attributes,
(typeBuilder.Namespace == null) ? default : metadata.GetOrAddString(typeBuilder.Namespace),
@namespace: (typeBuilder.Namespace == null) ? default : metadata.GetOrAddString(typeBuilder.Namespace),
name: metadata.GetOrAddString(typeBuilder.Name),
baseType: baseType,
fieldList: MetadataTokens.FieldDefinitionHandle(fieldToken),
Expand All @@ -49,28 +49,30 @@ internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, T
internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, AssemblyReferenceHandle parent, string name, string? nameSpace)
{
return metadata.AddTypeReference(
parent,
(nameSpace == null) ? default : metadata.GetOrAddString(nameSpace),
metadata.GetOrAddString(name)
resolutionScope: parent,
@namespace: (nameSpace == null) ? default : metadata.GetOrAddString(nameSpace),
name: metadata.GetOrAddString(name)
);
}

internal static MethodDefinitionHandle AddMethodDefinition(MetadataBuilder metadata, MethodBuilderImpl methodBuilder, BlobBuilder methodSignatureBlob)
{
return metadata.AddMethodDefinition(
methodBuilder.Attributes,
MethodImplAttributes.IL,
metadata.GetOrAddString(methodBuilder.Name),
metadata.GetOrAddBlob(methodSignatureBlob),
-1, // No body supported yet
attributes: methodBuilder.Attributes,
implAttributes: MethodImplAttributes.IL,
name: metadata.GetOrAddString(methodBuilder.Name),
signature: metadata.GetOrAddBlob(methodSignatureBlob),
bodyOffset: -1, // No body supported yet
parameterList: MetadataTokens.ParameterHandle(1)
);
}

internal static FieldDefinitionHandle AddFieldDefinition(MetadataBuilder metadata, FieldInfo field)
internal static FieldDefinitionHandle AddFieldDefinition(MetadataBuilder metadata, FieldInfo field, BlobBuilder fieldSignatureBlob)
{
return metadata.AddFieldDefinition(field.Attributes, metadata.GetOrAddString(field.Name),
metadata.GetOrAddBlob(MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType)));
return metadata.AddFieldDefinition(
attributes: field.Attributes,
name: metadata.GetOrAddString(field.Name),
signature: metadata.GetOrAddBlob(fieldSignatureBlob));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

namespace System.Reflection.Emit
{
Expand All @@ -22,7 +21,7 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv
Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType)
{
_module = module;
_returnType = returnType ?? _module.GetTypeFromCoreAssembly("System.Void"); ;
_returnType = returnType ?? _module.GetTypeFromCoreAssembly(CoreTypeId.Void);
_name = name;
_attributes = attributes;
_callingConventions = callingConventions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,49 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder
{
private readonly Assembly _coreAssembly;
private readonly string _name;
private readonly Dictionary<string, Type> _coreTypes = new();
private Type?[] _coreTypes;
private readonly Dictionary<Assembly, AssemblyReferenceHandle> _assemblyRefStore = new();
private readonly Dictionary<Type, TypeReferenceHandle> _typeRefStore = new();
private readonly List<TypeBuilderImpl> _typeDefStore = new();
private int _nextMethodDefRowId = 1;
private int _nextFieldDefRowId = 1;

private static readonly Type[] s_coreTypes = { typeof(void), typeof(object), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int),
typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(string), typeof(nint), typeof(nuint) };
internal ModuleBuilderImpl(string name, Assembly coreAssembly)
{
_coreAssembly = coreAssembly;
// Use s_coreTypes directly for runtime reflection
_coreTypes = (_coreAssembly == typeof(object).Assembly) ? s_coreTypes : new Type[s_coreTypes.Length];
_name = name;
}

internal Type GetTypeFromCoreAssembly(string name)
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Types are preserved via s_coreTypes")]
internal Type GetTypeFromCoreAssembly(CoreTypeId typeId)
{
int index = (int)typeId;

return _coreTypes[index] ?? (_coreTypes[index] = _coreAssembly.GetType(s_coreTypes[index].FullName!, throwOnError: true)!);
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Types are preserved via s_coreTypes")]
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
internal int GetTypeIdFromCoreTypes(Type type)
jkotas marked this conversation as resolved.
Show resolved Hide resolved
{
Type? type;
for(int i=0; i < _coreTypes.Length; i++)
{
if (_coreTypes[i] == type)
{
return i;
}
}

Type? foundType = _coreAssembly.GetType(type.FullName!, throwOnError: true);

// TODO: Use Enum as the key for perf
if (!_coreTypes.TryGetValue(name, out type))
if (foundType == null || !Enum.TryParse(foundType.Name, out CoreTypeId result))
jkotas marked this conversation as resolved.
Show resolved Hide resolved
{
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
type = _coreAssembly.GetType(name, throwOnError: true)!;
#pragma warning restore IL2026
_coreTypes.Add(name, type);
return -1;
}
jkotas marked this conversation as resolved.
Show resolved Hide resolved

return type;
return (int)result;
}

internal void AppendMetadata(MetadataBuilder metadata)
Expand Down Expand Up @@ -82,7 +98,7 @@ internal void AppendMetadata(MetadataBuilder metadata)

foreach (FieldBuilderImpl field in typeBuilder._fieldDefStore)
{
MetadataHelper.AddFieldDefinition(metadata, field);
MetadataHelper.AddFieldDefinition(metadata, field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this));
_nextFieldDefRowId++;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

Expand All @@ -9,16 +10,16 @@ namespace System.Reflection.Emit
// TODO: Only support simple signatures. More complex signatures will be added.
internal static class MetadataSignatureHelper
{
internal static BlobBuilder FieldSignatureEncoder(Type fieldType)
internal static BlobBuilder FieldSignatureEncoder(Type fieldType, ModuleBuilderImpl module)
{
BlobBuilder fieldSignature = new();

WriteSignatureTypeForReflectionType(new BlobEncoder(fieldSignature).FieldSignature(), fieldType);
WriteSignatureTypeForReflectionType(new BlobEncoder(fieldSignature).FieldSignature(), fieldType, module);

return fieldSignature;
}

internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl _module, Type[]? parameters, Type? returnType, bool isInstance)
internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, bool isInstance)
{
// Encoding return type and parameters.
BlobBuilder methodSignature = new();
Expand All @@ -30,9 +31,9 @@ internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl _module, Ty
MethodSignature(isInstanceMethod: isInstance).
Parameters((parameters == null) ? 0 : parameters.Length, out retEncoder, out parEncoder);

if (returnType != null && returnType != _module.GetTypeFromCoreAssembly("System.Void"))
if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void))
{
WriteSignatureTypeForReflectionType(retEncoder.Type(), returnType);
WriteSignatureTypeForReflectionType(retEncoder.Type(), returnType, module);
}
else // If null mark ReturnTypeEncoder as void
{
Expand All @@ -43,46 +44,95 @@ internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl _module, Ty
{
foreach (Type parameter in parameters)
{
WriteSignatureTypeForReflectionType(parEncoder.AddParameter().Type(), parameter);
WriteSignatureTypeForReflectionType(parEncoder.AddParameter().Type(), parameter, module);
}
}

return methodSignature;
}

private static void WriteSignatureTypeForReflectionType(SignatureTypeEncoder signature, Type type)
private static void WriteSignatureTypeForReflectionType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module)
{
// We need to translate from Reflection.Type to SignatureTypeEncoder. Most common types for proof of concept. More types will be added.
// TODO: This switch should be done by comparing Type objects, without fetching FullName.
switch (type.FullName)
{
case "System.Boolean":
signature.Boolean();
break;
case "System.Byte":
signature.Byte();
break;
case "System.Char":
signature.Char();
break;
case "System.Double":
signature.Double();
break;
case "System.Int32":
signature.Int32();
break;
case "System.Int64":
signature.Int64();
break;
case "System.Object":
signature.Object();
break;
case "System.String":
signature.String();
break;
int id = module.GetTypeIdFromCoreTypes(type);

default: throw new NotSupportedException(SR.Format(SR.NotSupported_Signature, type.FullName));
// We need to translate from Reflection.Type to SignatureTypeEncoder.
if (id > -1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the extra if?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

module.GetTypeIdFromCoreTypes(type) returns -1 in case it is not core type or the type not found

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right but why do you optimizing for exception code path?

Copy link
Contributor Author

@buyaa-n buyaa-n Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now we only support core types, eventually that path will be updated to support other types

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep but even in that case they would better be handled via default: than this if

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would a default signature option do?

The existing PrimitiveTypeCode defined by System.Reflection.Metadata looks very similar for the CoreTypeId. Maybe we should just use PrimitiveTypeCode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should just use PrimitiveTypeCode.

Thoughts about this one?

Copy link
Contributor Author

@buyaa-n buyaa-n Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts about this one?

Tried, PrimitiveTypeCode uses SignatureTypeCode values that are not matching by the index, specifically the value of IntPtr is 24, UIntPtr is 25, Object value is 28, leaving a gap in the array indexes

{
switch ((CoreTypeId)id)
{
case CoreTypeId.Boolean:
signature.Boolean();
return;
case CoreTypeId.Byte:
signature.Byte();
return;
case CoreTypeId.SByte:
signature.SByte();
return;
case CoreTypeId.Char:
signature.Char();
return;
case CoreTypeId.Int16:
signature.Int16();
break;
case CoreTypeId.UInt16:
signature.UInt16();
return;
case CoreTypeId.Int32:
signature.Int32();
return;
case CoreTypeId.UInt32:
signature.UInt32();
return;
case CoreTypeId.Int64:
signature.Int64();
return;
case CoreTypeId.UInt64:
signature.UInt64();
return;
case CoreTypeId.Single:
signature.Single();
return;
case CoreTypeId.Double:
signature.Double();
return;
case CoreTypeId.IntPtr:
signature.IntPtr();
return;
case CoreTypeId.UIntPtr:
signature.UIntPtr();
return;
case CoreTypeId.Object:
signature.Object();
return;
case CoreTypeId.String:
signature.String();
return;
}
}

jkotas marked this conversation as resolved.
Show resolved Hide resolved
throw new NotSupportedException(SR.Format(SR.NotSupported_Signature, type.FullName));
jkotas marked this conversation as resolved.
Show resolved Hide resolved
}
}

internal enum CoreTypeId
{
Void,
Object,
Boolean,
Char,
SByte,
Byte,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Single,
Double,
String,
IntPtr,
UIntPtr,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ protected override MethodBuilder DefineMethodCore(string name, MethodAttributes
protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotImplementedException();
protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException();

protected override void SetParentCore([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type? parent)
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2074:DynamicallyAccessedMembers",
Justification = "No need to propogate the attribute to the called method")]
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
protected override void SetParentCore([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent)
{
if (parent != null)
{
Expand All @@ -86,9 +88,7 @@ protected override void SetParentCore([DynamicallyAccessedMembers((DynamicallyAc
{
if ((_attributes & TypeAttributes.Interface) != TypeAttributes.Interface)
{
#pragma warning disable IL2074 // Value stored in field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The return value of the source method does not have matching annotations.
_typeParent = _module.GetTypeFromCoreAssembly("System.Object");
#pragma warning restore IL2074
_typeParent = _module.GetTypeFromCoreAssembly(CoreTypeId.Object);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static IEnumerable<object[]> VariousInterfacesStructsTestData()
yield return new object[] { new Type[] { typeof(EmptyStruct) } };
yield return new object[] { new Type[] { typeof(StructWithField) } };
yield return new object[] { new Type[] { typeof(StructWithField), typeof(EmptyStruct) } };
yield return new object[] { new Type[] { typeof(IMultipleMethod), typeof(EmptyStruct) , typeof(INoMethod2), typeof(StructWithField) } };
yield return new object[] { new Type[] { typeof(IMultipleMethod), typeof(EmptyStruct), typeof(INoMethod2), typeof(StructWithField) } };
}

[Theory]
Expand Down