Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Emit/Call member references in new ILGenerator #94116

Merged
merged 5 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,10 @@
<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>
<data name="InvalidOperation_NotAVarArgCallingConvention" xml:space="preserve">
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
<value>Calling convention must be VarArgs.</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)
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
{
_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)))
Copy link
Member Author

Choose a reason for hiding this comment

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

Runtime implementation Asserts this instead of throwing:

if (opcode.StackBehaviourPush == StackBehaviour.Varpush)
{
// Instruction must be one of call or callvirt.
Debug.Assert(opcode.Equals(OpCodes.Call) ||
opcode.Equals(OpCodes.Callvirt),
"Unexpected opcode encountered for StackBehaviour of VarPush.");
stackchange++;
}
if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
{
// Instruction must be one of call, callvirt or newobj.
Debug.Assert(opcode.Equals(OpCodes.Call) ||
opcode.Equals(OpCodes.Callvirt) ||
opcode.Equals(OpCodes.Newobj),
"Unexpected opcode encountered for StackBehaviour of VarPop.");

I think this should throw instead, same as in the EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes):
if (!(opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj)))
throw new ArgumentException(SR.Argument_NotMethodCallOpcode, nameof(opcode));

{
throw new ArgumentException(SR.Argument_NotMethodCallOpcode, nameof(opcode));
}

int stackchange = 0;
// Push the return value if there is one.
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
stackchange++;
Copy link
Member

Choose a reason for hiding this comment

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

Are we just assuming there is a return value then?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, as it is a constructor

// 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 sealed class MethodBuilderImpl : MethodBuilder
_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 SetImplementationFlagsCore(MethodImplAttributes attribut
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