diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeILGenerator.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeILGenerator.cs index 6cf211e713e383..2e58c7447e9ca2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeILGenerator.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeILGenerator.cs @@ -128,7 +128,7 @@ internal void InternalEmit(OpCode opcode) m_ILStream[m_length++] = (byte)opcodeValue; } - UpdateStackSize(opcode, opcode.StackChange()); + UpdateStackSize(opcode, opcode.StackDifference()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs index e9fdc1ec38c9ad..72342fc27e61a9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs @@ -45,7 +45,7 @@ internal OpCode(OpCodeValues value, int flags) internal bool EndsUncondJmpBlk() => (m_flags & EndsUncondJmpBlkFlag) != 0; - internal int StackChange() => + public int StackDifference() => m_flags >> StackChangeShift; public OperandType OperandType => (OperandType)(m_flags & OperandTypeMask); diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index f33ce87899cb59..642cdc1e47b97b 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -186,4 +186,7 @@ Method body should not exist. + + The specified opcode cannot be passed to EmitCall. + \ No newline at end of file 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 39e8881c2ab8c5..54c24aa7bdb923 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 @@ -7,8 +7,6 @@ using System.Globalization; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; namespace System.Reflection.Emit { @@ -23,6 +21,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder internal int _offset; internal List? _customAttributes; internal object? _defaultValue = DBNull.Value; + internal FieldDefinitionHandle _handle; internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) { @@ -107,7 +106,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan return; case "System.Runtime.InteropServices.MarshalAsAttribute": _attributes |= FieldAttributes.HasFieldMarshal; - _marshallingData = MarshallingData.CreateMarshallingData(con, binaryAttribute, isField : true); + _marshallingData = MarshallingData.CreateMarshallingData(con, binaryAttribute, isField: true); return; } @@ -124,7 +123,7 @@ protected override void SetOffsetCore(int iOffset) #region MemberInfo Overrides - public override int MetadataToken => throw new NotImplementedException(); + public override int MetadataToken => _handle == default ? 0 : MetadataTokens.GetToken(_handle); public override Module Module => _typeBuilder.Module; 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 7b61112ec8bf7a..1536c763084471 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 @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; +using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Reflection.Emit @@ -17,6 +16,8 @@ internal sealed class ILGeneratorImpl : ILGenerator private readonly InstructionEncoder _il; private bool _hasDynamicStackAllocation; private int _maxStackSize; + private int _currentStack; + private List> _memberReferences = new(); internal ILGeneratorImpl(MethodBuilder methodBuilder, int size) { @@ -27,7 +28,7 @@ internal ILGeneratorImpl(MethodBuilder methodBuilder, int size) } internal int GetMaxStackSize() => _maxStackSize; - + internal List> GetMemberReferences() => _memberReferences; internal InstructionEncoder Instructions => _il; internal bool HasDynamicStackAllocation => _hasDynamicStackAllocation; @@ -42,40 +43,53 @@ internal ILGeneratorImpl(MethodBuilder methodBuilder, int size) public override LocalBuilder DeclareLocal(Type localType, bool pinned) => throw new NotImplementedException(); public override Label DefineLabel() => throw new NotImplementedException(); - public override void Emit(OpCode opcode) + private void UpdateStackSize(OpCode opCode) + { + _currentStack += opCode.StackDifference(); + _maxStackSize = Math.Max(_maxStackSize, _currentStack); + } + + private void UpdateStackSize(OpCode opCode, int stackchange) + { + _currentStack += opCode.StackDifference(); + _currentStack += stackchange; + _maxStackSize = Math.Max(_maxStackSize, _currentStack); + } + + public void EmitOpcode(OpCode opcode) { if (opcode == OpCodes.Localloc) { _hasDynamicStackAllocation = true; } - _il.OpCode((ILOpCode)opcode.Value); - // TODO: for now only count the Opcodes emitted, in order to calculate it correctly we might need to make internal Opcode APIs public - // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs#L48 - _maxStackSize++; + _il.OpCode((ILOpCode)opcode.Value); + UpdateStackSize(opcode); } + public override void Emit(OpCode opcode) => EmitOpcode(opcode); + public override void Emit(OpCode opcode, byte arg) { - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _builder.WriteByte(arg); } public override void Emit(OpCode opcode, double arg) { - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _builder.WriteDouble(arg); } public override void Emit(OpCode opcode, float arg) { - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _builder.WriteSingle(arg); } public override void Emit(OpCode opcode, short arg) { - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _builder.WriteInt16(arg); } @@ -86,26 +100,25 @@ public override void Emit(OpCode opcode, int arg) { if (arg >= -1 && arg <= 8) { - _il.OpCode(arg switch + EmitOpcode(arg switch { - -1 => ILOpCode.Ldc_i4_m1, - 0 => ILOpCode.Ldc_i4_0, - 1 => ILOpCode.Ldc_i4_1, - 2 => ILOpCode.Ldc_i4_2, - 3 => ILOpCode.Ldc_i4_3, - 4 => ILOpCode.Ldc_i4_4, - 5 => ILOpCode.Ldc_i4_5, - 6 => ILOpCode.Ldc_i4_6, - 7 => ILOpCode.Ldc_i4_7, - _ => ILOpCode.Ldc_i4_8, + -1 => OpCodes.Ldc_I4_M1, + 0 => OpCodes.Ldc_I4_0, + 1 => OpCodes.Ldc_I4_1, + 2 => OpCodes.Ldc_I4_2, + 3 => OpCodes.Ldc_I4_3, + 4 => OpCodes.Ldc_I4_4, + 5 => OpCodes.Ldc_I4_5, + 6 => OpCodes.Ldc_I4_6, + 7 => OpCodes.Ldc_I4_7, + _ => OpCodes.Ldc_I4_8 }); return; } if (arg >= -128 && arg <= 127) { - _il.OpCode(ILOpCode.Ldc_i4_s); - _builder.WriteSByte((sbyte)arg) ; + Emit(OpCodes.Ldc_I4_S, (sbyte)arg); return; } } @@ -113,27 +126,25 @@ public override void Emit(OpCode opcode, int arg) { if ((uint)arg <= 3) { - _il.OpCode(arg switch + EmitOpcode(arg switch { - 0 => ILOpCode.Ldarg_0, - 1 => ILOpCode.Ldarg_1, - 2 => ILOpCode.Ldarg_2, - _ => ILOpCode.Ldarg_3, + 0 => OpCodes.Ldarg_0, + 1 => OpCodes.Ldarg_1, + 2 => OpCodes.Ldarg_2, + _ => OpCodes.Ldarg_3, }); return; } if ((uint)arg <= byte.MaxValue) { - _il.OpCode(ILOpCode.Ldarg_s); - _builder.WriteByte((byte)arg); + Emit(OpCodes.Ldarg_S, (byte)arg); return; } if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode { - _il.OpCode(ILOpCode.Ldarg); - _builder.WriteInt16((short)arg); + Emit(OpCodes.Ldarg, (short)arg); return; } } @@ -141,15 +152,13 @@ public override void Emit(OpCode opcode, int arg) { if ((uint)arg <= byte.MaxValue) { - _il.OpCode(ILOpCode.Ldarga_s); - _builder.WriteByte((byte)arg); + Emit(OpCodes.Ldarga_S, (byte)arg); return; } if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode { - _il.OpCode(ILOpCode.Ldarga); - _builder.WriteInt16((short)arg); + Emit(OpCodes.Ldarga, (short)arg); return; } } @@ -157,27 +166,25 @@ public override void Emit(OpCode opcode, int arg) { if ((uint)arg <= byte.MaxValue) { - _il.OpCode(ILOpCode.Starg_s); - _builder.WriteByte((byte)arg); + Emit(OpCodes.Starg_S, (byte)arg); return; } if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode { - _il.OpCode(ILOpCode.Starg); - _builder.WriteInt16((short)arg); + Emit(OpCodes.Starg, (short)arg); return; } } // For everything else, put the opcode followed by the arg onto the stream of instructions. - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _builder.WriteInt32(arg); } public override void Emit(OpCode opcode, long arg) { - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _il.CodeBuilder.WriteInt64(arg); } @@ -187,7 +194,7 @@ public override void Emit(OpCode opcode, string str) // represented by str. ModuleBuilder modBuilder = (ModuleBuilder)_methodBuilder.Module; int tempVal = modBuilder.GetStringMetadataToken(str); - _il.OpCode((ILOpCode)opcode.Value); + EmitOpcode(opcode); _il.Token(tempVal); } @@ -196,10 +203,88 @@ public override void Emit(OpCode opcode, string str) public override void Emit(OpCode opcode, Label[] labels) => throw new NotImplementedException(); public override void Emit(OpCode opcode, LocalBuilder local) => throw new NotImplementedException(); public override void Emit(OpCode opcode, SignatureHelper signature) => throw new NotImplementedException(); - public override void Emit(OpCode opcode, FieldInfo field) => throw new NotImplementedException(); - public override void Emit(OpCode opcode, MethodInfo meth) => throw new NotImplementedException(); - public override void Emit(OpCode opcode, Type cls) => throw new NotImplementedException(); - public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes) => throw new NotImplementedException(); + + public override void Emit(OpCode opcode, FieldInfo field) + { + ArgumentNullException.ThrowIfNull(field); + + EmitMember(opcode, field); + } + + public override void Emit(OpCode opcode, MethodInfo meth) + { + ArgumentNullException.ThrowIfNull(meth); + + if (opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj)) + { + EmitCall(opcode, meth, null); + } + else + { + EmitMember(opcode, meth); + } + } + + private void EmitMember(OpCode opcode, MemberInfo member) + { + EmitOpcode(opcode); + _memberReferences.Add(new KeyValuePair + (member, new BlobWriter(_il.CodeBuilder.ReserveBytes(sizeof(int))))); + } + + public override void Emit(OpCode opcode, Type cls) + { + ArgumentNullException.ThrowIfNull(cls); + + EmitOpcode(opcode); + ModuleBuilder module = (ModuleBuilder)_methodBuilder.Module; + _il.Token(module.GetTypeMetadataToken(cls)); + } + + public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes) + { + ArgumentNullException.ThrowIfNull(methodInfo); + + if (!(opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj))) + throw new ArgumentException(SR.Argument_NotMethodCallOpcode, nameof(opcode)); + + int stackchange = 0; + + // Push the return value if there is one. + if (methodInfo.ReturnType != typeof(void)) + { + stackchange++; + } + + // Pop the parameters. + if (methodInfo is MethodBuilderImpl builder) + { + stackchange -= builder.ParameterCount; + } + else + { + stackchange -= methodInfo.GetParameters().Length; + } + + // Pop the this parameter if the method is non-static and the + // instruction is not newobj. + if (!methodInfo.IsStatic && !opcode.Equals(OpCodes.Newobj)) + { + stackchange--; + } + + // Pop the optional parameters off the stack. + if (optionalParameterTypes != null) + { + stackchange -= optionalParameterTypes.Length; + } + + _il.OpCode((ILOpCode)opcode.Value); + UpdateStackSize(opcode, stackchange); + _memberReferences.Add(new KeyValuePair + (methodInfo, new BlobWriter(_il.CodeBuilder.ReserveBytes(sizeof(int))))); + } + public override void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => throw new NotImplementedException(); public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); public override void EndExceptionBlock() => throw new NotImplementedException(); 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 0b0fa27f091e75..afb6518fe261ca 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 @@ -27,6 +27,7 @@ internal sealed class MethodBuilderImpl : MethodBuilder internal DllImportData? _dllImportData; internal List? _customAttributes; internal ParameterBuilderImpl[]? _parameters; + internal MethodDefinitionHandle _handle; internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType) @@ -52,6 +53,8 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv _initLocals = true; } + internal int ParameterCount => _parameterTypes == null? 0 : _parameterTypes.Length; + internal ILGeneratorImpl? ILGeneratorImpl => _ilGenerator; internal BlobBuilder GetMethodSignatureBlob() => MetadataSignatureHelper.MethodSignatureEncoder(_module, @@ -201,7 +204,7 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq public override bool IsSecurityCritical => true; public override bool IsSecuritySafeCritical => false; public override bool IsSecurityTransparent => false; - public override int MetadataToken { get => throw new NotImplementedException(); } + public override int MetadataToken => _handle == default ? 0 : MetadataTokens.GetToken(_handle); public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(SR.NotSupported_DynamicModule); public override Type? ReflectedType { get => throw new NotImplementedException(); } public override ParameterInfo ReturnParameter { get => throw new NotImplementedException(); } @@ -224,8 +227,7 @@ public override int GetHashCode() public override MethodImplAttributes GetMethodImplementationFlags() => _methodImplFlags; - public override ParameterInfo[] GetParameters() - => throw new NotImplementedException(); + public override ParameterInfo[] GetParameters() => throw new NotImplementedException(); public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) => throw new NotSupportedException(SR.NotSupported_DynamicModule); 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 c30398046ab60e..ff48a0db42a230 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 @@ -17,6 +17,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly MetadataBuilder _metadataBuilder; private readonly Dictionary _assemblyReferences = new(); private readonly Dictionary _typeReferences = new(); + private readonly Dictionary _memberReferences = new(); private readonly List _typeDefinitions = new(); private readonly Dictionary _ctorReferences = new(); private Dictionary? _moduleReferences; @@ -161,8 +162,8 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) } WriteCustomAttributes(typeBuilder._customAttributes, typeHandle); - WriteMethods(typeBuilder, genericParams, methodBodyEncoder); WriteFields(typeBuilder); + WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder); } // Now write all generic parameters in order @@ -180,20 +181,32 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) } } - private void WriteMethods(TypeBuilderImpl typeBuilder, List genericParams, MethodBodyStreamEncoder methodBodyEncoder) + // All method handles need to pre populated so that they can be referenced from other method's IL + private void PrePopulateMethodDefinitionHandles(List methods) { - foreach (MethodBuilderImpl method in typeBuilder._methodDefinitions) + foreach (MethodBuilderImpl method in methods) + { + method._handle = MetadataTokens.MethodDefinitionHandle(_nextMethodDefRowId++); + } + } + + private void WriteMethods(List methods, List genericParams, MethodBodyStreamEncoder methodBodyEncoder) + { + PrePopulateMethodDefinitionHandles(methods); + + foreach (MethodBuilderImpl method in methods) { int offset = -1; ILGeneratorImpl? il = method.ILGeneratorImpl; if (il != null) { + FillMemberReferences(il); offset = AddMethodBody(method, il, methodBodyEncoder); } - MethodDefinitionHandle methodHandle = AddMethodDefinition(method, method.GetMethodSignatureBlob(), offset, _nextParameterRowId); - WriteCustomAttributes(method._customAttributes, methodHandle); - _nextMethodDefRowId++; + MethodDefinitionHandle handle = AddMethodDefinition(method, method.GetMethodSignatureBlob(), offset, _nextParameterRowId); + Debug.Assert(method._handle == handle); + WriteCustomAttributes(method._customAttributes, handle); if (method.IsGenericMethodDefinition) { @@ -201,7 +214,7 @@ private void WriteMethods(TypeBuilderImpl typeBuilder, List pair in il.GetMemberReferences()) + { + pair.Value.WriteInt32(MetadataTokens.GetToken(GetMemberHandle(pair.Key))); + } + } + private static int AddMethodBody(MethodBuilderImpl method, ILGeneratorImpl il, MethodBodyStreamEncoder methodBodyEncoder) => methodBodyEncoder.AddMethodBody( instructionEncoder: il.Instructions, @@ -249,23 +270,23 @@ private void WriteFields(TypeBuilderImpl typeBuilder) { foreach (FieldBuilderImpl field in typeBuilder._fieldDefinitions) { - FieldDefinitionHandle fieldHandle = AddFieldDefinition(field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this)); - WriteCustomAttributes(field._customAttributes, fieldHandle); + field._handle = AddFieldDefinition(field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this)); + WriteCustomAttributes(field._customAttributes, field._handle); _nextFieldDefRowId++; if (field._offset > 0 && (typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0) { - AddFieldLayout(fieldHandle, field._offset); + AddFieldLayout(field._handle, field._offset); } if (field._marshallingData != null) { - AddMarshalling(fieldHandle, field._marshallingData.SerializeMarshallingData()); + AddMarshalling(field._handle, field._marshallingData.SerializeMarshallingData()); } if (field._defaultValue != DBNull.Value) { - AddDefaultValue(fieldHandle, field._defaultValue); + AddDefaultValue(field._handle, field._defaultValue); } } } @@ -318,6 +339,50 @@ private TypeReferenceHandle GetTypeReference(Type type) return typeHandle; } + private MemberReferenceHandle GetMemberReference(MemberInfo member) + { + if (!_memberReferences.TryGetValue(member, out var memberHandle)) + { + memberHandle = AddMemberReference(member.Name, GetTypeReference(member.DeclaringType!), GetMemberSignature(member)); + _memberReferences.Add(member, memberHandle); + } + + return memberHandle; + } + + private BlobBuilder GetMemberSignature(MemberInfo member) + { + if (member is MethodInfo method) + { + return MetadataSignatureHelper.MethodSignatureEncoder(this, ParameterTypes(method.GetParameters()), method.ReturnType, + MethodBuilderImpl.GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic); + } + + if (member is FieldInfo field) + { + return MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this); + } + + throw new NotSupportedException(); + } + + private static Type[] ParameterTypes(ParameterInfo[] parameterInfos) + { + if (parameterInfos.Length == 0) + { + return Type.EmptyTypes; + } + + Type[] parameterTypes = new Type[parameterInfos.Length]; + + for (int i=0; i + _metadataBuilder.AddMemberReference( + parent: parent, + name: _metadataBuilder.GetOrAddString(memberName), + signature: _metadataBuilder.GetOrAddBlob(signature)); + private MemberReferenceHandle AddConstructorReference(TypeReferenceHandle parent, ConstructorInfo method) { var blob = MetadataSignatureHelper.ConstructorSignatureEncoder(method.GetParameters(), this); @@ -435,6 +506,21 @@ internal EntityHandle GetTypeHandle(Type type) return GetTypeReference(type); } + internal EntityHandle GetMemberHandle(MemberInfo member) + { + if (member is MethodBuilderImpl mb && Equals(mb.Module)) + { + return mb._handle; + } + + if (member is FieldBuilderImpl fb && Equals(fb.Module)) + { + return fb._handle; + } + + return GetMemberReference(member); + } + internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize, TypeBuilderImpl? enclosingType) { @@ -448,13 +534,33 @@ internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [Dynamic public override string Name => ""; public override string ScopeName => _name; public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); - public override int GetFieldMetadataToken(FieldInfo field) => throw new NotImplementedException(); + + public override int GetFieldMetadataToken(FieldInfo field) + { + if (field is FieldBuilderImpl fb && fb._handle != default) + { + return MetadataTokens.GetToken(fb._handle); + } + + return 0; + } + public override int GetMethodMetadataToken(ConstructorInfo constructor) => throw new NotImplementedException(); - public override int GetMethodMetadataToken(MethodInfo method) => throw new NotImplementedException(); + + public override int GetMethodMetadataToken(MethodInfo method) + { + if (method is MethodBuilderImpl mb && mb._handle != default) + { + return MetadataTokens.GetToken(mb._handle); + } + + return 0; + } public override int GetStringMetadataToken(string stringConstant) => MetadataTokens.GetToken(_metadataBuilder.GetOrAddUserString(stringConstant)); - public override int GetTypeMetadataToken(Type type) => throw new NotImplementedException(); + public override int GetTypeMetadataToken(Type type) => MetadataTokens.GetToken(GetTypeHandle(type)); + protected override void CreateGlobalFunctionsCore() => throw new NotImplementedException(); protected override EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index 2f4dde63b79fb8..147bee70101be6 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -168,5 +168,189 @@ public void MultipleTypesWithMultipleMethods() Assert.Equal(OpCodes.Ldc_I8.Value, longBody[0]); } } + + [Fact] + public void ILOffset_Test() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + MethodBuilder method = type.DefineMethod("Method1", MethodAttributes.Public | MethodAttributes.Static, typeof(Type), new Type[0]); + ILGenerator ilGenerator = method.GetILGenerator(); + + Assert.Equal(0, ilGenerator.ILOffset); + ilGenerator.Emit(OpCodes.Ret); + Assert.Equal(1, ilGenerator.ILOffset); + } + + [Fact] + 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 Type[] { typeof(int), typeof(long), typeof(short), typeof(byte) }); + ILGenerator il1 = method1.GetILGenerator(); + + // public int Method1(int x, int y, short z, byte r) => + // x + (z + 2 * (r + (8 * y + 3 * (y - (5 + x)))) + il1.Emit(OpCodes.Ldarg_1); // push 1 MaxStack 1 + il1.Emit(OpCodes.Ldarg_3); // push 1 MaxStack 2 + il1.Emit(OpCodes.Ldc_I4_2); // push 1 MaxStack 3 + il1.Emit(OpCodes.Ldarg_S, 4); // push 1 MaxStack 4 + il1.Emit(OpCodes.Ldc_I4_8); // push 1 MaxStack 5 + il1.Emit(OpCodes.Ldarg_2); // push 1 MaxStack 6 + il1.Emit(OpCodes.Mul); // pop 2 push 1 MaxStack 5 + il1.Emit(OpCodes.Ldc_I4_3); // push 1 MaxStack 6 + il1.Emit(OpCodes.Ldarg_2); // push 1 MaxStack 7 + il1.Emit(OpCodes.Ldc_I4_5); // push 1 MaxStack 8 + il1.Emit(OpCodes.Ldarg_1); // push 1 MaxStack 9 + il1.Emit(OpCodes.Add); // pop 2 push 1 stack size 8 + il1.Emit(OpCodes.Sub); // pop 2 push 1 stack size 7 + il1.Emit(OpCodes.Mul); // pop 2 push 1 stack size 6 + il1.Emit(OpCodes.Add); // pop 2 push 1 stack size 5 + il1.Emit(OpCodes.Add); // pop 2 push 1 stack size 4 + il1.Emit(OpCodes.Mul); // pop 2 push 1 stack size 3 + il1.Emit(OpCodes.Add); // pop 2 push 1 stack size 2 + il1.Emit(OpCodes.Add); // pop 2 push 1 stack size 1 + il1.Emit(OpCodes.Ret); // pop 1 stack size 0 + + MethodBuilder method2 = type.DefineMethod("Method2", MethodAttributes.Public, typeof(int), new Type[] { typeof(int), typeof(byte) }); + ILGenerator il2 = method2.GetILGenerator(); + + // int Method2(int x, int y) => x + (y + 18); + il2.Emit(OpCodes.Ldarg_1); // push 1 MaxStack 1 + il2.Emit(OpCodes.Ldarg_2); // push 1 MaxStack 2 + il2.Emit(OpCodes.Ldc_I4_S, 8); // push 1 MaxStack 3 + il2.Emit(OpCodes.Add); // pop 2 push 1 stack size 2 + il2.Emit(OpCodes.Add); // pop 2 push 1 stack size 1 + il2.Emit(OpCodes.Ret); // pop 1 stack size 0 + + saveMethod.Invoke(ab, new object[] { file.Path }); + + MethodInfo getMaxStackSizeMethod = LoadILGenerator_GetMaxStackSizeMethod(); + Assert.Equal(9, getMaxStackSizeMethod.Invoke(il1, new object[0])); + Assert.Equal(3, getMaxStackSizeMethod.Invoke(il2, new object[0])); + + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body1 = typeFromDisk.GetMethod("Method1").GetMethodBody(); + MethodBody body2 = typeFromDisk.GetMethod("Method2").GetMethodBody(); + Assert.Equal(9, body1.MaxStackSize); + Assert.Equal(8, body2.MaxStackSize); // apparently doesn't write lower than 8 + } + } + + private MethodInfo LoadILGenerator_GetMaxStackSizeMethod() + { + Type ilgType = Type.GetType("System.Reflection.Emit.ILGeneratorImpl, System.Reflection.Emit", throwOnError: true)!; + return ilgType.GetMethod("GetMaxStackSize", BindingFlags.NonPublic | BindingFlags.Instance, Type.EmptyTypes); + } + + [Fact] + public void ReferencesFieldInIL() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder methodBuilder = tb.DefineMethod("Method1", MethodAttributes.Public, typeof(int), new[] { typeof(int) }); + FieldBuilder fbNumber = tb.DefineField("_number", typeof(int), FieldAttributes.Private); + Assert.Equal(0, fbNumber.MetadataToken); + + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, fbNumber); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Mul); + il.Emit(OpCodes.Ret); + saveMethod.Invoke(ab, new object[] { file.Path }); + + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); + Assert.Equal(9, bodyBytes.Length); + Assert.NotEqual(0, fbNumber.MetadataToken); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldfld.Value, bodyBytes[1]); + Assert.Equal(fbNumber.MetadataToken, BitConverter.ToInt32(bodyBytes.AsSpan().Slice(2, 4))); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[6]); + Assert.Equal(OpCodes.Mul.Value, bodyBytes[7]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[8]); + } + } + + [Fact] + public void ReferencesFieldAndMethodsInIL() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder methodMain = tb.DefineMethod("Main", MethodAttributes.Public, typeof(void), new[] { typeof(int) }); + FieldBuilder field = tb.DefineField("_field", typeof(int), FieldAttributes.Private); + MethodInfo writeLineString = typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }); + MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", new[] { typeof(string), typeof(object), typeof(object), typeof(object) }); + MethodBuilder methodMultiply = tb.DefineMethod("Multiply", MethodAttributes.Public, typeof(int), new[] { typeof(int) }); + /* + class MyType + { + private int _field; + int Multiply(int value) => _field * value; + void Main(int a) + { + Console.WriteLine("Displaying the expression:"); + Console.WriteLine("{0} * {1} = {2}", _field, a, Multiply(a)); + } + } + */ + ILGenerator il = methodMultiply.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, field); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Mul); + il.Emit(OpCodes.Ret); + + ILGenerator ilMain = methodMain.GetILGenerator(); + ilMain.Emit(OpCodes.Ldstr, "Displaying the expression:"); + ilMain.Emit(OpCodes.Call, writeLineString); + ilMain.Emit(OpCodes.Ldstr, "{0} * {1} = {2}"); + ilMain.Emit(OpCodes.Ldarg_0); + ilMain.Emit(OpCodes.Ldfld, field); + ilMain.Emit(OpCodes.Box, typeof(int)); + ilMain.Emit(OpCodes.Ldarg_1); + ilMain.Emit(OpCodes.Box, typeof(int)); + ilMain.Emit(OpCodes.Ldarg_0); + ilMain.Emit(OpCodes.Ldarg_1); + ilMain.Emit(OpCodes.Call, methodMultiply); + ilMain.Emit(OpCodes.Box, typeof(int)); + ilMain.Emit(OpCodes.Call, writeLineObj); + ilMain.Emit(OpCodes.Ret); + + saveMethod.Invoke(ab, new object[] { file.Path }); + Console.WriteLine(file.Path); + + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[5]); + // Bytes 6, 7, 8, 9 are token for writeLineString, but it is not same as the value before save + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[10]); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[15]); + Assert.Equal(OpCodes.Ldfld.Value, bodyBytes[16]); + Assert.Equal(field.MetadataToken, BitConverter.ToInt32(bodyBytes.AsSpan().Slice(17, 4))); + Assert.Equal(OpCodes.Box.Value, bodyBytes[21]); + int intTypeToken = BitConverter.ToInt32(bodyBytes.AsSpan().Slice(22, 4)); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[26]); + Assert.Equal(OpCodes.Box.Value, bodyBytes[27]); + Assert.Equal(intTypeToken, BitConverter.ToInt32(bodyBytes.AsSpan().Slice(28, 4))); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[32]); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[33]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[34]); + Assert.Equal(methodMultiply.MetadataToken, BitConverter.ToInt32(bodyBytes.AsSpan().Slice(35, 4))); + Assert.Equal(OpCodes.Box.Value, bodyBytes[39]); + Assert.Equal(intTypeToken, BitConverter.ToInt32(bodyBytes.AsSpan().Slice(40, 4))); + Assert.Equal(OpCodes.Call.Value, bodyBytes[44]); + // Bytes 24, 46, 47, 48 are token for writeLineObj, but it is not same as the value before save + Assert.Equal(OpCodes.Ret.Value, bodyBytes[49]); + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs index e756c8c4cf7cd3..f63eb084abcf0d 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using Xunit; namespace System.Reflection.Emit.Tests diff --git a/src/libraries/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs b/src/libraries/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs index 2bd85772fef7bf..d8c0a9de6d6743 100644 --- a/src/libraries/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs +++ b/src/libraries/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs @@ -33,6 +33,7 @@ public enum FlowControl public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public bool Equals(System.Reflection.Emit.OpCode obj) { throw null; } public override int GetHashCode() { throw null; } + public int StackDifference() { throw null; } public static bool operator ==(System.Reflection.Emit.OpCode a, System.Reflection.Emit.OpCode b) { throw null; } public static bool operator !=(System.Reflection.Emit.OpCode a, System.Reflection.Emit.OpCode b) { throw null; } public override string? ToString() { throw null; }