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; }