diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs index 667bd0d54a022d..c78d8da5a9a07f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs @@ -829,7 +829,7 @@ protected override void CreateGlobalFunctionsCore() if (_hasGlobalBeenCreated) { // cannot create globals twice - throw new InvalidOperationException(SR.InvalidOperation_NotADebugModule); + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); } _globalTypeBuilder.CreateType(); _hasGlobalBeenCreated = true; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs index 15ce2971bed747..6a8aaf6898b834 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs @@ -256,12 +256,12 @@ private RuntimeType InitializeFieldType() public override Type[] GetRequiredCustomModifiers() { - return GetSignature().GetCustomModifiers(1, true); + return GetSignature().GetCustomModifiers(0, true); } public override Type[] GetOptionalCustomModifiers() { - return GetSignature().GetCustomModifiers(1, false); + return GetSignature().GetCustomModifiers(0, false); } internal Signature GetSignature() => new Signature(this, m_declaringType); diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 84f91988b3ba99..7a27f2807a39e6 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -267,4 +267,25 @@ Label defined multiple times. + + PInvoke methods must be static and native and cannot be abstract. + + + PInvoke methods cannot exist on interfaces. + + + Method has been already defined. + + + Global members must be static. + + + Type definition of the global function has been completed. + + + Data size must be &gt; 1 and &lt; 0x3f0000. + + + MetadataToken for the member is not generated until the assembly saved. + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index 8f252ace10fd7a..834d8612a949e0 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -44,7 +44,7 @@ internal static AssemblyBuilderImpl DefinePersistedAssembly(AssemblyName name, A IEnumerable? assemblyAttributes) => new AssemblyBuilderImpl(name, coreAssembly, assemblyAttributes); - private void WritePEImage(Stream peStream, BlobBuilder ilBuilder) + private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fieldData) { var peHeaderBuilder = new PEHeaderBuilder( // For now only support DLL, DLL files are considered executable files @@ -55,6 +55,7 @@ private void WritePEImage(Stream peStream, BlobBuilder ilBuilder) header: peHeaderBuilder, metadataRootBuilder: new MetadataRootBuilder(_metadataBuilder), ilStream: ilBuilder, + mappedFieldData: fieldData, strongNameSignatureSize: 0); // Write executable into the specified stream. @@ -91,10 +92,11 @@ internal void Save(Stream stream) _module.WriteCustomAttributes(_customAttributes, assemblyHandle); var ilBuilder = new BlobBuilder(); + var fieldDataBuilder = new BlobBuilder(); MethodBodyStreamEncoder methodBodyEncoder = new MethodBodyStreamEncoder(ilBuilder); - _module.AppendMetadata(methodBodyEncoder); + _module.AppendMetadata(methodBodyEncoder, fieldDataBuilder); - WritePEImage(stream, ilBuilder); + WritePEImage(stream, ilBuilder, fieldDataBuilder); _previouslySaved = true; } @@ -137,5 +139,9 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan } public override string? FullName => _assemblyName.FullName; + + public override Module ManifestModule => _module ?? throw new InvalidOperationException(SR.InvalidOperation_AModuleRequired); + + public override AssemblyName GetName(bool copiedName) => (AssemblyName)_assemblyName.Clone(); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs index 9fca44c0a608a6..5ea3676039c88a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs @@ -10,10 +10,11 @@ internal sealed class ConstructorBuilderImpl : ConstructorBuilder internal readonly MethodBuilderImpl _methodBuilder; internal bool _isDefaultConstructor; - public ConstructorBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConvention, - Type[]? parameterTypes, ModuleBuilderImpl mod, TypeBuilderImpl type) + public ConstructorBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConvention, Type[]? parameterTypes, + Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers, ModuleBuilderImpl module, TypeBuilderImpl type) { - _methodBuilder = new MethodBuilderImpl(name, attributes, callingConvention, null, parameterTypes, mod, type); + _methodBuilder = new MethodBuilderImpl(name, attributes, callingConvention, returnType: null, returnTypeRequiredCustomModifiers: null, + returnTypeOptionalCustomModifiers: null, parameterTypes, requiredCustomModifiers, optionalCustomModifiers, module, type); type._methodDefinitions.Add(_methodBuilder); } @@ -60,7 +61,7 @@ public override CallingConventions CallingConvention } } - public override TypeBuilder DeclaringType => _methodBuilder.DeclaringType; + public override Type DeclaringType => _methodBuilder.DeclaringType!; public override Module Module => _methodBuilder.Module; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index dc507fcc679963..35b8e40784d0ec 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -15,6 +15,8 @@ internal sealed class FieldBuilderImpl : FieldBuilder private readonly TypeBuilderImpl _typeBuilder; private readonly string _fieldName; private readonly Type _fieldType; + private readonly Type[]? _requiredCustomModifiers; + private readonly Type[]? _optionalCustomModifiers; private FieldAttributes _attributes; internal MarshallingData? _marshallingData; @@ -22,14 +24,17 @@ internal sealed class FieldBuilderImpl : FieldBuilder internal List? _customAttributes; internal object? _defaultValue = DBNull.Value; internal FieldDefinitionHandle _handle; + internal byte[]? _rvaData; - internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) + internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers) { _fieldName = fieldName; _typeBuilder = typeBuilder; _fieldType = type; _attributes = attributes & ~FieldAttributes.ReservedMask; _offset = -1; + _requiredCustomModifiers = requiredCustomModifiers; + _optionalCustomModifiers = optionalCustomModifiers; } protected override void SetConstantCore(object? defaultValue) @@ -37,6 +42,7 @@ protected override void SetConstantCore(object? defaultValue) _typeBuilder.ThrowIfCreated(); ValidateDefaultValueType(defaultValue, _fieldType); _defaultValue = defaultValue; + _attributes |= FieldAttributes.HasDefault; } internal static void ValidateDefaultValueType(object? defaultValue, Type destinationType) @@ -100,6 +106,12 @@ internal static void ValidateDefaultValueType(object? defaultValue, Type destina } } + internal void SetData(byte[] data) + { + _rvaData = data; + _attributes |= FieldAttributes.HasFieldRVA; + } + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { // Handle pseudo custom attributes @@ -142,9 +154,9 @@ protected override void SetOffsetCore(int iOffset) public override string Name => _fieldName; - public override Type? DeclaringType => _typeBuilder; + public override Type? DeclaringType => _typeBuilder._isHiddenGlobalType ? null : _typeBuilder; - public override Type? ReflectedType => _typeBuilder; + public override Type? ReflectedType => DeclaringType; #endregion @@ -159,6 +171,10 @@ public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, public override FieldAttributes Attributes => _attributes; + public override Type[] GetRequiredCustomModifiers() => _requiredCustomModifiers ?? Type.EmptyTypes; + + public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers ?? Type.EmptyTypes; + #endregion #region ICustomAttributeProvider Implementation diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs index 7fa928d22b689a..fccd56de74e79a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs @@ -29,12 +29,12 @@ internal GenericTypeParameterBuilderImpl(string name, int genParamPosition, Type _type = typeBuilder; } - public GenericTypeParameterBuilderImpl(string name, int genParamPosition, MethodBuilderImpl methodBuilder) + public GenericTypeParameterBuilderImpl(string name, int genParamPosition, MethodBuilderImpl methodBuilder, TypeBuilderImpl typeBuilder) { _name = name; _genParamPosition = genParamPosition; _methodBuilder = methodBuilder; - _type = methodBuilder.DeclaringType; + _type = typeBuilder; } protected override void SetBaseTypeConstraintCore([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? baseTypeConstraint) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index 6a424d04aad5b9..2862b8774e2959 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -418,12 +418,12 @@ public override void Emit(OpCode opcode, ConstructorInfo con) EmitOpcode(opcode); UpdateStackSize(stackChange); - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(con), con); + WriteOrReserveToken(_moduleBuilder.GetConstructorHandle(con), con); } - private void WriteOrReserveToken(int token, object member) + private void WriteOrReserveToken(EntityHandle handle, object member) { - if (token == -1) + if (handle.IsNil) { // The member is a `***BuilderImpl` and its token is not yet defined. // Reserve the token bytes and write them later when its ready @@ -432,7 +432,7 @@ private void WriteOrReserveToken(int token, object member) } else { - _il.Token(token); + _il.Token(MetadataTokens.GetToken(handle)); } } @@ -553,7 +553,7 @@ public override void Emit(OpCode opcode, FieldInfo field) ArgumentNullException.ThrowIfNull(field); EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetFieldMetadataToken(field), field); + WriteOrReserveToken(_moduleBuilder.GetFieldHandle(field), field); } public override void Emit(OpCode opcode, MethodInfo meth) @@ -567,7 +567,7 @@ public override void Emit(OpCode opcode, MethodInfo meth) else { EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(meth), meth); + WriteOrReserveToken(_moduleBuilder.GetMethodHandle(meth), meth); } } @@ -576,7 +576,7 @@ public override void Emit(OpCode opcode, Type cls) ArgumentNullException.ThrowIfNull(cls); EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetTypeMetadataToken(cls), cls); + WriteOrReserveToken(_moduleBuilder.GetTypeHandle(cls), cls); } public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes) @@ -592,11 +592,11 @@ public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? opti UpdateStackSize(GetStackChange(opcode, methodInfo, optionalParameterTypes)); if (optionalParameterTypes == null || optionalParameterTypes.Length == 0) { - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(methodInfo), methodInfo); + WriteOrReserveToken(_moduleBuilder.GetMethodHandle(methodInfo), methodInfo); } else { - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(methodInfo, optionalParameterTypes), + WriteOrReserveToken(_moduleBuilder.GetMethodHandle(methodInfo, optionalParameterTypes), new KeyValuePair(methodInfo, optionalParameterTypes)); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 8ecc173cc109f5..a20e067e6b006a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; namespace System.Reflection.Emit { @@ -23,6 +24,10 @@ internal sealed class MethodBuilderImpl : MethodBuilder private GenericTypeParameterBuilderImpl[]? _typeParameters; private ILGeneratorImpl? _ilGenerator; private bool _initLocals; + internal Type[]? _returnTypeRequiredModifiers; + internal Type[]? _returnTypeOptionalCustomModifiers; + internal Type[][]? _parameterTypeRequiredCustomModifiers; + internal Type[][]? _parameterTypeOptionalCustomModifiers; internal bool _canBeRuntimeImpl; internal DllImportData? _dllImportData; @@ -30,8 +35,10 @@ internal sealed class MethodBuilderImpl : MethodBuilder internal ParameterBuilderImpl[]? _parameterBuilders; internal MethodDefinitionHandle _handle; - internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, - Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType) + internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, + Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, + Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, + ModuleBuilderImpl module, TypeBuilderImpl declaringType) { _module = module; _returnType = returnType ?? _module.GetTypeFromCoreAssembly(CoreTypeId.Void); @@ -50,9 +57,13 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv _callingConventions = callingConventions; _declaringType = declaringType; + _returnTypeRequiredModifiers = returnTypeRequiredCustomModifiers; + _returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers; if (parameterTypes != null) { + _parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers; + _parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers; _parameterTypes = new Type[parameterTypes.Length]; _parameterBuilders = new ParameterBuilderImpl[parameterTypes.Length + 1]; // parameter 0 reserved for return type for (int i = 0; i < parameterTypes.Length; i++) @@ -65,6 +76,11 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv _initLocals = true; } + internal void CreateDllImportData(string dllName, string entryName, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + _dllImportData = DllImportData.Create(dllName, entryName, nativeCallConv, nativeCharSet); + } + internal int ParameterCount => _parameterTypes == null ? 0 : _parameterTypes.Length; internal Type[]? ParameterTypes => _parameterTypes; @@ -85,32 +101,8 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv } internal BlobBuilder GetMethodSignatureBlob() => MetadataSignatureHelper.GetMethodSignature(_module, _parameterTypes, - _returnType, GetSignatureConvention(_callingConventions, _dllImportData), GetGenericArguments().Length, !IsStatic); - - internal static SignatureCallingConvention GetSignatureConvention(CallingConventions callingConvention, DllImportData? dllImportData = null) - { - SignatureCallingConvention convention = SignatureCallingConvention.Default; - - if ((callingConvention & CallingConventions.VarArgs) != 0) - { - convention = SignatureCallingConvention.VarArgs; - } - - if (dllImportData != null) - { - // Set native call signature - convention = dllImportData.Flags switch - { - MethodImportAttributes.CallingConventionCDecl => SignatureCallingConvention.CDecl, - MethodImportAttributes.CallingConventionStdCall => SignatureCallingConvention.StdCall, - MethodImportAttributes.CallingConventionThisCall => SignatureCallingConvention.ThisCall, - MethodImportAttributes.CallingConventionFastCall => SignatureCallingConvention.FastCall, - _ => SignatureCallingConvention.Unmanaged, - }; - } - - return convention; - } + _returnType, ModuleBuilderImpl.GetSignatureConvention(_callingConventions), GetGenericArguments().Length, !IsStatic, optionalParameterTypes: null, + _returnTypeRequiredModifiers, _returnTypeOptionalCustomModifiers, _parameterTypeRequiredCustomModifiers, _parameterTypeOptionalCustomModifiers); protected override bool InitLocalsCore { @@ -130,7 +122,7 @@ protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(par { string name = names[i]; ArgumentNullException.ThrowIfNull(names, nameof(names)); - typeParameters[i] = new GenericTypeParameterBuilderImpl(name, i, this); + typeParameters[i] = new GenericTypeParameterBuilderImpl(name, i, this, _declaringType); } return _typeParameters = typeParameters; @@ -190,7 +182,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan return; case "System.Runtime.InteropServices.DllImportAttribute": { - _dllImportData = DllImportData.CreateDllImportData(CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute), out var preserveSig); + _dllImportData = DllImportData.Create(CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute), out var preserveSig); _attributes |= MethodAttributes.PinvokeImpl; _canBeRuntimeImpl = true; if (preserveSig) @@ -228,6 +220,8 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq if (returnType != null) { _returnType = returnType; + _returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers; + _returnTypeRequiredModifiers = returnTypeRequiredCustomModifiers; } if (parameterTypes != null) @@ -238,14 +232,16 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq { ArgumentNullException.ThrowIfNull(_parameterTypes[i] = parameterTypes[i], nameof(parameterTypes)); } + + _parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers; + _parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers; } - // TODO: Add support for other parameters: returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers, parameterTypeRequiredCustomModifiers and parameterTypeOptionalCustomModifiers } public override string Name => _name; public override MethodAttributes Attributes => _attributes; public override CallingConventions CallingConvention => _callingConventions; - public override TypeBuilder DeclaringType => _declaringType; + public override Type? DeclaringType => _declaringType._isHiddenGlobalType ? null : _declaringType; public override Module Module => _module; public override bool ContainsGenericParameters => throw new NotSupportedException(); public override bool IsGenericMethod => _typeParameters != null; @@ -255,7 +251,7 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq public override bool IsSecurityTransparent => false; public override int MetadataToken => _handle == default ? 0 : MetadataTokens.GetToken(_handle); public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(SR.NotSupported_DynamicModule); - public override Type? ReflectedType => _declaringType; + public override Type? ReflectedType => DeclaringType; public override ParameterInfo ReturnParameter { get => throw new NotImplementedException(); } public override Type ReturnType => _returnType; public override ICustomAttributeProvider ReturnTypeCustomAttributes { get => throw new NotImplementedException(); } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 2232bd17c61c34..cf0725c9673e04 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; namespace System.Reflection.Emit @@ -16,6 +17,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly string _name; private readonly MetadataBuilder _metadataBuilder; private readonly AssemblyBuilderImpl _assemblyBuilder; + private readonly TypeBuilderImpl _globalTypeBuilder; private readonly Dictionary _assemblyReferences = new(); private readonly Dictionary _typeReferences = new(); private readonly Dictionary _memberReferences = new(); @@ -30,6 +32,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private int _nextPropertyRowId = 1; private int _nextEventRowId = 1; private bool _coreTypesFullyPopulated; + private bool _hasGlobalBeenCreated; private Type?[]? _coreTypes; 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), typeof(TypedReference) }; @@ -41,6 +44,7 @@ internal ModuleBuilderImpl(string name, Assembly coreAssembly, MetadataBuilder b _metadataBuilder = builder; _assemblyBuilder = assemblyBuilder; _moduleVersionId = Guid.NewGuid(); + _globalTypeBuilder = new TypeBuilderImpl(this); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Types are preserved via s_coreTypes")] @@ -104,7 +108,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) + internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder) { // Add module metadata ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule( @@ -124,11 +128,14 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) methodList: MetadataTokens.MethodDefinitionHandle(1)); WriteCustomAttributes(_customAttributes, moduleHandle); - - _typeDefinitions.Sort((x, y) => x.MetadataToken.CompareTo(y.MetadataToken)); - // All generic parameters for all types and methods should be written in specific order List genericParams = new(); + PopulateTokensForTypesAndItsMembers(); + + // Add global members + WriteFields(_globalTypeBuilder, fieldDataBuilder); + WriteMethods(_globalTypeBuilder._methodDefinitions, genericParams, methodBodyEncoder); + // Add each type definition to metadata table. foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) { @@ -165,7 +172,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) WriteInterfaceImplementations(typeBuilder, typeHandle); WriteCustomAttributes(typeBuilder._customAttributes, typeHandle); WriteProperties(typeBuilder); - WriteFields(typeBuilder); + WriteFields(typeBuilder, fieldDataBuilder); WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder); WriteEvents(typeBuilder); } @@ -316,17 +323,20 @@ private void PopulateEventDefinitionHandles(List eventDefiniti } } - internal void PopulateTypeAndItsMembersTokens(TypeBuilderImpl typeBuilder) + private void PopulateTokensForTypesAndItsMembers() { - typeBuilder._handle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); - typeBuilder._firstMethodToken = _nextMethodDefRowId; - typeBuilder._firstFieldToken = _nextFieldDefRowId; - typeBuilder._firstPropertyToken = _nextPropertyRowId; - typeBuilder._firstEventToken = _nextEventRowId; - PopulateMethodDefinitionHandles(typeBuilder._methodDefinitions); - PopulateFieldDefinitionHandles(typeBuilder._fieldDefinitions); - PopulatePropertyDefinitionHandles(typeBuilder._propertyDefinitions); - PopulateEventDefinitionHandles(typeBuilder._eventDefinitions); + foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) + { + typeBuilder._handle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); + typeBuilder._firstMethodToken = _nextMethodDefRowId; + typeBuilder._firstFieldToken = _nextFieldDefRowId; + typeBuilder._firstPropertyToken = _nextPropertyRowId; + typeBuilder._firstEventToken = _nextEventRowId; + PopulateMethodDefinitionHandles(typeBuilder._methodDefinitions); + PopulateFieldDefinitionHandles(typeBuilder._fieldDefinitions); + PopulatePropertyDefinitionHandles(typeBuilder._propertyDefinitions); + PopulateEventDefinitionHandles(typeBuilder._eventDefinitions); + } } private void WriteMethods(List methods, List genericParams, MethodBodyStreamEncoder methodBodyEncoder) @@ -413,11 +423,12 @@ private static int AddMethodBody(MethodBuilderImpl method, ILGeneratorImpl il, S attributes: method.InitLocals ? MethodBodyAttributes.InitLocals : MethodBodyAttributes.None, hasDynamicStackAllocation: il.HasDynamicStackAllocation); - private void WriteFields(TypeBuilderImpl typeBuilder) + private void WriteFields(TypeBuilderImpl typeBuilder, BlobBuilder fieldDataBuilder) { foreach (FieldBuilderImpl field in typeBuilder._fieldDefinitions) { - FieldDefinitionHandle handle = AddFieldDefinition(field, MetadataSignatureHelper.GetFieldSignature(field.FieldType, this)); + FieldDefinitionHandle handle = AddFieldDefinition(field, + MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); Debug.Assert(field._handle == handle); WriteCustomAttributes(field._customAttributes, handle); @@ -426,15 +437,22 @@ private void WriteFields(TypeBuilderImpl typeBuilder) AddFieldLayout(handle, field._offset); } - if (field._marshallingData != null) + if (field.Attributes.HasFlag(FieldAttributes.HasFieldMarshal) && field._marshallingData != null) { AddMarshalling(handle, field._marshallingData.SerializeMarshallingData()); } - if (field._defaultValue != DBNull.Value) + if (field.Attributes.HasFlag(FieldAttributes.HasDefault) && field._defaultValue != DBNull.Value) { AddDefaultValue(handle, field._defaultValue); } + + if (field.Attributes.HasFlag(FieldAttributes.HasFieldRVA) && field._rvaData != null) + { + _metadataBuilder.AddFieldRelativeVirtualAddress(handle, fieldDataBuilder.Count); + fieldDataBuilder.WriteBytes(field._rvaData!); + fieldDataBuilder.Align(ManagedPEBuilder.MappedFieldDataAlignment); + } } } @@ -501,17 +519,19 @@ private MethodSpecificationHandle AddMethodSpecification(EntityHandle methodHand method: methodHandle, instantiation: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetMethodSpecificationSignature(genericArgs, this))); - private EntityHandle GetMemberReferenceHandle(MemberInfo member) + private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) { - if (!_memberReferences.TryGetValue(member, out var memberHandle)) + if (!_memberReferences.TryGetValue(memberInfo, out var memberHandle)) { + MemberInfo member = GetOriginalMemberIfConstructedType(memberInfo); switch (member) { case FieldInfo field: - memberHandle = AddMemberReference(field.Name, GetTypeHandle(field.DeclaringType!), MetadataSignatureHelper.GetFieldSignature(field.FieldType, this)); + memberHandle = AddMemberReference(field.Name, GetTypeHandle(memberInfo.DeclaringType!), + MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); break; case ConstructorInfo ctor: - memberHandle = AddMemberReference(ctor.Name, GetTypeHandle(ctor.DeclaringType!), GetMemberSignature(ctor)); + memberHandle = AddMemberReference(ctor.Name, GetTypeHandle(memberInfo.DeclaringType!), MetadataSignatureHelper.GetConstructorSignature(ctor.GetParameters(), this)); break; case MethodInfo method: if (method.IsConstructedGenericMethod) @@ -520,7 +540,7 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo member) } else { - memberHandle = AddMemberReference(member.Name, GetTypeHandle(member.DeclaringType!), GetMemberSignature(member)); + memberHandle = AddMemberReference(method.Name, GetTypeHandle(memberInfo.DeclaringType!), GetMethodSignature(method, null)); } break; default: @@ -528,7 +548,7 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo member) } - _memberReferences.Add(member, memberHandle); + _memberReferences.Add(memberInfo, memberHandle); } return memberHandle; @@ -551,39 +571,29 @@ private EntityHandle GetMethodReference(MethodInfo methodInfo, Type[] optionalPa private BlobBuilder GetMethodSignature(MethodInfo method, Type[]? optionalParameterTypes) => MetadataSignatureHelper.GetMethodSignature(this, ParameterTypes(method.GetParameters()), method.ReturnType, - MethodBuilderImpl.GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic, optionalParameterTypes); + GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic, optionalParameterTypes); - private static MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) + internal static SignatureCallingConvention GetSignatureConvention(CallingConventions callingConvention) { - Type declaringType = memberInfo.DeclaringType!; - if (declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) + SignatureCallingConvention convention = SignatureCallingConvention.Default; + + if ((callingConvention & CallingConventions.VarArgs) != 0) { - return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo); + convention = SignatureCallingConvention.VarArgs; } - return memberInfo; + return convention; } - private BlobBuilder GetMemberSignature(MemberInfo memberInfo) + private static MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) { - MemberInfo member = GetOriginalMemberIfConstructedType(memberInfo); - - if (member is MethodInfo method) - { - return GetMethodSignature(method, optionalParameterTypes: null); - } - - if (member is FieldInfo field) - { - return MetadataSignatureHelper.GetFieldSignature(field.FieldType, this); - } - - if (member is ConstructorInfo ctor) + Type declaringType = memberInfo.DeclaringType!; + if (declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) { - return MetadataSignatureHelper.GetConstructorSignature(ctor.GetParameters(), this); + return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo); } - throw new NotSupportedException(); + return memberInfo; } private static Type[] ParameterTypes(ParameterInfo[] parameterInfos) @@ -608,7 +618,19 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly) if (!_assemblyReferences.TryGetValue(assembly, out var handle)) { AssemblyName aName = assembly.GetName(); - handle = AddAssemblyReference(aName.Name, aName.Version, aName.CultureName, aName.GetPublicKeyToken(), aName.Flags, aName.ContentType); + // For ref assembly flags shall have only one bit set, the PublicKey bit. + // All other bits shall be zero. See ECMA-335 spec II.22.5 + AssemblyFlags assemblyFlags = 0; + byte[]? publicKeyOrToken = aName.GetPublicKey(); + if (publicKeyOrToken != null && publicKeyOrToken.Length > 0) + { + assemblyFlags = AssemblyFlags.PublicKey; + } + else + { + publicKeyOrToken = aName.GetPublicKeyToken(); + } + handle = AddAssemblyReference(aName.Name, aName.Version, aName.CultureName, publicKeyOrToken, assemblyFlags); _assemblyReferences.Add(assembly, handle); } @@ -721,13 +743,13 @@ private ParameterHandle AddParameter(ParameterBuilderImpl parameter) => sequenceNumber: parameter.Position); private AssemblyReferenceHandle AddAssemblyReference(string? name, Version? version, string? culture, - byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) => + byte[]? publicKeyToken, AssemblyFlags assemblyFlags) => _metadataBuilder.AddAssemblyReference( name: name == null ? default : _metadataBuilder.GetOrAddString(name), version: version ?? new Version(0, 0, 0, 0), culture: (culture == null) ? default : _metadataBuilder.GetOrAddString(value: culture), - publicKeyOrToken: (publicKeyToken == null) ? default : _metadataBuilder.GetOrAddBlob(publicKeyToken), // reference has token, not full public key - flags: (AssemblyFlags)((int)contentType << 9) | ((flags & AssemblyNameFlags.Retargetable) != 0 ? AssemblyFlags.Retargetable : 0), + publicKeyOrToken: (publicKeyToken == null) ? default : _metadataBuilder.GetOrAddBlob(publicKeyToken), + flags: assemblyFlags, hashValue: default); // .file directive assemblies not supported, no need to handle this value. internal EntityHandle GetTypeHandle(Type type) @@ -800,58 +822,69 @@ internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [Dynamic public override Guid ModuleVersionId => _moduleVersionId; public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); - public override int GetFieldMetadataToken(FieldInfo field) + public override int GetFieldMetadataToken(FieldInfo field) => GetTokenForHandle(GetFieldHandle(field)); + + internal EntityHandle GetFieldHandle(FieldInfo field) { if (field is FieldBuilderImpl fb) { - return fb._handle != default ? MetadataTokens.GetToken(fb._handle) : -1; + return fb._handle; } - if (IsConstuctedFromNotBakedTypeBuilder(field.DeclaringType!)) + return GetHandleForMember(field); + } + + private static int GetTokenForHandle(EntityHandle handle) + { + if (handle.IsNil) { - return -1; + throw new InvalidOperationException(SR.InvalidOperation_TokenNotPopulated); } - return MetadataTokens.GetToken(GetMemberReferenceHandle(field)); + return MetadataTokens.GetToken(handle); } - private static bool IsConstuctedFromNotBakedTypeBuilder(Type type) => type.IsConstructedGenericType && - type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default; - - public override int GetMethodMetadataToken(ConstructorInfo constructor) + private EntityHandle GetHandleForMember(MemberInfo member) { - if (constructor is ConstructorBuilderImpl cb) + if (IsConstructedFromNotBakedTypeBuilder(member.DeclaringType!)) { - return cb._methodBuilder._handle != default ? MetadataTokens.GetToken(cb._methodBuilder._handle) : -1; + return default; } - if (IsConstuctedFromNotBakedTypeBuilder(constructor.DeclaringType!)) + return GetMemberReferenceHandle(member); + } + + private static bool IsConstructedFromNotBakedTypeBuilder(Type type) => type.IsConstructedGenericType && + type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default; + + public override int GetMethodMetadataToken(ConstructorInfo constructor) => GetTokenForHandle(GetConstructorHandle(constructor)); + + internal EntityHandle GetConstructorHandle(ConstructorInfo constructor) + { + if (constructor is ConstructorBuilderImpl cb) { - return -1; + return cb._methodBuilder._handle; } - return MetadataTokens.GetToken(GetMemberReferenceHandle(constructor)); + return GetHandleForMember(constructor); } - public override int GetMethodMetadataToken(MethodInfo method) + public override int GetMethodMetadataToken(MethodInfo method) => GetTokenForHandle(GetMethodHandle(method)); + + internal EntityHandle GetMethodHandle(MethodInfo method) { if (method is MethodBuilderImpl mb) { - return mb._handle != default ? MetadataTokens.GetToken(mb._handle) : -1; - } - - if (IsConstructedMethodFromNotBakedMethodBuilder(method)) - { - return -1; + return mb._handle; } - return MetadataTokens.GetToken(GetMemberReferenceHandle(method)); + return GetHandleForMember(method); } private static bool IsConstructedMethodFromNotBakedMethodBuilder(MethodInfo method) => method.IsConstructedGenericMethod && method.GetGenericMethodDefinition() is MethodBuilderImpl mb2 && mb2._handle == default; - internal int GetMethodMetadataToken(MethodInfo method, Type[] optionalParameterTypes) + internal EntityHandle GetMethodHandle(MethodInfo method, Type[] optionalParameterTypes) { if ((method.CallingConvention & CallingConventions.VarArgs) == 0) { @@ -859,43 +892,104 @@ internal int GetMethodMetadataToken(MethodInfo method, Type[] optionalParameterT throw new InvalidOperationException(SR.InvalidOperation_NotAVarArgCallingConvention); } - if (method is MethodBuilderImpl mb && mb._handle == default || IsConstructedMethodFromNotBakedMethodBuilder(method)) + if (method is MethodBuilderImpl mb) + { + return mb._handle; + } + + if (IsConstructedMethodFromNotBakedMethodBuilder(method)) { - return -1; + return default; } - return MetadataTokens.GetToken(GetMethodReference(method, optionalParameterTypes)); + return GetMethodReference(method, optionalParameterTypes); } - public override int GetStringMetadataToken(string stringConstant) => MetadataTokens.GetToken(_metadataBuilder.GetOrAddUserString(stringConstant)); - - public override int GetTypeMetadataToken(Type type) + internal TypeBuilderImpl? FindTypeBuilderWithName(string strTypeName, bool ignoreCase) { - if (type is TypeBuilderImpl tb && Equals(tb.Module)) + StringComparison casing = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + + foreach (TypeBuilderImpl type in _typeDefinitions) { - return tb._handle != default ? MetadataTokens.GetToken(tb._handle) : -1; + if (string.Equals(type.Name, strTypeName, casing)) + { + return type; + } } - if (type is EnumBuilderImpl eb && Equals(eb.Module)) + return null; + } + + public override int GetStringMetadataToken(string stringConstant) => MetadataTokens.GetToken(_metadataBuilder.GetOrAddUserString(stringConstant)); + + public override int GetTypeMetadataToken(Type type) => GetTokenForHandle(GetTypeHandle(type)); + + protected override void CreateGlobalFunctionsCore() + { + if (_hasGlobalBeenCreated) { - return eb._typeBuilder._handle != default ? MetadataTokens.GetToken(eb._typeBuilder._handle) : -1; + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); } - return MetadataTokens.GetToken(GetTypeReferenceOrSpecificationHandle(type)); + _globalTypeBuilder.CreateTypeInfo(); + _hasGlobalBeenCreated = true; } - protected override void CreateGlobalFunctionsCore() => throw new NotImplementedException(); - protected override EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType) { EnumBuilderImpl enumBuilder = new EnumBuilderImpl(name, underlyingType, visibility, this); _typeDefinitions.Add(enumBuilder._typeBuilder); return enumBuilder; } - protected override MethodBuilder DefineGlobalMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers, Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers) => throw new NotImplementedException(); - protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) => throw new NotImplementedException(); + + protected override MethodBuilder DefineGlobalMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, + Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers, Type[]? parameterTypes, + Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers) + { + if (_hasGlobalBeenCreated) + { + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); + } + + if ((attributes & MethodAttributes.Static) == 0) + { + throw new ArgumentException(SR.Argument_GlobalMembersMustBeStatic); + } + + MethodBuilderImpl method = (MethodBuilderImpl)_globalTypeBuilder.DefineMethod(name, attributes, callingConvention, + returnType, requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers, + parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers); + method._handle = MetadataTokens.MethodDefinitionHandle(_nextMethodDefRowId++); + return method; + } + + protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) + { + if (_hasGlobalBeenCreated) + { + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); + } + + FieldBuilderImpl field = (FieldBuilderImpl)_globalTypeBuilder.DefineInitializedData(name, data, attributes); + field._handle = MetadataTokens.FieldDefinitionHandle(_nextFieldDefRowId++); + return field; + } + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] - protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); + protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, + CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + // Global methods must be static. + if ((attributes & MethodAttributes.Static) == 0) + { + throw new ArgumentException(SR.Argument_GlobalMembersMustBeStatic); + } + + MethodBuilderImpl method = (MethodBuilderImpl)_globalTypeBuilder.DefinePInvokeMethod( + name, dllName, entryName, attributes, callingConvention, returnType, parameterTypes, nativeCallConv, nativeCharSet); + method._handle = MetadataTokens.MethodDefinitionHandle(_nextMethodDefRowId++); + return method; + } protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) @@ -905,7 +999,18 @@ protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, return _type; } - protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); + protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) + { + if (_hasGlobalBeenCreated) + { + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); + } + + FieldBuilderImpl field = (FieldBuilderImpl)_globalTypeBuilder.DefineUninitializedData(name, size, attributes); + field._handle = MetadataTokens.FieldDefinitionHandle(_nextFieldDefRowId++); + return field; + } + protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { @@ -913,13 +1018,31 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } + [RequiresUnreferencedCode("Methods might be removed")] + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, + CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) + { + if (types == null) + { + return _globalTypeBuilder.GetMethod(name, bindingAttr); + } + + return _globalTypeBuilder.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); + } + + [RequiresUnreferencedCode("Methods might be removed")] + public override MethodInfo[] GetMethods(BindingFlags bindingFlags) + { + return _globalTypeBuilder.GetMethods(bindingFlags); + } + public override int GetSignatureMetadataToken(SignatureHelper signature) => MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob(signature.GetSignature()))); - internal int GetSignatureToken(CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => + internal int GetSignatureToken(CallingConventions callingConventions, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature( _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetMethodSignature(this, parameterTypes, returnType, - MethodBuilderImpl.GetSignatureConvention(callingConvention), optionalParameterTypes: optionalParameterTypes)))); + GetSignatureConvention(callingConventions), optionalParameterTypes: optionalParameterTypes)))); internal int GetSignatureToken(CallingConvention callingConvention, Type? returnType, Type[]? parameterTypes) => MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob( diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs index 54c5a80079d5ee..97de7d45d44911 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs @@ -18,12 +18,15 @@ internal sealed class PropertyBuilderImpl : PropertyBuilder private MethodInfo? _getMethod; private MethodInfo? _setMethod; internal HashSet? _otherMethods; - + internal readonly Type[]? _returnTypeRequiredCustomModifiers; + internal readonly Type[]? _returnTypeOptionalCustomModifiers; + internal readonly Type[][]? _parameterTypeRequiredCustomModifiers; + internal readonly Type[][]? _parameterTypeOptionalCustomModifiers; internal PropertyDefinitionHandle _handle; internal List? _customAttributes; internal object? _defaultValue = DBNull.Value; - internal PropertyBuilderImpl(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? parameterTypes, TypeBuilderImpl containingType) + internal PropertyBuilderImpl(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, TypeBuilderImpl containingType) { ArgumentException.ThrowIfNullOrEmpty(name); @@ -33,6 +36,10 @@ internal PropertyBuilderImpl(string name, PropertyAttributes attributes, Calling _propertyType = returnType; _parameterTypes = parameterTypes; _containingType = containingType; + _returnTypeRequiredCustomModifiers = returnTypeRequiredCustomModifiers; + _returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers; + _parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers; + _parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers; } internal Type[]? ParameterTypes => _parameterTypes; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs index bd95b6aee4c82e..e6f3b480f89523 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs @@ -26,7 +26,7 @@ internal DllImportData(string moduleName, string? entryPoint, MethodImportAttrib public MethodImportAttributes Flags => _flags; - internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig) + internal static DllImportData Create(CustomAttributeInfo attr, out bool preserveSig) { string? moduleName = (string?)attr._ctorArgs[0]; if (string.IsNullOrEmpty(moduleName)) @@ -47,23 +47,10 @@ internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out preserveSig = (bool)value; break; case "CallingConvention": - importAttributes |= (CallingConvention)value switch - { - CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, - CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, - CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, - CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, - _ => MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this - }; + importAttributes |= MatchNativeCallingConvention((CallingConvention)value); break; case "CharSet": - importAttributes |= (CharSet)value switch - { - CharSet.Ansi => MethodImportAttributes.CharSetAnsi, - CharSet.Auto => MethodImportAttributes.CharSetAuto, - CharSet.Unicode => MethodImportAttributes.CharSetUnicode, - _ => MethodImportAttributes.CharSetAuto - }; + importAttributes |= MatchNativeCharSet((CharSet)value); break; case "EntryPoint": entryPoint = (string?)value; @@ -105,6 +92,38 @@ internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out return new DllImportData(moduleName, entryPoint, importAttributes); } + + internal static DllImportData Create(string moduleName, string entryName, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + if (string.IsNullOrEmpty(moduleName)) + { + throw new ArgumentException(SR.Argument_DllNameCannotBeEmpty); + } + + MethodImportAttributes importAttributes = MatchNativeCallingConvention(nativeCallConv); + importAttributes |= MatchNativeCharSet(nativeCharSet); + + return new DllImportData(moduleName, entryName, importAttributes); + } + + private static MethodImportAttributes MatchNativeCharSet(CharSet nativeCharSet) => + nativeCharSet switch + { + CharSet.Ansi => MethodImportAttributes.CharSetAnsi, + CharSet.Auto => MethodImportAttributes.CharSetAuto, + CharSet.Unicode => MethodImportAttributes.CharSetUnicode, + _ => MethodImportAttributes.CharSetAuto + }; + + private static MethodImportAttributes MatchNativeCallingConvention(CallingConvention nativeCallConv) => + nativeCallConv switch + { + CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, + CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, + CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, + CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, + _ => MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this + }; } internal sealed class MarshallingData diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs index 2fd790cd324df8..c0866d997cf9ab 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs @@ -24,10 +24,12 @@ internal static BlobBuilder GetLocalSignature(List locals, ModuleB return localSignature; } - internal static BlobBuilder GetFieldSignature(Type fieldType, ModuleBuilderImpl module) + internal static BlobBuilder GetFieldSignature(Type fieldType, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, ModuleBuilderImpl module) { BlobBuilder fieldSignature = new(); - WriteSignatureForType(new BlobEncoder(fieldSignature).Field().Type(), fieldType, module); + FieldTypeEncoder encoder = new BlobEncoder(fieldSignature).Field(); + WriteReturnTypeCustomModifiers(encoder.CustomModifiers(), requiredCustomModifiers, optionalCustomModifiers, module); + WriteSignatureForType(encoder.Type(), fieldType, module); return fieldSignature; } @@ -70,8 +72,9 @@ internal static BlobBuilder GetMethodSpecificationSignature(Type[] genericArgume return methodSpecSignature; } - internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, - SignatureCallingConvention convention, int genParamCount = 0, bool isInstance = false, Type[]? optionalParameterTypes = null) + internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, SignatureCallingConvention convention, + int genParamCount = 0, bool isInstance = false, Type[]? optionalParameterTypes = null, Type[]? returnTypeRequiredModifiers = null, + Type[]? returnTypeOptionalModifiers = null, Type[][]? parameterRequiredModifiers = null, Type[][]? parameterOptionalModifiers = null) { BlobBuilder methodSignature = new(); @@ -80,6 +83,8 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? new BlobEncoder(methodSignature).MethodSignature(convention, genParamCount, isInstance). Parameters(paramsLength, out ReturnTypeEncoder retEncoder, out ParametersEncoder parEncoder); + WriteReturnTypeCustomModifiers(retEncoder.CustomModifiers(), returnTypeRequiredModifiers, returnTypeOptionalModifiers, module); + if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void)) { WriteSignatureForType(retEncoder.Type(), returnType, module); @@ -89,7 +94,7 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? retEncoder.Void(); } - WriteParametersSignature(module, parameters, parEncoder); + WriteParametersSignature(module, parameters, parEncoder, parameterRequiredModifiers, parameterOptionalModifiers); if (optionalParameterTypes != null && optionalParameterTypes.Length != 0) { @@ -99,13 +104,48 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? return methodSignature; } - private static void WriteParametersSignature(ModuleBuilderImpl module, Type[]? parameters, ParametersEncoder parameterEncoder) + private static void WriteReturnTypeCustomModifiers(CustomModifiersEncoder encoder, + Type[]? requiredModifiers, Type[]? optionalModifiers, ModuleBuilderImpl module) + { + if (requiredModifiers != null) + { + WriteCustomModifiers(encoder, requiredModifiers, isOptional: false, module); + } + + if (optionalModifiers != null) + { + WriteCustomModifiers(encoder, optionalModifiers, isOptional: true, module); + } + } + + private static void WriteCustomModifiers(CustomModifiersEncoder encoder, Type[] customModifiers, bool isOptional, ModuleBuilderImpl module) + { + foreach (Type modifier in customModifiers) + { + encoder.AddModifier(module.GetTypeHandle(modifier), isOptional); + } + } + + private static void WriteParametersSignature(ModuleBuilderImpl module, Type[]? parameters, + ParametersEncoder parameterEncoder, Type[][]? requiredModifiers = null, Type[][]? optionalModifiers = null) { if (parameters != null) // If parameters null, just keep the ParametersEncoder empty { - foreach (Type parameter in parameters) + for (int i = 0; i < parameters.Length; i++) { - WriteSignatureForType(parameterEncoder.AddParameter().Type(), parameter, module); + ParameterTypeEncoder encoder = parameterEncoder.AddParameter(); + + if (requiredModifiers != null && requiredModifiers.Length > i && requiredModifiers[i] != null) + { + WriteCustomModifiers(encoder.CustomModifiers(), requiredModifiers[i], isOptional: false, module); + } + + if (optionalModifiers != null && optionalModifiers.Length > i && optionalModifiers[i] != null) + { + WriteCustomModifiers(encoder.CustomModifiers(), optionalModifiers[i], isOptional: true, module); + } + + WriteSignatureForType(encoder.Type(), parameters[i], module); } } } @@ -118,8 +158,9 @@ internal static BlobBuilder GetPropertySignature(PropertyBuilderImpl property, M PropertySignature(isInstanceProperty: property.CallingConventions.HasFlag(CallingConventions.HasThis)). Parameters(property.ParameterTypes == null ? 0 : property.ParameterTypes.Length, out ReturnTypeEncoder retType, out ParametersEncoder paramEncoder); + WriteReturnTypeCustomModifiers(retType.CustomModifiers(), property._returnTypeRequiredCustomModifiers, property._returnTypeOptionalCustomModifiers, module); WriteSignatureForType(retType.Type(), property.PropertyType, module); - WriteParametersSignature(module, property.ParameterTypes, paramEncoder); + WriteParametersSignature(module, property.ParameterTypes, paramEncoder, property._parameterTypeRequiredCustomModifiers, property._parameterTypeOptionalCustomModifiers); return propertySignature; } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 1cf4d6009408e3..176a04a217dec6 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -26,6 +26,7 @@ internal sealed class TypeBuilderImpl : TypeBuilder private Type? _enumUnderlyingType; private bool _isCreated; + internal bool _isHiddenGlobalType; internal TypeDefinitionHandle _handle; internal int _firstFieldToken; internal int _firstMethodToken; @@ -40,6 +41,15 @@ internal sealed class TypeBuilderImpl : TypeBuilder internal List? _customAttributes; internal Dictionary>? _methodOverrides; + // Only for creating the global type + internal TypeBuilderImpl(ModuleBuilderImpl module) + { + _name = ""; // Defined in ECMA-335 II.10.8 + _module = module; + _isHiddenGlobalType = true; + _handle = MetadataTokens.TypeDefinitionHandle(1); + } + internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module, Type[]? interfaces, PackingSize packingSize, int typeSize, TypeBuilderImpl? enclosingType) @@ -94,6 +104,12 @@ protected override TypeInfo CreateTypeInfoCore() return this; } + if (_isHiddenGlobalType) + { + _isCreated = true; + return null!; + } + // Create a public default constructor if this class has no constructor. Except the type is Interface, ValueType, Enum, or a static class. // (TypeAttributes.Abstract | TypeAttributes.Sealed) determines if the type is static if (_constructorDefinitions.Count == 0 && (_attributes & TypeAttributes.Interface) == 0 && !IsValueType && @@ -102,7 +118,6 @@ protected override TypeInfo CreateTypeInfoCore() DefineDefaultConstructor(MethodAttributes.Public); } - _module.PopulateTypeAndItsMembersTokens(this); ValidateMethods(); _isCreated = true; @@ -182,11 +197,16 @@ private void CheckInterfaces(Type[] _interfaces) foreach (Type interfaceType in _interfaces) { #pragma warning disable IL2075 // Analyzer produces a different warning code than illink. The IL2065 suppression takes care of illink: https://github.com/dotnet/runtime/issues/96646 - MethodInfo[] interfaceMethods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo[] interfaceMethods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); #pragma warning restore IL2075 for (int i = 0; i < interfaceMethods.Length; i++) { MethodInfo interfaceMethod = interfaceMethods[i]; + if (!interfaceMethod.IsAbstract) + { + continue; + } + MethodInfo? implementedMethod = GetMethodImpl(interfaceMethod.Name, GetBindingFlags(interfaceMethod), null, interfaceMethod.CallingConvention, GetParameterTypes(interfaceMethod.GetParameters()), null); if ((implementedMethod == null || implementedMethod.IsAbstract) && !FoundInInterfaceMapping(interfaceMethod)) @@ -231,7 +251,8 @@ internal void ThrowIfCreated() } } - protected override ConstructorBuilder DefineConstructorCore(MethodAttributes attributes, CallingConventions callingConvention, Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers) + protected override ConstructorBuilder DefineConstructorCore(MethodAttributes attributes, CallingConventions callingConvention, + Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers) { if ((_attributes & TypeAttributes.Interface) == TypeAttributes.Interface && (attributes & MethodAttributes.Static) != MethodAttributes.Static) { @@ -251,7 +272,8 @@ protected override ConstructorBuilder DefineConstructorCore(MethodAttributes att } attributes |= MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; - ConstructorBuilderImpl constBuilder = new ConstructorBuilderImpl(name, attributes, callingConvention, parameterTypes, _module, this); + ConstructorBuilderImpl constBuilder = new ConstructorBuilderImpl(name, attributes, callingConvention, + parameterTypes, requiredCustomModifiers, optionalCustomModifiers, _module, this); _constructorDefinitions.Add(constBuilder); return constBuilder; } @@ -322,7 +344,7 @@ protected override FieldBuilder DefineFieldCore(string fieldName, Type type, Typ } } - var field = new FieldBuilderImpl(this, fieldName, type, attributes); + var field = new FieldBuilderImpl(this, fieldName, type, attributes, requiredCustomModifiers, optionalCustomModifiers); _fieldDefinitions.Add(field); return field; } @@ -345,13 +367,28 @@ protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(par return _typeParameters = typeParameters; } - protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) => throw new NotImplementedException(); + protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) + { + if (data.Length <= 0 || data.Length >= 0x003f0000) + { + throw new ArgumentException(SR.Argument_BadSizeForData, nameof(data.Length)); + } + + // This method will define an initialized Data in .sdata. + // We will create a fake TypeDef to represent the data with size. This TypeDef + // will be the signature for the Field. + return DefineDataHelper(name, data, data.Length, attributes); + } - protected override MethodBuilder DefineMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) + protected override MethodBuilder DefineMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, + Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, + Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) { ThrowIfCreated(); - MethodBuilderImpl methodBuilder = new(name, attributes, callingConvention, returnType, parameterTypes, _module, this); + + MethodBuilderImpl methodBuilder = new(name, attributes, callingConvention, returnType, returnTypeRequiredCustomModifiers, + returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers, _module, this); _methodDefinitions.Add(methodBuilder); return methodBuilder; } @@ -466,19 +503,98 @@ protected override TypeBuilder DefineNestedTypeCore(string name, TypeAttributes } [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] - protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); + protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, + CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, + Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + if ((attributes & MethodAttributes.Abstract) != 0) + { + throw new ArgumentException(SR.Argument_BadPInvokeMethod); + } + + if ((_attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) + { + throw new ArgumentException(SR.Argument_BadPInvokeOnInterface); + } + + ThrowIfCreated(); + + attributes |= MethodAttributes.PinvokeImpl; + MethodBuilderImpl method = new MethodBuilderImpl(name, attributes, callingConvention, returnType, returnTypeRequiredCustomModifiers, + returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers, _module, this); + method.CreateDllImportData(dllName, entryName, nativeCallConv, nativeCharSet); + + if (_methodDefinitions.Contains(method)) + { + throw new ArgumentException(SR.Argument_MethodRedefined); + } + _methodDefinitions.Add(method); - protected override PropertyBuilder DefinePropertyCore(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? returnTypeRequiredCustomModifiers, - Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) + return method; + } + + protected override PropertyBuilder DefinePropertyCore(string name, PropertyAttributes attributes, CallingConventions callingConvention, + Type returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, + Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) { - PropertyBuilderImpl property = new PropertyBuilderImpl(name, attributes, callingConvention, returnType, parameterTypes, this); + PropertyBuilderImpl property = new PropertyBuilderImpl(name, attributes, callingConvention, returnType, returnTypeRequiredCustomModifiers, + returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers, this); _propertyDefinitions.Add(property); return property; } - protected override ConstructorBuilder DefineTypeInitializerCore() => throw new NotImplementedException(); - protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); + protected override ConstructorBuilder DefineTypeInitializerCore() + { + ThrowIfCreated(); + + const MethodAttributes attr = MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + + return new ConstructorBuilderImpl(ConstructorInfo.TypeConstructorName, attr, CallingConventions.Standard, null, null, null, _module, this); + } + + protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) + { + if (size <= 0 || size >= 0x003f0000) + { + throw new ArgumentException(SR.Argument_BadSizeForData, nameof(size)); + } + + // This method will define an uninitialized Data in .sdata. + // We will create a fake TypeDef to represent the data with size. This TypeDef + // will be the signature for the Field. + return DefineDataHelper(name, new byte[size], size, attributes); + } + + private FieldBuilder DefineDataHelper(string name, byte[] data, int size, FieldAttributes attributes) + { + ArgumentException.ThrowIfNullOrEmpty(name); + + ThrowIfCreated(); + + // form the value class name + string strValueClassName = $"$ArrayType${size}"; + + // Is this already defined in this module? + TypeBuilderImpl? valueClassType = _module.FindTypeBuilderWithName(strValueClassName, false); + + if (valueClassType == null) + { + TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.ExplicitLayout | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.AnsiClass; + + // Define the backing value class + valueClassType = (TypeBuilderImpl)_module.DefineType(strValueClassName, typeAttributes, typeof(ValueType), PackingSize.Size1, size); + valueClassType.CreateType(); + } + + FieldBuilder fdBuilder = DefineField(name, valueClassType, attributes | FieldAttributes.Static | FieldAttributes.HasFieldRVA); + + // now we need to set the RVA + ((FieldBuilderImpl)fdBuilder).SetData(data); + return fdBuilder; + } + protected override bool IsCreatedCore() => _isCreated; + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { // Handle pseudo custom attributes @@ -618,7 +734,8 @@ public override Type GetGenericTypeDefinition() public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); public override object[] GetCustomAttributes(bool inherit) => throw new NotImplementedException(); public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException(); - public override Type GetElementType() => throw new NotSupportedException(); + // You will never have to deal with a TypeBuilder if you are just referring to arrays. + public override Type GetElementType() => throw new NotSupportedException(SR.NotSupported_DynamicModule); public override string? AssemblyQualifiedName => throw new NotSupportedException(); public override string? FullName => _strFullName ??= TypeNameBuilder.ToString(this, TypeNameBuilder.Format.FullName); public override string? Namespace => _namespace; diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs index 96016fddcab86a..4fca521e6a352c 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs @@ -134,16 +134,16 @@ public void AssemblyWithDifferentTypes() fb.SetCustomAttribute(cattrb); tbFields.CreateType(); - // Data TODO: not supported yet - //module.DefineUninitializedData("data1", 16, FieldAttributes.Public); - //module.DefineInitializedData("data2", new byte[] { 1, 2, 3, 4, 5, 6 }, FieldAttributes.Public); + // Data + module.DefineUninitializedData("Data1", 16, FieldAttributes.Public); + module.DefineInitializedData("Data2", new byte[] { 1, 2, 3, 4, 5, 6 }, FieldAttributes.Public); // Methods and signatures TypeBuilder tb5 = module.DefineType("TypeMethods", TypeAttributes.Public, typeof(object)); // .ctor var cmodsReq1 = new Type[] { typeof(object) }; var cmodsOpt1 = new Type[] { typeof(int) }; - var ctorb = tb5.DefineConstructor(MethodAttributes.Public | MethodAttributes.RTSpecialName, CallingConventions.HasThis, [typeof(int), typeof(object)], new Type[][] { cmodsReq1, null }, new Type[][] { cmodsOpt1, null }); + var ctorb = tb5.DefineConstructor(MethodAttributes.Public | MethodAttributes.RTSpecialName, CallingConventions.HasThis, [typeof(int), typeof(object)], [cmodsReq1, null], [cmodsOpt1, null]); ctorb.SetImplementationFlags(MethodImplAttributes.NoInlining); ctorb.GetILGenerator().Emit(OpCodes.Ret); // Parameters @@ -156,7 +156,7 @@ public void AssemblyWithDifferentTypes() var ctorb2 = tb5.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.RTSpecialName, CallingConventions.Standard, [typeof(int), typeof(object)]); ctorb2.GetILGenerator().Emit(OpCodes.Ret); // method - var mb = tb5.DefineMethod("Method1", MethodAttributes.Public, CallingConventions.Standard, typeof(int), cmodsReq1, cmodsOpt1, [typeof(int), typeof(object)], new Type[][] { cmodsReq1, null }, new Type[][] { cmodsOpt1, null }); + var mb = tb5.DefineMethod("Method1", MethodAttributes.Public, CallingConventions.Standard, typeof(int), cmodsReq1, cmodsOpt1, [typeof(int), typeof(object)], [cmodsReq1, null], [cmodsOpt1, null]); mb.SetImplementationFlags(MethodImplAttributes.NoInlining); mb.GetILGenerator().Emit(OpCodes.Ldc_I4_0); mb.GetILGenerator().Emit(OpCodes.Ret); @@ -322,14 +322,14 @@ void CheckAssembly(Assembly a) Assert.Equal(42, field.GetRawConstantValue()); field = type4.GetField("FieldOffset"); Assert.NotNull(field); - /* TODO: Not supported yet + field = type4.GetField("FieldModopt"); var cmods = field.GetRequiredCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(int), cmods[0]); + Assert.Equal(typeof(int).FullName, cmods[0].FullName); cmods = field.GetOptionalCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(uint), cmods[0]);*/ + Assert.Equal(typeof(uint).FullName, cmods[0].FullName); // Simple marshal field = type4.GetField("FieldMarshal1"); CheckMarshallAttribute(field.GetCustomAttributesData(), UnmanagedType.U4); @@ -347,11 +347,11 @@ void CheckAssembly(Assembly a) field = type4.GetField("FieldCAttr"); CheckCattr(field.GetCustomAttributesData()); - // TODO: Global fields - /*field = a.ManifestModule.GetField("Data1"); + // Global fields + field = a.ManifestModule.GetField("Data1"); Assert.NotNull(field); field = a.ManifestModule.GetField("Data2"); - Assert.NotNull(field);*/ + Assert.NotNull(field); // Methods and signatures var typeMethods = a.GetType("TypeMethods"); @@ -367,13 +367,12 @@ void CheckAssembly(Assembly a) Assert.Equal(typeof(int).FullName, parameters[0].ParameterType.FullName); Assert.Equal(16, parameters[0].RawDefaultValue); CheckCattr(parameters[0].GetCustomAttributesData()); - /* TODO: Not supported yet cmods = parameters[0].GetRequiredCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(object), cmods[0]); + Assert.Equal(typeof(object).FullName, cmods[0].FullName); cmods = parameters[0].GetOptionalCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(int), cmods[0]);*/ + Assert.Equal(typeof(int).FullName, cmods[0].FullName); Assert.Equal("param2", parameters[1].Name); Assert.Equal(ParameterAttributes.Out | ParameterAttributes.HasFieldMarshal, parameters[1].Attributes); Assert.Equal(typeof(object).FullName, parameters[1].ParameterType.FullName); @@ -412,12 +411,12 @@ void CheckAssembly(Assembly a) // return type var rparam = method.ReturnParameter; - /*cmods = rparam.GetRequiredCustomModifiers(); + cmods = rparam.GetRequiredCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(object), cmods[0]); + Assert.Equal(typeof(object).FullName, cmods[0].FullName); cmods = rparam.GetOptionalCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(int), cmods[0]);*/ + Assert.Equal(typeof(int).FullName, cmods[0].FullName); CheckMarshallAttribute(rparam.GetCustomAttributesData(), UnmanagedType.U4); CheckCattr(rparam.GetCustomAttributesData()); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs index 7b7a15bde56854..31ec0e31d87be0 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using Xunit; namespace System.Reflection.Emit.Tests diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs index e838af252c31f7..8ab0de66a8f205 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs @@ -80,7 +80,7 @@ public void DefineLiteral(Type underlyingType, object literalValue) FieldInfo testField = testEnum.GetField("FieldOne"); Assert.Equal(enumBuilder.Name, testField.DeclaringType.Name); - Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal, literal.Attributes); + Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault, literal.Attributes); Assert.Equal(enumBuilder.AsType().FullName, testField.FieldType.FullName); } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index f11c899eb30406..331f8169f3afa5 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Reflection.Emit.Tests @@ -192,7 +191,7 @@ public void ILMaxStack_Test() using (TempFile file = TempFile.Create()) { AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), new [] { typeof(int), typeof(long), typeof(short), typeof(byte) }); + MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), new[] { typeof(int), typeof(long), typeof(short), typeof(byte) }); ILGenerator il1 = method1.GetILGenerator(); // public int Method1(int x, int y, short z, byte r) => @@ -711,7 +710,7 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() FieldBuilder myField = type.DefineField("Field", typeParams[0], FieldAttributes.Public); MethodBuilder genericMethod = type.DefineMethod("GM", MethodAttributes.Public | MethodAttributes.Static); GenericTypeParameterBuilder[] methodParams = genericMethod.DefineGenericParameters("U"); - genericMethod.SetSignature(null, null, null, new [] { methodParams[0] }, null, null); + genericMethod.SetSignature(null, null, null, new[] { methodParams[0] }, null, null); ILGenerator ilg = genericMethod.GetILGenerator(); Type SampleOfU = type.MakeGenericType(methodParams[0]); ilg.DeclareLocal(SampleOfU); @@ -742,22 +741,22 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() /* Generated IL would like this in C#: public class MyType { - public T Field; - - public static void GM(U P_0) - { - MyType myType = new MyType(); - myType.Field = P_0; - Console.WriteLine(myType.Field); - } + public T Field; + + public static void GM(U P_0) + { + MyType myType = new MyType(); + myType.Field = P_0; + Console.WriteLine(myType.Field); + } } internal class Dummy { - public static void Main() - { - MyType.GM("HelloWorld"); - } + public static void Main() + { + MyType.GM("HelloWorld"); + } } */ saveMethod.Invoke(ab, new[] { file.Path }); @@ -960,7 +959,7 @@ public void MemberReferenceExceptions() Assert.Throws("cls", () => il.Emit(OpCodes.Switch, nullType)); Assert.Throws("methodInfo", () => il.EmitCall(OpCodes.Call, nullMethod, null)); // only OpCodes.Switch expected - Assert.Throws("opcode", () => il.Emit(OpCodes.Call, new Label[0])); + Assert.Throws("opcode", () => il.Emit(OpCodes.Call, new Label[0])); // only OpCodes.Call or .OpCodes.Callvirt or OpCodes.Newob expected Assert.Throws("opcode", () => il.Emit(OpCodes.Switch, typeof(object).GetConstructor(Type.EmptyTypes))); // Undefined label @@ -1177,7 +1176,7 @@ public void TryCatchFilterCatchBlock() ilGenerator.Emit(OpCodes.Ldarg_1); ilGenerator.Emit(OpCodes.Div); ilGenerator.Emit(OpCodes.Stloc_0); - Assert.Equal(2, maxStackField.GetValue(ilGenerator)); + Assert.Equal(2, maxStackField.GetValue(ilGenerator)); ilGenerator.BeginCatchBlock(overflowException); ilGenerator.EmitWriteLine("Overflow Exception!"); ilGenerator.ThrowException(overflowException); @@ -1314,9 +1313,9 @@ public void TryFilterCatchFinallyBlock() AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int), typeof(int) }); Type overflowEType = typeof(OverflowException); - ConstructorInfo myConstructorInfo = overflowEType.GetConstructor(new [] { typeof(string) }); + ConstructorInfo myConstructorInfo = overflowEType.GetConstructor(new[] { typeof(string) }); MethodInfo myExToStrMI = overflowEType.GetMethod("ToString"); - MethodInfo myWriteLineMI = typeof(Console).GetMethod("WriteLine", new [] {typeof(string),typeof(object) }); + MethodInfo myWriteLineMI = typeof(Console).GetMethod("WriteLine", new[] { typeof(string), typeof(object) }); ILGenerator ilGenerator = method.GetILGenerator(); LocalBuilder myLocalBuilder1 = ilGenerator.DeclareLocal(typeof(int)); LocalBuilder myLocalBuilder2 = ilGenerator.DeclareLocal(overflowEType); @@ -1592,75 +1591,71 @@ public void DeeperNestedTryCatchFilterFinallyBlocks() private static int Int32Sum(int a, int b) => a + b; - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void EmitCalliBlittable() { - RemoteExecutor.Invoke(() => + int a = 1, b = 1, result = 2; + using (TempFile file = TempFile.Create()) { - int a = 1, b = 1, result = 2; - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliBlittable"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - Type returnType = typeof(int); - MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); - methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldarg_0); - il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, [typeof(int), typeof(int)]); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - var del = new Int32SumStdCall(Int32Sum); - IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); - object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); - GC.KeepAlive(del); - - Assert.IsType(returnType, resultValue); - Assert.Equal(result, resultValue); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliBlittable"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + Type returnType = typeof(int); + MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); + methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldarg_0); + il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, [typeof(int), typeof(int)]); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + var del = new Int32SumStdCall(Int32Sum); + IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); + object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); + GC.KeepAlive(del); + + Assert.IsType(returnType, resultValue); + Assert.Equal(result, resultValue); + tlc.Unload(); + } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void EmitCalliManagedBlittable() { - RemoteExecutor.Invoke(() => + int a = 1, b = 1, result = 2; + using (TempFile file = TempFile.Create()) { - int a = 1, b = 1, result = 2; - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliManagedBlittable"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - Type returnType = typeof(int); - MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); - methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); - MethodInfo method = typeof(AssemblySaveILGeneratorTests).GetMethod(nameof(Int32Sum), BindingFlags.NonPublic | BindingFlags.Static)!; - IntPtr funcPtr = method.MethodHandle.GetFunctionPointer(); - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldarg_0); - il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, returnType, [typeof(int), typeof(int)], null); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); - - Assert.IsType(returnType, resultValue); - Assert.Equal(result, resultValue); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliManagedBlittable"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + Type returnType = typeof(int); + MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); + methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); + MethodInfo method = typeof(AssemblySaveILGeneratorTests).GetMethod(nameof(Int32Sum), BindingFlags.NonPublic | BindingFlags.Static)!; + IntPtr funcPtr = method.MethodHandle.GetFunctionPointer(); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldarg_0); + il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, returnType, [typeof(int), typeof(int)], null); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); + + Assert.IsType(returnType, resultValue); + Assert.Equal(result, resultValue); + tlc.Unload(); + } } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -1668,39 +1663,37 @@ public void EmitCalliManagedBlittable() private static string StringReverse(string a) => string.Join("", a.Reverse()); - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void EmitCalliNonBlittable() { - RemoteExecutor.Invoke(() => + string input = "Test string!", result = "!gnirts tseT"; + using (TempFile file = TempFile.Create()) { - string input = "Test string!", result = "!gnirts tseT"; - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliNonBlittable"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - Type returnType = typeof(string); - MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(string)]); - methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldarg_0); - il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, [typeof(string)]); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - var del = new StringReverseCdecl(StringReverse); - IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); - object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, input]); - GC.KeepAlive(del); - - Assert.IsType(returnType, resultValue); - Assert.Equal(result, resultValue); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliNonBlittable"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + Type returnType = typeof(string); + MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(string)]); + methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_0); + il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, [typeof(string)]); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + var del = new StringReverseCdecl(StringReverse); + IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); + object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, input]); + GC.KeepAlive(del); + + Assert.IsType(returnType, resultValue); + Assert.Equal(result, resultValue); + tlc.Unload(); + } } [Fact] @@ -2071,7 +2064,7 @@ static void GetCode() MethodInfo getMaxStackMethod = GetMaxStackMethod(); // Observed depth of 2, with "adjustment" of 1. - Assert.Equal(2 +1, getMaxStackMethod.Invoke(ilg, null)); + Assert.Equal(2 + 1, getMaxStackMethod.Invoke(ilg, null)); } } @@ -2106,131 +2099,124 @@ static void GetCode() } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void SimpleForLoopTest() { - RemoteExecutor.Invoke(() => + using (TempFile file = TempFile.Create()) { - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder mb2 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); - ILGenerator il = mb2.GetILGenerator(); - LocalBuilder sum = il.DeclareLocal(typeof(int)); - LocalBuilder i = il.DeclareLocal(typeof(int)); - Label loopEnd = il.DefineLabel(); - Label loopStart = il.DefineLabel(); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Stloc_1); - il.MarkLabel(loopStart); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Bgt, loopEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Br, loopStart); - il.MarkLabel(loopEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - MethodInfo getMaxStackMethod = GetMaxStackMethod(); - Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - MethodInfo sumMethodFromDisk = typeFromDisk.GetMethod("SumMethod"); - Assert.Equal(55, sumMethodFromDisk.Invoke(null, [10])); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder mb2 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); + ILGenerator il = mb2.GetILGenerator(); + LocalBuilder sum = il.DeclareLocal(typeof(int)); + LocalBuilder i = il.DeclareLocal(typeof(int)); + Label loopEnd = il.DefineLabel(); + Label loopStart = il.DefineLabel(); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Stloc_1); + il.MarkLabel(loopStart); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Bgt, loopEnd); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Br, loopStart); + il.MarkLabel(loopEnd); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + MethodInfo getMaxStackMethod = GetMaxStackMethod(); + Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + MethodInfo sumMethodFromDisk = typeFromDisk.GetMethod("SumMethod"); + Assert.Equal(55, sumMethodFromDisk.Invoke(null, [10])); + tlc.Unload(); + } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void RecursiveSumTest() { - RemoteExecutor.Invoke(() => + using (TempFile file = TempFile.Create()) { - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("RecursiveSumTest"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - MethodBuilder mb2 = tb.DefineMethod("RecursiveMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); - ILGenerator il = mb2.GetILGenerator(); - Label loopEnd = il.DefineLabel(); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ble, loopEnd); // if (value1 <= value2) - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Sub); - il.Emit(OpCodes.Call, mb2); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Ret); - il.MarkLabel(loopEnd); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - MethodInfo getMaxStackMethod = GetMaxStackMethod(); - Assert.Equal(3, getMaxStackMethod.Invoke(il, null)); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - MethodInfo recursiveMethodFromDisk = typeFromDisk.GetMethod("RecursiveMethod"); - Assert.NotNull(recursiveMethodFromDisk); - Assert.Equal(55, recursiveMethodFromDisk.Invoke(null, [10])); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder mb2 = tb.DefineMethod("RecursiveMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); + ILGenerator il = mb2.GetILGenerator(); + Label loopEnd = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ble, loopEnd); // if (value1 <= value2) + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Sub); + il.Emit(OpCodes.Call, mb2); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Ret); + il.MarkLabel(loopEnd); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + MethodInfo getMaxStackMethod = GetMaxStackMethod(); + Assert.Equal(3, getMaxStackMethod.Invoke(il, null)); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + MethodInfo recursiveMethodFromDisk = typeFromDisk.GetMethod("RecursiveMethod"); + Assert.NotNull(recursiveMethodFromDisk); + Assert.Equal(55, recursiveMethodFromDisk.Invoke(null, [10])); + tlc.Unload(); + } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void CallOpenGenericMembersFromConstructedGenericType() { - RemoteExecutor.Invoke(() => + using (TempFile file = TempFile.Create()) { - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder method = type.DefineMethod("M1", MethodAttributes.Public, typeof(string), null); - - ILGenerator ilGenerator = method.GetILGenerator(); - LocalBuilder span = ilGenerator.DeclareLocal(typeof(ReadOnlySpan)); - LocalBuilder str = ilGenerator.DeclareLocal(typeof(string)); - - ilGenerator.Emit(OpCodes.Ldstr, "hello"); - ilGenerator.Emit(OpCodes.Call, typeof(MemoryExtensions).GetMethod("AsSpan", [typeof(string)])!); - ilGenerator.Emit(OpCodes.Stloc, span); - ilGenerator.Emit(OpCodes.Ldloca_S, span); - ilGenerator.Emit(OpCodes.Ldc_I4_1); - ilGenerator.Emit(OpCodes.Call, typeof(ReadOnlySpan).GetMethod("Slice", [typeof(int)])!); - ilGenerator.Emit(OpCodes.Stloc, span); - ilGenerator.Emit(OpCodes.Ldloca_S, span); - ilGenerator.Emit(OpCodes.Constrained, typeof(ReadOnlySpan)); - ilGenerator.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); - ilGenerator.Emit(OpCodes.Ret); - - type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Type typeFromDisk = Assembly.LoadFile(file.Path).GetType("MyType"); - string result = (string)typeFromDisk.GetMethod("M1").Invoke(Activator.CreateInstance(typeFromDisk), null); - Assert.Equal("ello", result); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + MethodBuilder method = type.DefineMethod("M1", MethodAttributes.Public, typeof(string), null); + + ILGenerator ilGenerator = method.GetILGenerator(); + LocalBuilder span = ilGenerator.DeclareLocal(typeof(ReadOnlySpan)); + LocalBuilder str = ilGenerator.DeclareLocal(typeof(string)); + + ilGenerator.Emit(OpCodes.Ldstr, "hello"); + ilGenerator.Emit(OpCodes.Call, typeof(MemoryExtensions).GetMethod("AsSpan", [typeof(string)])!); + ilGenerator.Emit(OpCodes.Stloc, span); + ilGenerator.Emit(OpCodes.Ldloca_S, span); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Call, typeof(ReadOnlySpan).GetMethod("Slice", [typeof(int)])!); + ilGenerator.Emit(OpCodes.Stloc, span); + ilGenerator.Emit(OpCodes.Ldloca_S, span); + ilGenerator.Emit(OpCodes.Constrained, typeof(ReadOnlySpan)); + ilGenerator.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + string result = (string)typeFromDisk.GetMethod("M1").Invoke(Activator.CreateInstance(typeFromDisk), null); + Assert.Equal("ello", result); + tlc.Unload(); + } } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs new file mode 100644 index 00000000000000..6a3bb1e4dcbcbc --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs @@ -0,0 +1,291 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public class AssemblySaveModuleBuilderTests + { + [Fact] + public void DefineGlobalMethodAndCreateGlobalFunctionsTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + MethodBuilder method = module.DefineGlobalMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public, null, null); + ILGenerator ilGenerator = method.GetILGenerator(); + ilGenerator.EmitWriteLine("Hello World from global method."); + ilGenerator.Emit(OpCodes.Ret); + + MethodBuilder method2 = module.DefineGlobalMethod("MyMethod", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(int), [typeof(string), typeof(int)]); + ilGenerator = method2.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", [typeof(string)])); + ilGenerator.Emit(OpCodes.Ldarg_1); + ilGenerator.Emit(OpCodes.Ret); + + module.CreateGlobalFunctions(); + Assert.Null(method.DeclaringType); + Assert.Equal(typeof(void), method.ReturnType); + Assert.Equal(method, module.GetMethod("TestMethod")); + Assert.Equal(method2, module.GetMethod("MyMethod", [typeof(string), typeof(int)])); + Assert.Equal(2, module.GetMethods().Length); + + saveMethod.Invoke(ab, [file.Path]); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + MethodInfo globalMethod1 = assemblyFromDisk.ManifestModule.GetMethod("TestMethod"); + Assert.NotNull(globalMethod1); + Assert.True(globalMethod1.IsStatic); + Assert.True(globalMethod1.IsPublic); + Assert.Equal(0, globalMethod1.GetParameters().Length); + Assert.Equal(method.ReturnType.FullName, globalMethod1.ReturnType.FullName); + Assert.NotNull(globalMethod1.DeclaringType); + + MethodInfo globalMethod2 = assemblyFromDisk.ManifestModule.GetMethod("MyMethod"); + Assert.NotNull(globalMethod2); + Assert.True(globalMethod2.IsStatic); + Assert.True(globalMethod2.IsPublic); + Assert.Equal(2, globalMethod2.GetParameters().Length); + Assert.Equal(method.ReturnType.FullName, globalMethod1.ReturnType.FullName); + Assert.Equal("", globalMethod2.DeclaringType.Name); + } + } + } + + [Fact] + public void DefineGlobalMethodAndCreateGlobalFunctions_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + Assert.Throws(() => module.DefineGlobalMethod("TestMethod", MethodAttributes.Public, null, null)); // must be static + MethodBuilder method = module.DefineGlobalMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public, null, null); + ILGenerator ilGenerator = method.GetILGenerator(); + ilGenerator.EmitWriteLine("Hello World from global method."); + ilGenerator.Emit(OpCodes.Ret); + + Assert.Throws(() => module.GetMethod("TestMethod")); // not supported when not created + Assert.Throws(() => module.GetMethods()); + module.CreateGlobalFunctions(); + + Assert.Null(method.DeclaringType); + Assert.Throws(() => module.CreateGlobalFunctions()); // already created + Assert.Throws(() => module.DefineGlobalMethod("TestMethod2", MethodAttributes.Static | MethodAttributes.Public, null, null)); + } + + [Fact] + public static void DefinePInvokeMethodTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + DpmParams p = new DpmParams() { MethodName = "A2", LibName = "Foo2.dll", EntrypointName = "Wha2", ReturnType = typeof(int), + ParameterTypes = [typeof(int)], NativeCallConv = CallingConvention.Cdecl }; + + ModuleBuilder modb = ab.DefineDynamicModule("MyModule"); + MethodBuilder mb = modb.DefinePInvokeMethod(p.MethodName, p.LibName, p.EntrypointName, p.Attributes, p.ManagedCallConv, p.ReturnType, + p.ParameterTypes, p.NativeCallConv, p.Charset); + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); + + modb.CreateGlobalFunctions(); + saveMethod.Invoke(ab, [file.Path]); + MethodInfo m = modb.GetMethod(p.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.Any, p.ParameterTypes, null); + Assert.NotNull(m); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Module moduleFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetModule("MyModule"); + Assert.Equal(1, moduleFromDisk.GetMethods().Length); + MethodInfo method = moduleFromDisk.GetMethod(p.MethodName); + Assert.NotNull(method); + AssemblySaveTypeBuilderAPIsTests.VerifyPInvokeMethod(method.DeclaringType, method, p, mlc.CoreAssembly); + } + } + } + + [Theory] + [InlineData(FieldAttributes.Static | FieldAttributes.Public)] + [InlineData(FieldAttributes.Private)] + [InlineData(FieldAttributes.FamANDAssem)] + [InlineData(FieldAttributes.Assembly | FieldAttributes.SpecialName)] + public void DefineUninitializedDataTest(FieldAttributes attributes) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + foreach (int size in new int[] { 1, 2, 0x003f0000 - 1 }) + { + FieldBuilder field = module.DefineUninitializedData(size.ToString(), size, attributes); + + Assert.Equal(size.ToString(), field.Name); + Assert.True(field.IsStatic); + Assert.True((field.Attributes & FieldAttributes.HasFieldRVA) != 0); + Assert.Equal(attributes | FieldAttributes.Static | FieldAttributes.HasFieldRVA, field.Attributes); + } + } + + [Fact] + public void DefineUninitializedData_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + + AssertExtensions.Throws("name", () => module.DefineUninitializedData(null, 1, FieldAttributes.Family)); + AssertExtensions.Throws("name", () => module.DefineUninitializedData("", 1, FieldAttributes.Public)); + + foreach (int size in new int[] { -1, 0, 0x003f0000, 0x003f0000 + 1 }) + { + AssertExtensions.Throws("size", () => module.DefineUninitializedData("TestField", size, FieldAttributes.Private)); + } + + module.CreateGlobalFunctions(); + Assert.Throws(() => module.DefineUninitializedData("TestField", 1, FieldAttributes.HasDefault)); + } + + [Theory] + [InlineData(FieldAttributes.Static | FieldAttributes.Public)] + [InlineData(FieldAttributes.Static | FieldAttributes.Private)] + [InlineData(FieldAttributes.Private)] + public void DefineInitializedDataTest(FieldAttributes attributes) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + FieldBuilder field = module.DefineInitializedData("MyField", [01, 00, 01], attributes); + + Assert.True(field.IsStatic); + Assert.Equal((attributes & FieldAttributes.Public) != 0, field.IsPublic); + Assert.Equal((attributes & FieldAttributes.Private) != 0, field.IsPrivate); + Assert.Equal("MyField", field.Name); + } + + [Fact] + public void DefineInitializedData_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + + AssertExtensions.Throws("name", () => module.DefineInitializedData(null, [1, 0, 1], FieldAttributes.Public)); + AssertExtensions.Throws("name", () => module.DefineInitializedData("", [1, 0, 1], FieldAttributes.Private)); + AssertExtensions.Throws("data", () => module.DefineInitializedData("MyField", null, FieldAttributes.Public)); + AssertExtensions.Throws("Length", () => module.DefineInitializedData("MyField", [], FieldAttributes.Public)); + AssertExtensions.Throws("Length", () => module.DefineInitializedData("MyField", new byte[0x3f0000], FieldAttributes.Public)); + + FieldBuilder field = module.DefineInitializedData("MyField", [1, 0, 1], FieldAttributes.Public); + module.CreateGlobalFunctions(); + + Assert.Null(field.DeclaringType); + Assert.Throws(() => module.DefineInitializedData("MyField2", new byte[] { 1, 0, 1 }, FieldAttributes.Public)); + } + + [Fact] + // Field RVA alignment is not working on mono + [ActiveIssue("https://github.com/dotnet/runtime/issues/97172", TestRuntimes.Mono)] + public void DefineInitializedData_EnsureAlignmentIsMinimumNeededForUseOfCreateSpan() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + TypeBuilder tb = module.DefineType("MyType", TypeAttributes.Public); + // Create static field data in a variety of orders that requires the runtime to actively apply alignment + // RuntimeHelpers.CreateSpan requires data to be naturally aligned within the "PE" file. At this time CreateSpan only + // requires alignments up to 8 bytes. + FieldBuilder field1Byte = module.DefineInitializedData("Field1Byte", new byte[] { 1 }, FieldAttributes.Public); + byte[] field4Byte_1_data = new byte[] { 1, 2, 3, 4 }; + byte[] field8Byte_1_data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + byte[] field4Byte_2_data = new byte[] { 5, 6, 7, 8 }; + byte[] field8Byte_2_data = new byte[] { 9, 10, 11, 12, 13, 14, 15, 16 }; + FieldBuilder field4Byte_1 = module.DefineInitializedData("Field4Bytes_1", field4Byte_1_data, FieldAttributes.Public); + FieldBuilder tbField4Byte_1 = tb.DefineInitializedData("Field4Bytes_1", field4Byte_1_data, FieldAttributes.Public); + FieldBuilder field8Byte_1 = module.DefineInitializedData("Field8Bytes_1", field8Byte_1_data, FieldAttributes.Public); + FieldBuilder field4Byte_2 = module.DefineInitializedData("Field4Bytes_2", field4Byte_2_data, FieldAttributes.Public); + FieldBuilder field8Byte_2 = module.DefineInitializedData("Field8Bytes_2", field8Byte_2_data, FieldAttributes.Public); + FieldBuilder tbField8Byte_2 = tb.DefineInitializedData("Field8Bytes_2", field8Byte_2_data, FieldAttributes.Public); + module.CreateGlobalFunctions(); + tb.CreateType(); + Assert.Null(field4Byte_1.DeclaringType); + Assert.Null(field8Byte_1.DeclaringType); + Assert.Null(field4Byte_2.DeclaringType); + Assert.Null(field8Byte_2.DeclaringType); + + TypeBuilder checkTypeBuilder = module.DefineType("CheckType", TypeAttributes.Public); + CreateLoadAddressMethod("LoadAddress1", field1Byte); + CreateLoadAddressMethod("LoadAddress4_1", field4Byte_1); + CreateLoadAddressMethod("LoadAddress4_3", tbField4Byte_1); + CreateLoadAddressMethod("LoadAddress4_2", field4Byte_2); + CreateLoadAddressMethod("LoadAddress8_1", field8Byte_1); + CreateLoadAddressMethod("LoadAddress8_2", field8Byte_2); + CreateLoadAddressMethod("LoadAddress8_3", tbField8Byte_2); + + void CreateLoadAddressMethod(string name, FieldBuilder fieldBuilder) + { + MethodBuilder loadAddressMethod = checkTypeBuilder.DefineMethod(name, MethodAttributes.Public | MethodAttributes.Static, typeof(IntPtr), null); + ILGenerator methodIL = loadAddressMethod.GetILGenerator(); + methodIL.Emit(OpCodes.Ldsflda, fieldBuilder); + methodIL.Emit(OpCodes.Ret); + } + + checkTypeBuilder.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type checkType = assemblyFromDisk.GetType("CheckType"); + + CheckMethod("LoadAddress4_1", 4, field4Byte_1_data); + CheckMethod("LoadAddress4_3", 4, field4Byte_1_data); + CheckMethod("LoadAddress4_2", 4, field4Byte_2_data); + CheckMethod("LoadAddress8_1", 8, field8Byte_1_data); + CheckMethod("LoadAddress8_2", 8, field8Byte_2_data); + CheckMethod("LoadAddress8_3", 8, field8Byte_2_data); + tlc.Unload(); + + void CheckMethod(string name, int minAlignmentRequired, byte[] dataToVerify) + { + MethodInfo methodToCall = checkType.GetMethod(name); + nint address = (nint)methodToCall.Invoke(null, null); + + for (int i = 0; i < dataToVerify.Length; i++) + { + Assert.Equal(dataToVerify[i], Marshal.ReadByte(address + (nint)i)); + } + Assert.Equal(name + "_0" + "_" + address.ToString(), name + "_" + (address % minAlignmentRequired).ToString() + "_" + address.ToString()); + } + } + } + + [Fact] + // Standalone signature is not supported on mono + [ActiveIssue("https://github.com/dotnet/runtime/issues/96389", TestRuntimes.Mono)] + public void GetABCMetadataToken_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + TypeBuilder type = module.DefineType("MyType", TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public); + FieldBuilder field = type.DefineField("MyField", typeof(int), FieldAttributes.Public); + ConstructorBuilder constructor = type.DefineDefaultConstructor(MethodAttributes.Public); + + Assert.Throws(() => module.GetMethodMetadataToken(method)); + Assert.Throws(() => module.GetMethodMetadataToken(constructor)); + Assert.Throws(() => module.GetFieldMetadataToken(field)); + Assert.Throws(() => module.GetTypeMetadataToken(type)); + + SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConventions.HasThis, typeof(void)); + signature.AddArgument(typeof(string)); + signature.AddArgument(typeof(int)); + + int signatureToken = module.GetSignatureMetadataToken(signature); + int stringToken = module.GetStringMetadataToken("Hello"); + + Assert.True(signatureToken > 0); + Assert.True(stringToken > 0); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs index 69ec92d4b97a51..65762a54b4751b 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs @@ -4,10 +4,24 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.Loader; using Xunit; namespace System.Reflection.Emit.Tests { + class TestAssemblyLoadContext : AssemblyLoadContext + { + public TestAssemblyLoadContext() : base(isCollectible: true) + { + } + + protected override Assembly? Load(AssemblyName name) + { + return null; + } + } + + internal static class AssemblySaveTools { private static readonly AssemblyName s_assemblyName = new AssemblyName("MyDynamicAssembly") diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveMethodBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs similarity index 59% rename from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveMethodBuilderTests.cs rename to src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index 5d1b92e0cf52d4..88755b56714897 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveMethodBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -1,14 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; +using System.Text; using Xunit; namespace System.Reflection.Emit.Tests { [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] - public class AssemblySaveMethodBuilderTests + public class AssemblySaveTypeBuilderAPIsTests { [Fact] public void DefineMethodOverride_InterfaceMethod() @@ -315,6 +317,17 @@ public abstract class PartialImplementation : InterfaceDerivedFromOtherInterface public abstract string M2(int a); } + public interface IDefaultImplementation + { + void Method() => Console.WriteLine("Hello"); + } + + public interface IStaticAbstract + { + static abstract void Method(); + } + + [Fact] public void CreateType_ValidateAllAbstractMethodsAreImplemented() { @@ -329,11 +342,18 @@ public void CreateType_ValidateAllAbstractMethodsAreImplemented() baseTypeImplementedTheInterfaceMethod.DefineMethod("M2", MethodAttributes.Public, typeof(string), [typeof(int)]).GetILGenerator().Emit(OpCodes.Ret); TypeBuilder baseTypePartiallyImplemented = module.DefineType("Type4", TypeAttributes.Public, parent: typeof(PartialImplementation)); baseTypePartiallyImplemented.AddInterfaceImplementation(typeof(InterfaceDerivedFromOtherInterface)); + TypeBuilder interfaceHasStaticAbstractMethod = module.DefineType("Type5", TypeAttributes.Public); + interfaceHasStaticAbstractMethod.AddInterfaceImplementation(typeof(IStaticAbstract)); + TypeBuilder interfaceMethodHasDefaultImplementation = module.DefineType("Type6", TypeAttributes.Public); + interfaceMethodHasDefaultImplementation.AddInterfaceImplementation(typeof(IDefaultImplementation)); Assert.Throws(() => typeNotImplementedIfaceMethod.CreateType()); Assert.Throws(() => partiallyImplementedType.CreateType()); baseTypeImplementedTheInterfaceMethod.CreateType(); // succeeds + interfaceMethodHasDefaultImplementation.CreateType(); //succeeds Assert.Throws(() => baseTypePartiallyImplemented.CreateType()); + Assert.Throws(() => interfaceHasStaticAbstractMethod.CreateType()); + Assert.Throws(() => interfaceMethodHasDefaultImplementation.DefineTypeInitializer()); } [Fact] @@ -395,5 +415,312 @@ public void GetMethodsGetMethodImpl_Tests() Assert.Throws(() => type.GetMethod("VoidMethod")); Assert.Throws(() => type.GetMethod("VoidMethod", BindingFlags.NonPublic | BindingFlags.Instance)); } + + [Fact] + public void ReturnTypeAndParameterRequiredOptionalCustomModifiers() + { + using (TempFile file = TempFile.Create()) + { + Type[] cmodsReq1 = [typeof(object), typeof(string)]; + Type[] cmodsReq2 = [typeof(uint)]; + Type[] cmodsOpt1 = [typeof(int)]; + Type[] cmodsOpt2 = [typeof(long), typeof(byte), typeof(bool)]; + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + MethodBuilder methodAll = type.DefineMethod("AllModifiers", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, + typeof(string), [typeof(int), typeof(short)], [typeof(Version)], [typeof(int), typeof(long)], [cmodsReq1, cmodsReq2], [cmodsOpt1, cmodsOpt2]); + ILGenerator ilGenerator = methodAll.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldstr, "Hello World"); + ilGenerator.Emit(OpCodes.Ret); + Type createdType = type.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo allModMethod = typeFromDisk.GetMethod("AllModifiers"); + Type[] returnReqMods = allModMethod.ReturnParameter.GetRequiredCustomModifiers(); + Type[] returnOptMods = allModMethod.ReturnParameter.GetOptionalCustomModifiers(); + Type[] par0RequiredMods = allModMethod.GetParameters()[0].GetRequiredCustomModifiers(); + Type[] par0OptionalMods = allModMethod.GetParameters()[0].GetOptionalCustomModifiers(); + Assert.Equal(2, returnReqMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(short).FullName), returnReqMods[0]); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(int).FullName), returnReqMods[1]); + Assert.Equal(1, returnOptMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(Version).FullName), returnOptMods[0]); + Assert.Equal(cmodsReq1.Length, par0RequiredMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[1].FullName), par0RequiredMods[0]); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[0].FullName), par0RequiredMods[1]); + Assert.Equal(cmodsOpt1.Length, par0OptionalMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsOpt1[0].FullName), par0OptionalMods[0]); + Assert.Equal(cmodsReq2.Length, allModMethod.GetParameters()[1].GetRequiredCustomModifiers().Length); + Assert.Equal(cmodsOpt2.Length, allModMethod.GetParameters()[1].GetOptionalCustomModifiers().Length); + } + } + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public static void DefinePInvokeMethodExecution_Windows() + { + const string EnvironmentVariable = "COMPUTERNAME"; + + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("DefinePInvokeMethodExecution_Windows"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + MethodBuilder mb = tb.DefinePInvokeMethod( + "GetEnvironmentVariableW", + "kernel32.dll", + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl, + CallingConventions.Standard, + typeof(int), + [typeof(string), typeof(StringBuilder), typeof(int)], + CallingConvention.StdCall, + CharSet.Unicode); + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); + + Type t = tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("GetEnvironmentVariableW", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(methodFromDisk); + + string expected = Environment.GetEnvironmentVariable(EnvironmentVariable); + + int numCharsRequired = (int)methodFromDisk.Invoke(null, [EnvironmentVariable, null, 0]); + if (numCharsRequired == 0) + { + // Environment variable is not defined. Make sure we got that result using both techniques. + Assert.Null(expected); + } + else + { + StringBuilder sb = new StringBuilder(numCharsRequired); + int numCharsWritten = (int)methodFromDisk.Invoke(null, [EnvironmentVariable, sb, numCharsRequired]); + Assert.NotEqual(0, numCharsWritten); + string actual = sb.ToString(); + Assert.Equal(expected, actual); + } + tlc.Unload(); + } + } + + public static IEnumerable TestData + { + get + { + yield return [new DpmParams() { MethodName = "A1", LibName = "Foo1.dll", EntrypointName = "A1", + ReturnType = typeof(int), ParameterTypes = [typeof(string)] }]; + yield return [new DpmParams() { MethodName = "A2", LibName = "Foo2.dll", EntrypointName = "Wha2", + ReturnType = typeof(int), ParameterTypes = [typeof(int)], + NativeCallConv = CallingConvention.Cdecl}]; + yield return [new DpmParams() { MethodName = "A3", LibName = "Foo3.dll", EntrypointName = "Wha3", + ReturnType = typeof(double), ParameterTypes = [typeof(string)], + Charset = CharSet.Ansi, ReturnTypeOptMods = [typeof(short)]}]; + yield return [new DpmParams() { MethodName = "A4", LibName = "Foo4.dll", EntrypointName = "Wha4", + ReturnType = typeof(IntPtr), ParameterTypes = [typeof(string)], + Charset = CharSet.Auto, ReturnTypeReqMods = [typeof(bool)], NativeCallConv = CallingConvention.FastCall}]; + yield return [new DpmParams() { MethodName = "C1", LibName = "Foo5.dll", EntrypointName = "Wha5", + ReturnType = typeof(int), ParameterTypes = [typeof(string)], ReturnTypeReqMods = [typeof(int)], + ReturnTypeOptMods = [typeof(short)], ParameterTypeOptMods = [[typeof(double)]], ParameterTypeReqMods = [[typeof(float)]]}]; + } + } + + [Theory] + [MemberData(nameof(TestData))] + public static void TestDefinePInvokeMethod(DpmParams p) + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder mb = tb.DefinePInvokeMethod(p.MethodName, p.LibName, p.EntrypointName, p.Attributes, p.ManagedCallConv, p.ReturnType, + p.ReturnTypeReqMods, p.ReturnTypeOptMods, p.ParameterTypes, p.ParameterTypeReqMods, p.ParameterTypeOptMods, p.NativeCallConv, p.Charset); + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); + Type t = tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo m = typeFromDisk.GetMethod(p.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(m); + VerifyPInvokeMethod(t, m, p, mlc.CoreAssembly); + } + } + } + + internal static void VerifyPInvokeMethod(Type type, MethodInfo method, DpmParams p, Assembly coreAssembly) + { + Assert.Equal(type.FullName, method.DeclaringType.FullName); + Assert.Equal(p.MethodName, method.Name); + Assert.Equal(p.Attributes, method.Attributes); + Assert.Equal(p.ManagedCallConv, method.CallingConvention); + Assert.Equal(coreAssembly.GetType(p.ReturnType.FullName), method.ReturnType); + + ParameterInfo[] parameters = method.GetParameters(); + Assert.Equal(coreAssembly.GetType(p.ParameterTypes[0].FullName), parameters[0].ParameterType); + + CustomAttributeData dllAttrData = method.GetCustomAttributesData()[0]; + if (dllAttrData.AttributeType.FullName == typeof(PreserveSigAttribute).FullName) + { + dllAttrData = method.GetCustomAttributesData()[1]; + } + + Assert.Equal(coreAssembly.GetType(typeof(DllImportAttribute).FullName), dllAttrData.AttributeType); + Assert.Equal(p.LibName, dllAttrData.ConstructorArguments[0].Value); + foreach (CustomAttributeNamedArgument namedArg in dllAttrData.NamedArguments) + { + if (namedArg.MemberName == "EntryPoint") + { + Assert.Equal(p.EntrypointName, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "CharSet") + { + Assert.Equal(p.Charset, (CharSet)namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "SetLastError") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "ExactSpelling") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "BestFitMapping") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "ThrowOnUnmappableChar") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "PreserveSig") + { + Assert.Equal(true, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "CallingConvention") + { + Assert.Equal(p.NativeCallConv, (CallingConvention)namedArg.TypedValue.Value); + } + } + + IList returnTypeOptMods = method.ReturnParameter.GetOptionalCustomModifiers(); + if (p.ReturnTypeOptMods == null) + { + Assert.Equal(0, returnTypeOptMods.Count); + } + else + { + Assert.Equal(coreAssembly.GetType(p.ReturnTypeOptMods[0].FullName), returnTypeOptMods[0]); + } + + IList returnTypeReqMods = method.ReturnParameter.GetRequiredCustomModifiers(); + if (p.ReturnTypeReqMods == null) + { + Assert.Equal(0, returnTypeReqMods.Count); + } + else + { + Assert.Equal(coreAssembly.GetType(p.ReturnTypeReqMods[0].FullName), returnTypeReqMods[0]); + } + + if (p.ParameterTypeOptMods == null) + { + foreach (ParameterInfo pi in method.GetParameters()) + { + Assert.Equal(0, pi.GetOptionalCustomModifiers().Length); + } + } + else + { + Assert.Equal(parameters.Length, p.ParameterTypeOptMods.Length); + for (int i = 0; i < p.ParameterTypeOptMods.Length; i++) + { + Type[] mods = parameters[i].GetOptionalCustomModifiers(); + Assert.Equal(coreAssembly.GetType(p.ParameterTypeOptMods[i][0].FullName), mods[0]); + } + } + + if (p.ParameterTypeReqMods == null) + { + foreach (ParameterInfo pi in method.GetParameters()) + { + Assert.Equal(0, pi.GetRequiredCustomModifiers().Length); + } + } + else + { + Assert.Equal(parameters.Length, p.ParameterTypeReqMods.Length); + for (int i = 0; i < p.ParameterTypeReqMods.Length; i++) + { + Type[] mods = parameters[i].GetRequiredCustomModifiers(); + Assert.Equal(coreAssembly.GetType(p.ParameterTypeReqMods[i][0].FullName), mods[0]); + } + } + } + + [Fact] + public void DefineTypeInitializer() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + FieldBuilder greetingField = tb.DefineField("Greeting", typeof(string), FieldAttributes.Private | FieldAttributes.Static); + ConstructorBuilder constructor = tb.DefineTypeInitializer(); + ILGenerator constructorIlGenerator = constructor.GetILGenerator(); + constructorIlGenerator.Emit(OpCodes.Ldstr, "hello"); + constructorIlGenerator.Emit(OpCodes.Stsfld, greetingField); + constructorIlGenerator.Emit(OpCodes.Ret); + + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + FieldInfo createdField = typeFromDisk.GetField("Greeting", BindingFlags.NonPublic | BindingFlags.Static); + Assert.Equal("hello", createdField.GetValue(null)); + tlc.Unload(); + } + } + + [Fact] + public static void DefineUninitializedDataTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + FieldBuilder myFieldBuilder = tb.DefineUninitializedData("MyGreeting", 4, FieldAttributes.Public); + var loadAddressMethod = tb.DefineMethod("LoadAddress", MethodAttributes.Public | MethodAttributes.Static, typeof(IntPtr), null); + var methodIL = loadAddressMethod.GetILGenerator(); + methodIL.Emit(OpCodes.Ldsflda, myFieldBuilder); + methodIL.Emit(OpCodes.Ret); + + Type t = tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + byte[] initBytes = [4, 3, 2, 1]; + nint myIntPtr = Marshal.AllocHGlobal(4); + nint intptrTemp = Marshal.AllocHGlobal(4); + for (int j = 0; j < 4; j++) + { + Marshal.WriteByte(myIntPtr + j, initBytes[j]); + } + object myObj = Marshal.PtrToStructure(myIntPtr, typeFromDisk.GetField("MyGreeting").FieldType); + Marshal.StructureToPtr(myObj, intptrTemp, false); + for (int j = 0; j < 4; j++) + { + Assert.Equal(initBytes[j], Marshal.ReadByte(intptrTemp, j)); + } + Marshal.FreeHGlobal(myIntPtr); + Marshal.FreeHGlobal(intptrTemp); + tlc.Unload(); + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 79b3f309d3d3ef..8e6d7227c02a7b 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -515,9 +515,9 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); Module m = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First(); - Type[] type1Params = m.GetTypes()[2].GetGenericArguments(); + Type[] type1Params = m.GetTypes()[0].GetGenericArguments(); Type[] type2Params = m.GetTypes()[1].GetGenericArguments(); - Type[] type3Params = m.GetTypes()[0].GetGenericArguments(); + Type[] type3Params = m.GetTypes()[2].GetGenericArguments(); Assert.Equal("U", type1Params[0].Name); Assert.Empty(type1Params[0].GetTypeInfo().GetGenericParameterConstraints()); @@ -529,10 +529,10 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() Assert.Equal("TOne", type3Params[0].Name); Assert.Equal(nameof(EmptyTestClass), type3Params[0].GetTypeInfo().GetGenericParameterConstraints()[0].Name); - Type[] method11Params = m.GetTypes()[2].GetMethod("TwoParameters").GetGenericArguments(); - Type[] method12Params = m.GetTypes()[2].GetMethod("FiveTypeParameters").GetGenericArguments(); + Type[] method11Params = m.GetTypes()[0].GetMethod("TwoParameters").GetGenericArguments(); + Type[] method12Params = m.GetTypes()[0].GetMethod("FiveTypeParameters").GetGenericArguments(); Assert.Equal(nameof(IMultipleMethod), method12Params[2].GetTypeInfo().GetGenericParameterConstraints()[0].Name); - Type[] method13Params = m.GetTypes()[2].GetMethod("OneParameter").GetGenericArguments(); + Type[] method13Params = m.GetTypes()[0].GetMethod("OneParameter").GetGenericArguments(); Type[] method21Params = m.GetTypes()[1].GetMethod("TestMethod").GetGenericArguments(); Assert.Equal("M", method11Params[0].Name); diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index efb87722f12940..f70ec0e3d751ed 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -68,7 +68,8 @@ - + +