|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.SymbolStore;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
namespace System.Reflection.Emit
{
internal sealed class ILGeneratorImpl : ILGenerator
{
private const int DefaultSize = 16;
private readonly MethodBuilderImpl _methodBuilder;
private readonly ModuleBuilderImpl _moduleBuilder;
private readonly BlobBuilder _builder;
private readonly InstructionEncoder _il;
private readonly ControlFlowBuilder _cfBuilder;
private readonly Scope _scope; // scope of the entire method body
private Scope _currentScope;
private bool _hasDynamicStackAllocation;
private int _maxStackDepth;
private int _currentStackDepth; // Current stack labelStartDepth
private int _targetDepth; // Stack labelStartDepth at a target of the previous instruction (when it is branching)
// Adjustment to add to _maxStackDepth for incorrect/invalid IL. For example, when branch
// instructions branches backward with non zero stack depths targeting the same label.
private int _depthAdjustment;
private int _localCount;
private Dictionary<Label, LabelInfo> _labelTable = new(2);
private List<KeyValuePair<object, BlobWriter>> _memberReferences = new();
private List<ExceptionBlock> _exceptionStack = new(); // tracks the exception nesting
private List<ExceptionHandlerInfo> _exceptionBlocks = new(); // keeps all ExceptionHandler blocks
private Dictionary<SymbolDocumentWriter, List<SequencePoint>> _documentToSequencePoints = new();
internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size)
{
_methodBuilder = methodBuilder;
_moduleBuilder = (ModuleBuilderImpl)methodBuilder.Module;
// For compat, runtime implementation doesn't throw for negative or zero value.
_builder = new BlobBuilder(Math.Max(size, DefaultSize));
_cfBuilder = new ControlFlowBuilder();
_il = new InstructionEncoder(_builder, _cfBuilder);
_scope = new Scope(_il.Offset, parent: null);
_currentScope = _scope;
}
internal int GetMaxStack() => Math.Min(ushort.MaxValue, _maxStackDepth + _depthAdjustment);
internal List<KeyValuePair<object, BlobWriter>> GetMemberReferences() => _memberReferences;
internal InstructionEncoder Instructions => _il;
internal bool HasDynamicStackAllocation => _hasDynamicStackAllocation;
internal List<LocalBuilder> Locals => _scope.GetAllLocals();
internal int LocalCount => _localCount;
internal Scope Scope => _scope;
internal Dictionary<SymbolDocumentWriter, List<SequencePoint>> DocumentToSequencePoints => _documentToSequencePoints;
internal void AddExceptionBlocks()
{
foreach(ExceptionHandlerInfo eb in _exceptionBlocks)
{
switch (eb.Kind)
{
case ExceptionRegionKind.Catch:
_cfBuilder.AddCatchRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd), _moduleBuilder.GetTypeHandle(eb.ExceptionType!));
break;
case ExceptionRegionKind.Filter:
_cfBuilder.AddFilterRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd), GetMetaLabel(eb.FilterStart));
break;
case ExceptionRegionKind.Fault:
_cfBuilder.AddFaultRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd));
break;
case ExceptionRegionKind.Finally:
_cfBuilder.AddFinallyRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd));
break;
}
}
}
public override int ILOffset => _il.Offset;
public override void BeginCatchBlock(Type? exceptionType)
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
if (currentExBlock.State == ExceptionState.Filter)
{
// Filter block should be followed by catch block with null exception type
if (exceptionType != null)
{
throw new ArgumentException(SR.Argument_ShouldNotSpecifyExceptionType);
}
Emit(OpCodes.Endfilter);
MarkLabel(currentExBlock.HandleStart);
}
else
{
ArgumentNullException.ThrowIfNull(exceptionType);
Emit(OpCodes.Leave, currentExBlock.EndLabel);
if (currentExBlock.State == ExceptionState.Try)
{
MarkLabel(currentExBlock.TryEnd);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
MarkLabel(currentExBlock.HandleEnd);
}
currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = DefineLabel();
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Catch, currentExBlock.TryStart,
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd, default, exceptionType));
MarkLabel(currentExBlock.HandleStart);
}
// Stack depth for "catch" starts at one.
_currentStackDepth = 1;
currentExBlock.State = ExceptionState.Catch;
}
private LabelHandle GetMetaLabel(Label label) => _labelTable[label]._metaLabel;
public override void BeginExceptFilterBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
Emit(OpCodes.Leave, currentExBlock.EndLabel);
if (currentExBlock.State == ExceptionState.Try)
{
MarkLabel(currentExBlock.TryEnd);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
MarkLabel(currentExBlock.HandleEnd);
}
currentExBlock.FilterStart = DefineLabel();
currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = DefineLabel();
currentExBlock.State = ExceptionState.Filter;
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Filter, currentExBlock.TryStart,
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd, currentExBlock.FilterStart));
MarkLabel(currentExBlock.FilterStart);
// Stack depth for "filter" starts at one.
_currentStackDepth = 1;
}
public override Label BeginExceptionBlock()
{
ExceptionBlock currentExBlock = new ExceptionBlock();
currentExBlock.TryStart = DefineLabel();
currentExBlock.TryEnd = DefineLabel();
currentExBlock.EndLabel = DefineLabel(); // End label of the whole exception block
MarkLabel(currentExBlock.TryStart);
currentExBlock.State = ExceptionState.Try;
_exceptionStack.Add(currentExBlock);
// Stack depth for "try" starts at zero.
_currentStackDepth = 0;
return currentExBlock.EndLabel;
}
public override void BeginFaultBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
Emit(OpCodes.Leave, currentExBlock.EndLabel);
if (currentExBlock.State == ExceptionState.Try)
{
MarkLabel(currentExBlock.TryEnd);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
MarkLabel(currentExBlock.HandleEnd);
}
currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = DefineLabel();
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Fault, currentExBlock.TryStart,
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd));
currentExBlock.State = ExceptionState.Fault;
MarkLabel(currentExBlock.HandleStart);
// Stack depth for "fault" starts at zero.
_currentStackDepth = 0;
}
public override void BeginFinallyBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
Label finallyEndLabel = DefineLabel();
if (currentExBlock.State == ExceptionState.Try)
{
Emit(OpCodes.Leave, finallyEndLabel);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
Emit(OpCodes.Leave, currentExBlock.EndLabel);
MarkLabel(currentExBlock.HandleEnd);
currentExBlock.TryEnd = DefineLabel(); // need to nest the catch block within finally
}
MarkLabel(currentExBlock.TryEnd);
currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = finallyEndLabel;
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Finally, currentExBlock.TryStart,
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd));
currentExBlock.State = ExceptionState.Finally;
MarkLabel(currentExBlock.HandleStart);
// Stack depth for "finally" starts at zero.
_currentStackDepth = 0;
}
public override void BeginScope()
{
_currentScope._children ??= new List<Scope>();
Scope newScope = new Scope(_il.Offset, _currentScope);
_currentScope._children.Add(newScope);
_currentScope = newScope;
}
public override LocalBuilder DeclareLocal(Type localType, bool pinned)
{
ArgumentNullException.ThrowIfNull(localType);
_currentScope._locals ??= new List<LocalBuilder>();
LocalBuilder local = new LocalBuilderImpl(_localCount++, localType, _methodBuilder, pinned);
_currentScope._locals.Add(local);
return local;
}
public override Label DefineLabel()
{
LabelHandle metadataLabel = _il.DefineLabel();
Label emitLabel = CreateLabel(metadataLabel.Id);
_labelTable.Add(emitLabel, new LabelInfo(metadataLabel));
return emitLabel;
}
private void UpdateStackSize(OpCode opCode)
{
UpdateStackSize(opCode.EvaluationStackDelta);
if (UnconditionalJump(opCode))
{
_currentStackDepth = 0;
}
}
private static bool UnconditionalJump(OpCode opCode) =>
opCode.FlowControl == FlowControl.Throw || opCode.FlowControl == FlowControl.Return || opCode == OpCodes.Jmp;
private void UpdateStackSize(int stackChange)
{
_currentStackDepth += stackChange;
_maxStackDepth = Math.Max(_maxStackDepth, _currentStackDepth);
// Record the "target" stack depth at this instruction.
_targetDepth = _currentStackDepth;
}
public void EmitOpcode(OpCode opcode)
{
if (opcode == OpCodes.Localloc)
{
_hasDynamicStackAllocation = true;
}
_il.OpCode((ILOpCode)opcode.Value);
UpdateStackSize(opcode);
}
public override void Emit(OpCode opcode) => EmitOpcode(opcode);
public override void Emit(OpCode opcode, byte arg)
{
EmitOpcode(opcode);
_builder.WriteByte(arg);
}
public override void Emit(OpCode opcode, double arg)
{
EmitOpcode(opcode);
_builder.WriteDouble(arg);
}
public override void Emit(OpCode opcode, float arg)
{
EmitOpcode(opcode);
_builder.WriteSingle(arg);
}
public override void Emit(OpCode opcode, short arg)
{
EmitOpcode(opcode);
_builder.WriteInt16(arg);
}
public override void Emit(OpCode opcode, int arg)
{
// Special-case several opcodes that have shorter variants for common values.
if (opcode.Equals(OpCodes.Ldc_I4))
{
if (arg >= -1 && arg <= 8)
{
EmitOpcode(arg switch
{
-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)
{
Emit(OpCodes.Ldc_I4_S, (sbyte)arg);
return;
}
}
else if (opcode.Equals(OpCodes.Ldarg))
{
if ((uint)arg <= 3)
{
EmitOpcode(arg switch
{
0 => OpCodes.Ldarg_0,
1 => OpCodes.Ldarg_1,
2 => OpCodes.Ldarg_2,
_ => OpCodes.Ldarg_3,
});
return;
}
if ((uint)arg <= byte.MaxValue)
{
Emit(OpCodes.Ldarg_S, (byte)arg);
return;
}
if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
{
Emit(OpCodes.Ldarg, (short)arg);
return;
}
}
else if (opcode.Equals(OpCodes.Ldarga))
{
if ((uint)arg <= byte.MaxValue)
{
Emit(OpCodes.Ldarga_S, (byte)arg);
return;
}
if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
{
Emit(OpCodes.Ldarga, (short)arg);
return;
}
}
else if (opcode.Equals(OpCodes.Starg))
{
if ((uint)arg <= byte.MaxValue)
{
Emit(OpCodes.Starg_S, (byte)arg);
return;
}
if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
{
Emit(OpCodes.Starg, (short)arg);
return;
}
}
// For everything else, put the opcode followed by the arg onto the stream of instructions.
EmitOpcode(opcode);
_builder.WriteInt32(arg);
}
public override void Emit(OpCode opcode, long arg)
{
EmitOpcode(opcode);
_il.CodeBuilder.WriteInt64(arg);
}
public override void Emit(OpCode opcode, string str)
{
// Puts the opcode onto the IL stream followed by the metadata token represented by str.
EmitOpcode(opcode);
_il.Token(_moduleBuilder.GetStringMetadataToken(str));
}
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;
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.");
if (con is ConstructorBuilderImpl builder)
{
stackChange -= builder._methodBuilder.ParameterCount;
}
else
{
stackChange -= con.GetParameters().Length;
}
}
EmitOpcode(opcode);
UpdateStackSize(stackChange);
WriteOrReserveToken(_moduleBuilder.TryGetConstructorHandle(con), con);
}
private void WriteOrReserveToken(EntityHandle handle, object member)
{
if (handle.IsNil)
{
// The member is a `***BuilderImpl` and its token is not yet defined.
// Reserve the token bytes and write them later when its ready
_memberReferences.Add(new KeyValuePair<object, BlobWriter>
(member, new BlobWriter(_il.CodeBuilder.ReserveBytes(sizeof(int)))));
}
else
{
_il.Token(MetadataTokens.GetToken(handle));
}
}
private void AdjustDepth(OpCode opcode, LabelInfo label)
{
int labelStartDepth = label._startDepth;
int targetDepth = _targetDepth;
Debug.Assert(labelStartDepth >= -1);
Debug.Assert(targetDepth >= -1);
if (labelStartDepth < targetDepth)
{
// Either unknown depth for this label or this branch location has a larger depth than previously recorded.
// In the latter case, the IL is (likely) invalid, but we just compensate for it using _depthAdjustment.
if (labelStartDepth >= 0)
{
_depthAdjustment += targetDepth - labelStartDepth;
}
// Keep the target depth, it will used as starting stack size from the marked location.
label._startDepth = targetDepth;
}
// If it is unconditionally branching to a new location, for the next instruction invocation stack should be empty.
// if this location is marked with a label, the starting stack size will be adjusted with label._startDepth.
if (UnconditionalBranching(opcode))
{
_currentStackDepth = 0;
}
}
private static bool UnconditionalBranching(OpCode opcode) =>
opcode.FlowControl == FlowControl.Branch;
public override void Emit(OpCode opcode, Label label)
{
if (_labelTable.TryGetValue(label, out LabelInfo? labelInfo))
{
_il.Branch((ILOpCode)opcode.Value, labelInfo._metaLabel);
UpdateStackSize(opcode);
AdjustDepth(opcode, labelInfo);
}
else
{
throw new ArgumentException(SR.Argument_InvalidLabel);
}
}
public override void Emit(OpCode opcode, Label[] labels)
{
ArgumentNullException.ThrowIfNull(labels);
if (!opcode.Equals(OpCodes.Switch))
{
throw new ArgumentException(SR.Argument_MustBeSwitchOpCode, nameof(opcode));
}
SwitchInstructionEncoder switchEncoder = _il.Switch(labels.Length);
UpdateStackSize(opcode);
foreach (Label label in labels)
{
LabelInfo labelInfo = _labelTable[label];
switchEncoder.Branch(labelInfo._metaLabel);
AdjustDepth(opcode, labelInfo);
}
}
public override void Emit(OpCode opcode, LocalBuilder local)
{
ArgumentNullException.ThrowIfNull(local);
if (local is not LocalBuilderImpl localBuilder || localBuilder.GetMethodBuilder() != _methodBuilder)
{
throw new ArgumentException(SR.Argument_UnmatchedMethodForLocal, nameof(local));
}
int tempVal = local.LocalIndex;
string name = opcode.Name!;
if (name.StartsWith("ldloca"))
{
_il.LoadLocalAddress(tempVal);
}
else if (name.StartsWith("ldloc"))
{
_il.LoadLocal(tempVal);
}
else if (name.StartsWith("stloc"))
{
_il.StoreLocal(tempVal);
}
UpdateStackSize(opcode);
}
public override void Emit(OpCode opcode, SignatureHelper signature)
{
ArgumentNullException.ThrowIfNull(signature);
EmitOpcode(opcode);
// The only IL instruction that has VarPop behaviour, that takes a Signature
// token as a parameter is Calli. Pop the parameters and the native function pointer.
if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
{
Debug.Assert(opcode.Equals(OpCodes.Calli), "Unexpected opcode encountered for StackBehaviour VarPop.");
// Pop the arguments. Used reflection since ArgumentCount property is not public.
PropertyInfo argCountProperty = typeof(SignatureHelper).GetProperty("ArgumentCount", BindingFlags.NonPublic | BindingFlags.Instance)!;
int stackChange = -(int)argCountProperty.GetValue(signature)!;
// Pop native function pointer off the stack.
stackChange--;
UpdateStackSize(stackChange);
}
_il.Token(_moduleBuilder.GetSignatureMetadataToken(signature));
}
public override void Emit(OpCode opcode, FieldInfo field)
{
ArgumentNullException.ThrowIfNull(field);
EmitOpcode(opcode);
WriteOrReserveToken(_moduleBuilder.TryGetFieldHandle(field), 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
{
EmitOpcode(opcode);
WriteOrReserveToken(_moduleBuilder.TryGetMethodHandle(meth), meth);
}
}
public override void Emit(OpCode opcode, Type cls)
{
ArgumentNullException.ThrowIfNull(cls);
EmitOpcode(opcode);
WriteOrReserveToken(_moduleBuilder.TryGetTypeHandle(cls), 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));
}
EmitOpcode(opcode);
UpdateStackSize(GetStackChange(opcode, methodInfo, _moduleBuilder.GetTypeFromCoreAssembly(CoreTypeId.Void), optionalParameterTypes));
if (optionalParameterTypes == null || optionalParameterTypes.Length == 0)
{
WriteOrReserveToken(_moduleBuilder.TryGetMethodHandle(methodInfo), methodInfo);
}
else
{
WriteOrReserveToken(_moduleBuilder.TryGetMethodHandle(methodInfo, optionalParameterTypes),
new KeyValuePair<MethodInfo, Type[]>(methodInfo, optionalParameterTypes));
}
}
private static int GetStackChange(OpCode opcode, MethodInfo methodInfo, Type voidType, Type[]? optionalParameterTypes)
{
int stackChange = 0;
// Push the return value if there is one.
if (methodInfo.ReturnType != voidType)
{
stackChange++;
}
// Pop the parameters.
if (methodInfo is MethodBuilderImpl builder)
{
stackChange -= builder.ParameterCount;
}
else if (methodInfo is ArrayMethod sm)
{
stackChange -= sm.ParameterTypes.Length;
}
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)
{
if (optionalParameterTypes != null && optionalParameterTypes.Length > 0)
{
if ((callingConvention & CallingConventions.VarArgs) == 0)
{
// Client should not supply optional parameter in default calling convention
throw new InvalidOperationException(SR.InvalidOperation_NotAVarArgCallingConvention);
}
}
int stackChange = GetStackChange(returnType, _moduleBuilder.GetTypeFromCoreAssembly(CoreTypeId.Void), parameterTypes);
// Pop off VarArg arguments.
if (optionalParameterTypes != null)
{
stackChange -= optionalParameterTypes.Length;
}
// Pop the this parameter if the method has a this parameter.
if ((callingConvention & CallingConventions.HasThis) == CallingConventions.HasThis)
{
stackChange--;
}
UpdateStackSize(stackChange);
EmitOpcode(OpCodes.Calli);
_il.Token(_moduleBuilder.GetSignatureToken(callingConvention, returnType, parameterTypes, optionalParameterTypes));
}
public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes)
{
int stackChange = GetStackChange(returnType, _moduleBuilder.GetTypeFromCoreAssembly(CoreTypeId.Void), parameterTypes);
UpdateStackSize(stackChange);
Emit(OpCodes.Calli);
_il.Token(_moduleBuilder.GetSignatureToken(unmanagedCallConv, returnType, parameterTypes));
}
private static int GetStackChange(Type? returnType, Type voidType, Type[]? parameterTypes)
{
int stackChange = 0;
// If there is a non-void return type, push one.
if (returnType != voidType)
{
stackChange++;
}
// Pop off arguments if any.
if (parameterTypes != null)
{
stackChange -= parameterTypes.Length;
}
// Pop the native function pointer.
stackChange--;
return stackChange;
}
public override void EndExceptionBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
ExceptionState state = currentExBlock.State;
Label endLabel = currentExBlock.EndLabel;
if (state == ExceptionState.Filter || state == ExceptionState.Try)
{
throw new InvalidOperationException(SR.Argument_BadExceptionCodeGen);
}
if (state == ExceptionState.Catch)
{
Emit(OpCodes.Leave, endLabel);
MarkLabel(currentExBlock.HandleEnd);
}
else if (state == ExceptionState.Finally || state == ExceptionState.Fault)
{
Emit(OpCodes.Endfinally);
MarkLabel(currentExBlock.HandleEnd);
}
MarkLabel(endLabel);
currentExBlock.State = ExceptionState.Done;
_exceptionStack.Remove(currentExBlock);
}
public override void EndScope()
{
if (_currentScope._parent == null)
{
throw new InvalidOperationException(SR.InvalidOperation_UnmatchingSymScope);
}
_currentScope._endOffset = _il.Offset;
_currentScope = _currentScope._parent;
}
public override void MarkLabel(Label loc)
{
if (_labelTable.TryGetValue(loc, out LabelInfo? labelInfo))
{
if (labelInfo._position != -1)
{
throw new ArgumentException(SR.Argument_RedefinedLabel);
}
_il.MarkLabel(labelInfo._metaLabel);
labelInfo._position = _il.Offset;
int depth = labelInfo._startDepth;
if (depth < 0)
{
// Unknown start depth for this label, indicating that it hasn't been used yet.
// Or we're in the Backward branch constraint case mentioned in ECMA-335 III.1.7.5.
// But the constraint is not enforced by any mainstream .NET runtime and they are not
// respected by .NET compilers. The _depthAdjustment field will compensate for violations
// of this constraint, as we discover them, check AdjustDepth method for detail. Here
// we assume a depth of zero. If a (later) branch to this label has a positive stack
// depth, we'll record that as the new depth and add the delta into _depthAdjustment.
labelInfo._startDepth = _currentStackDepth;
}
else if (depth < _currentStackDepth)
{
// A branch location with smaller stack targets this label. In this case, the IL is invalid
// but we just compensate for it.
_depthAdjustment += _currentStackDepth - depth;
labelInfo._startDepth = _currentStackDepth;
}
else if (depth > _currentStackDepth)
{
// A branch location with larger stack depth targets this label, can be invalid IL.
// Either case adjust the current stack depth.
_currentStackDepth = depth;
}
}
else
{
throw new ArgumentException(SR.Argument_InvalidLabel);
}
}
protected override void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn)
{
if (document is SymbolDocumentWriter symbolDoc)
{
if (_documentToSequencePoints.TryGetValue(symbolDoc, out List<SequencePoint>? sequencePoints))
{
sequencePoints.Add(new SequencePoint(_il.Offset, startLine, startColumn, endLine, endColumn));
}
else
{
sequencePoints = new List<SequencePoint> { new SequencePoint(_il.Offset, startLine, startColumn, endLine, endColumn) };
_documentToSequencePoints.Add(symbolDoc, sequencePoints);
}
}
else
{
throw new ArgumentException(SR.InvalidOperation_InvalidDocument, nameof(document));
}
}
public override void UsingNamespace(string usingNamespace)
{
ArgumentException.ThrowIfNullOrEmpty(usingNamespace);
_currentScope._importNamespaces ??= new List<string>();
_currentScope._importNamespaces.Add(usingNamespace);
}
}
internal sealed class ExceptionBlock
{
public Label TryStart;
public Label TryEnd;
public Label HandleStart;
public Label HandleEnd;
public Label FilterStart;
public Label EndLabel;
public ExceptionState State;
}
internal struct ExceptionHandlerInfo
{
public readonly ExceptionRegionKind Kind;
public readonly Label TryStart, TryEnd, HandlerStart, HandlerEnd, FilterStart;
public Type? ExceptionType;
public ExceptionHandlerInfo(
ExceptionRegionKind kind,
Label tryStart,
Label tryEnd,
Label handlerStart,
Label handlerEnd,
Label filterStart = default,
Type? catchType = null)
{
Kind = kind;
TryStart = tryStart;
TryEnd = tryEnd;
HandlerStart = handlerStart;
HandlerEnd = handlerEnd;
FilterStart = filterStart;
ExceptionType = catchType;
}
}
internal enum ExceptionState
{
Undefined,
Try,
Filter,
Catch,
Finally,
Fault,
Done
}
internal sealed class LabelInfo
{
internal LabelInfo(LabelHandle metaLabel)
{
_position = -1;
_startDepth = -1;
_metaLabel = metaLabel;
}
internal int _position; // Position in the il stream, with -1 meaning unknown.
internal int _startDepth; // Stack labelStartDepth, with -1 meaning unknown.
internal LabelHandle _metaLabel;
}
internal sealed class Scope
{
internal Scope(int offset, Scope? parent)
{
_startOffset = offset;
_parent = parent;
}
internal Scope? _parent;
internal List<Scope>? _children;
internal List<LocalBuilder>? _locals;
internal List<string>? _importNamespaces;
internal int _startOffset;
internal int _endOffset;
internal List<LocalBuilder> GetAllLocals()
{
List<LocalBuilder> locals = new List<LocalBuilder>();
if (_locals != null)
{
locals.AddRange(_locals);
}
if (_children != null)
{
foreach (Scope child in _children)
{
locals.AddRange(child.GetAllLocals());
}
}
return locals;
}
}
}
|