Skip to content

Commit

Permalink
Emit/Call member references in new ILGenerator (#94116)
Browse files Browse the repository at this point in the history
* Emit/Call member references in IL

* Add more tests, add double iterations for prepopulating tokens

* Emit constructor reference, add more tests

* Apply feedbacks
  • Loading branch information
buyaa-n committed Oct 30, 2023
1 parent bec0756 commit 8d59c90
Show file tree
Hide file tree
Showing 8 changed files with 571 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,7 @@
<data name="Argument_MustBeSwitchOpCode" xml:space="preserve">
<value>Only 'OpCode.Switch' can be used.</value>
</data>
<data name="Argument_NotMethodCallOpcode" xml:space="preserve">
<value>The specified opcode cannot be passed to EmitCall.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -23,6 +21,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder
internal int _offset;
internal List<CustomAttributeWrapper>? _customAttributes;
internal object? _defaultValue = DBNull.Value;
internal FieldDefinitionHandle _handle;

internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes)
{
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
Expand All @@ -19,6 +20,7 @@ internal sealed class ILGeneratorImpl : ILGenerator
private int _currentStack;
private List<LocalBuilder> _locals = new();
private Dictionary<Label, LabelHandle> _labelTable = new(2);
private List<KeyValuePair<MemberInfo, BlobWriter>> _memberReferences = new();

internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)
{
Expand All @@ -29,6 +31,7 @@ internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)
}

internal int GetMaxStackSize() => _maxStackSize;
internal List<KeyValuePair<MemberInfo, BlobWriter>> GetMemberReferences() => _memberReferences;
internal InstructionEncoder Instructions => _il;
internal bool HasDynamicStackAllocation => _hasDynamicStackAllocation;
internal List<LocalBuilder> Locals => _locals;
Expand Down Expand Up @@ -68,6 +71,13 @@ private void UpdateStackSize(OpCode opCode)
_maxStackSize = Math.Max(_maxStackSize, _currentStack);
}

private void UpdateStackSize(OpCode opCode, int stackChange)
{
_currentStack += opCode.EvaluationStackDelta;
_currentStack += stackChange;
_maxStackSize = Math.Max(_maxStackSize, _currentStack);
}

public void EmitOpcode(OpCode opcode)
{
if (opcode == OpCodes.Localloc)
Expand Down Expand Up @@ -210,7 +220,32 @@ public override void Emit(OpCode opcode, string str)
_il.Token(tempVal);
}

public override void Emit(OpCode opcode, ConstructorInfo con) => throw new NotImplementedException();
public override void Emit(OpCode opcode, ConstructorInfo con)
{
ArgumentNullException.ThrowIfNull(con);

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
stackChange++;
// Pop the parameters.
stackChange -= con.GetParameters().Length;
// Pop the this parameter if the constructor is non-static and the
// instruction is not newobj.
if (!con.IsStatic && !opcode.Equals(OpCodes.Newobj))
{
stackChange--;
}

UpdateStackSize(opcode, stackChange);
_il.OpCode((ILOpCode)opcode.Value);
_memberReferences.Add(new KeyValuePair<MemberInfo, BlobWriter>
(con, new BlobWriter(_il.CodeBuilder.ReserveBytes(sizeof(int)))));
}

public override void Emit(OpCode opcode, Label label)
{
Expand All @@ -227,6 +262,8 @@ public override void Emit(OpCode opcode, Label label)

public override void Emit(OpCode opcode, Label[] labels)
{
ArgumentNullException.ThrowIfNull(labels);

if (!opcode.Equals(OpCodes.Switch))
{
throw new ArgumentException(SR.Argument_MustBeSwitchOpCode, nameof(opcode));
Expand Down Expand Up @@ -270,10 +307,95 @@ public override void Emit(OpCode opcode, LocalBuilder local)
}

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<MemberInfo, BlobWriter>
(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));
}

_il.OpCode((ILOpCode)opcode.Value);
UpdateStackSize(opcode, GetStackChange(opcode, methodInfo, optionalParameterTypes));
_memberReferences.Add(new KeyValuePair<MemberInfo, BlobWriter>
(methodInfo, new BlobWriter(_il.CodeBuilder.ReserveBytes(sizeof(int)))));
}

private static int GetStackChange(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes)
{
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;
}

return stackChange;
}

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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal sealed class MethodBuilderImpl : MethodBuilder
internal DllImportData? _dllImportData;
internal List<CustomAttributeWrapper>? _customAttributes;
internal ParameterBuilderImpl[]? _parameters;
internal MethodDefinitionHandle _handle;

internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType,
Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType)
Expand All @@ -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,
Expand Down Expand Up @@ -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(); }
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 8d59c90

Please sign in to comment.