зеркало из https://github.com/mono/ikvm-fork.git
1003 строки
24 KiB
C#
1003 строки
24 KiB
C#
/*
|
|
Copyright (C) 2008 Jeroen Frijters
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jeroen Frijters
|
|
jeroen@frijters.net
|
|
|
|
*/
|
|
using System;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Collections.Generic;
|
|
using IKVM.Reflection.Emit.Writer;
|
|
using System.Diagnostics.SymbolStore;
|
|
using System.Diagnostics;
|
|
|
|
namespace IKVM.Reflection.Emit
|
|
{
|
|
public struct Label
|
|
{
|
|
// 1-based here, to make sure that an uninitialized Label isn't valid
|
|
private readonly int index1;
|
|
|
|
internal Label(int index)
|
|
{
|
|
this.index1 = index + 1;
|
|
}
|
|
|
|
internal int Index
|
|
{
|
|
get { return index1 - 1; }
|
|
}
|
|
}
|
|
|
|
public sealed class LocalBuilder
|
|
{
|
|
private readonly Type localType;
|
|
private readonly int index;
|
|
internal string name;
|
|
|
|
internal LocalBuilder(Type localType, int index)
|
|
{
|
|
this.localType = localType;
|
|
this.index = index;
|
|
}
|
|
|
|
public void SetLocalSymInfo(string name)
|
|
{
|
|
this.name = name;
|
|
}
|
|
|
|
public Type LocalType
|
|
{
|
|
get { return localType; }
|
|
}
|
|
|
|
public int LocalIndex
|
|
{
|
|
get { return index; }
|
|
}
|
|
}
|
|
|
|
public sealed class ILGenerator
|
|
{
|
|
private static readonly Type FAULT = typeof(ExceptionBlock); // the type we use here doesn't matter, as long as it can never be used as a real exception type
|
|
private readonly ModuleBuilder moduleBuilder;
|
|
private readonly ByteBuffer code = new ByteBuffer(16);
|
|
private readonly List<LocalBuilder> locals = new List<LocalBuilder>();
|
|
private readonly List<int> tokenFixups = new List<int>();
|
|
private readonly List<int> labels = new List<int>();
|
|
private readonly List<int> labelStackHeight = new List<int>();
|
|
private readonly List<LabelFixup> labelFixups = new List<LabelFixup>();
|
|
private readonly List<SequencePoint> sequencePoints = new List<SequencePoint>();
|
|
private readonly List<ExceptionBlock> exceptions = new List<ExceptionBlock>();
|
|
private readonly Stack<ExceptionBlock> exceptionStack = new Stack<ExceptionBlock>();
|
|
private ushort maxStack;
|
|
private int stackHeight;
|
|
private Scope scope;
|
|
|
|
private struct LabelFixup
|
|
{
|
|
internal int label;
|
|
internal int offset;
|
|
}
|
|
|
|
private class ExceptionBlock : IComparer<ExceptionBlock>
|
|
{
|
|
internal readonly int ordinal;
|
|
internal Label labelEnd;
|
|
internal int tryOffset;
|
|
internal int tryLength;
|
|
internal int handlerOffset;
|
|
internal int handlerLength;
|
|
internal Type exceptionType; // null = finally block, FAULT = fault block
|
|
|
|
internal ExceptionBlock(int ordinal)
|
|
{
|
|
this.ordinal = ordinal;
|
|
}
|
|
|
|
int IComparer<ExceptionBlock>.Compare(ExceptionBlock x, ExceptionBlock y)
|
|
{
|
|
if (x.tryOffset == y.tryOffset && x.tryLength == y.tryLength)
|
|
{
|
|
return x.ordinal < y.ordinal ? -1 : 1;
|
|
}
|
|
if (x.tryOffset + x.tryLength <= y.tryOffset)
|
|
{
|
|
return -1;
|
|
}
|
|
if (y.tryOffset + y.tryLength <= x.tryOffset)
|
|
{
|
|
return 1;
|
|
}
|
|
if (x.tryOffset > y.tryOffset || (x.tryOffset == y.tryOffset && x.tryLength < y.tryLength))
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SequencePoint
|
|
{
|
|
internal ISymbolDocumentWriter document;
|
|
internal int offset;
|
|
internal int startLine;
|
|
internal int startColumn;
|
|
internal int endLine;
|
|
internal int endColumn;
|
|
}
|
|
|
|
private class Scope
|
|
{
|
|
internal readonly Scope parent;
|
|
internal readonly List<Scope> children = new List<Scope>();
|
|
internal readonly List<LocalBuilder> locals = new List<LocalBuilder>();
|
|
internal int startOffset;
|
|
internal int endOffset;
|
|
|
|
internal Scope(Scope parent)
|
|
{
|
|
this.parent = parent;
|
|
}
|
|
}
|
|
|
|
internal ILGenerator(ModuleBuilder moduleBuilder)
|
|
{
|
|
this.moduleBuilder = moduleBuilder;
|
|
if (moduleBuilder.symbolWriter != null)
|
|
{
|
|
scope = new Scope(null);
|
|
}
|
|
}
|
|
|
|
public void BeginCatchBlock(Type exceptionType)
|
|
{
|
|
ExceptionBlock block = exceptionStack.Peek();
|
|
Emit(OpCodes.Leave, block.labelEnd);
|
|
stackHeight = 0;
|
|
UpdateStack(1);
|
|
if (block.tryLength == 0)
|
|
{
|
|
block.tryLength = code.Position - block.tryOffset;
|
|
}
|
|
else
|
|
{
|
|
block.handlerLength = code.Position - block.handlerOffset;
|
|
exceptionStack.Pop();
|
|
ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count);
|
|
newBlock.labelEnd = block.labelEnd;
|
|
newBlock.tryOffset = block.tryOffset;
|
|
newBlock.tryLength = block.tryLength;
|
|
block = newBlock;
|
|
exceptions.Add(block);
|
|
exceptionStack.Push(block);
|
|
}
|
|
block.handlerOffset = code.Position;
|
|
block.exceptionType = exceptionType;
|
|
}
|
|
|
|
public void BeginExceptFilterBlock()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public Label BeginExceptionBlock()
|
|
{
|
|
ExceptionBlock block = new ExceptionBlock(exceptions.Count);
|
|
block.labelEnd = DefineLabel();
|
|
block.tryOffset = code.Position;
|
|
exceptionStack.Push(block);
|
|
exceptions.Add(block);
|
|
return block.labelEnd;
|
|
}
|
|
|
|
public void BeginFaultBlock()
|
|
{
|
|
BeginFinallyBlock();
|
|
exceptionStack.Peek().exceptionType = FAULT;
|
|
}
|
|
|
|
public void BeginFinallyBlock()
|
|
{
|
|
ExceptionBlock block = exceptionStack.Peek();
|
|
Emit(OpCodes.Leave, block.labelEnd);
|
|
stackHeight = 0;
|
|
if (block.handlerOffset == 0)
|
|
{
|
|
block.tryLength = code.Position - block.tryOffset;
|
|
}
|
|
else
|
|
{
|
|
block.handlerLength = code.Position - block.handlerOffset;
|
|
MarkLabel(block.labelEnd);
|
|
Label labelEnd = DefineLabel();
|
|
Emit(OpCodes.Leave, labelEnd);
|
|
exceptionStack.Pop();
|
|
ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count);
|
|
newBlock.labelEnd = labelEnd;
|
|
newBlock.tryOffset = block.tryOffset;
|
|
newBlock.tryLength = code.Position - block.tryOffset;
|
|
block = newBlock;
|
|
exceptions.Add(block);
|
|
exceptionStack.Push(block);
|
|
}
|
|
block.handlerOffset = code.Position;
|
|
}
|
|
|
|
public void EndExceptionBlock()
|
|
{
|
|
ExceptionBlock block = exceptionStack.Pop();
|
|
if (block.exceptionType != null && block.exceptionType != FAULT)
|
|
{
|
|
Emit(OpCodes.Leave, block.labelEnd);
|
|
}
|
|
else
|
|
{
|
|
Emit(OpCodes.Endfinally);
|
|
}
|
|
block.handlerLength = code.Position - block.handlerOffset;
|
|
MarkLabel(block.labelEnd);
|
|
}
|
|
|
|
public void BeginScope()
|
|
{
|
|
Scope newScope = new Scope(scope);
|
|
scope.children.Add(newScope);
|
|
scope = newScope;
|
|
scope.startOffset = code.Position;
|
|
}
|
|
|
|
public LocalBuilder DeclareLocal(Type localType)
|
|
{
|
|
LocalBuilder local = new LocalBuilder(localType, locals.Count);
|
|
locals.Add(local);
|
|
if (scope != null)
|
|
{
|
|
scope.locals.Add(local);
|
|
}
|
|
return local;
|
|
}
|
|
|
|
public Label DefineLabel()
|
|
{
|
|
Label label = new Label(labels.Count);
|
|
labels.Add(-1);
|
|
labelStackHeight.Add(-1);
|
|
return label;
|
|
}
|
|
|
|
public void Emit(OpCode opc)
|
|
{
|
|
if (opc.Value < 0)
|
|
{
|
|
code.Write((byte)(opc.Value >> 8));
|
|
}
|
|
code.Write((byte)opc.Value);
|
|
switch (opc.FlowControl)
|
|
{
|
|
case FlowControl.Branch:
|
|
case FlowControl.Break:
|
|
case FlowControl.Return:
|
|
case FlowControl.Throw:
|
|
stackHeight = -1;
|
|
break;
|
|
default:
|
|
UpdateStack(opc.StackDiff);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdateStack(int stackdiff)
|
|
{
|
|
if (stackHeight == -1)
|
|
{
|
|
// we're about to emit unreachable code
|
|
stackHeight = 0;
|
|
}
|
|
Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue);
|
|
stackHeight += stackdiff;
|
|
Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue);
|
|
maxStack = Math.Max(maxStack, (ushort)stackHeight);
|
|
}
|
|
|
|
public void Emit(OpCode opc, byte arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, double arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, FieldInfo field)
|
|
{
|
|
Emit(opc);
|
|
WriteToken(moduleBuilder.GetFieldToken(field));
|
|
}
|
|
|
|
public void Emit(OpCode opc, short arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, int arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, long arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, Label label)
|
|
{
|
|
// We need special stackHeight handling for unconditional branches,
|
|
// because the branch and next flows have differing stack heights.
|
|
// Note that this assumes that unconditional branches do not push/pop.
|
|
int flowStackHeight = this.stackHeight;
|
|
Emit(opc);
|
|
if (opc.Value == OpCodes.Leave.Value || opc.Value == OpCodes.Leave_S.Value)
|
|
{
|
|
flowStackHeight = 0;
|
|
}
|
|
else if (opc.FlowControl != FlowControl.Branch)
|
|
{
|
|
flowStackHeight = this.stackHeight;
|
|
}
|
|
// if the label has already been marked, we can emit the branch offset directly
|
|
if (labels[label.Index] != -1)
|
|
{
|
|
if (labelStackHeight[label.Index] != flowStackHeight)
|
|
{
|
|
// the "backward branch constraint" prohibits this, so we don't need to support it
|
|
throw new NotSupportedException();
|
|
}
|
|
switch (opc.OperandType)
|
|
{
|
|
case OperandType.InlineBrTarget:
|
|
code.Write(labels[label.Index] - (code.Position + 4));
|
|
break;
|
|
case OperandType.ShortInlineBrTarget:
|
|
code.Write((byte)(labels[label.Index] - (code.Position + 1)));
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == flowStackHeight);
|
|
labelStackHeight[label.Index] = flowStackHeight;
|
|
LabelFixup fix = new LabelFixup();
|
|
fix.label = label.Index;
|
|
fix.offset = code.Position;
|
|
labelFixups.Add(fix);
|
|
switch (opc.OperandType)
|
|
{
|
|
case OperandType.InlineBrTarget:
|
|
code.Write(4);
|
|
break;
|
|
case OperandType.ShortInlineBrTarget:
|
|
code.Write((byte)1);
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Emit(OpCode opc, Label[] labels)
|
|
{
|
|
Emit(opc);
|
|
LabelFixup fix = new LabelFixup();
|
|
fix.label = -1;
|
|
fix.offset = code.Position;
|
|
labelFixups.Add(fix);
|
|
code.Write(labels.Length);
|
|
foreach (Label label in labels)
|
|
{
|
|
code.Write(label.Index);
|
|
if (this.labels[label.Index] != -1)
|
|
{
|
|
if (labelStackHeight[label.Index] != stackHeight)
|
|
{
|
|
// the "backward branch constraint" prohibits this, so we don't need to support it
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == stackHeight);
|
|
labelStackHeight[label.Index] = stackHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Emit(OpCode opc, LocalBuilder local)
|
|
{
|
|
if ((opc.Value == OpCodes.Ldloc.Value || opc.Value == OpCodes.Ldloca.Value || opc.Value == OpCodes.Stloc.Value) && local.LocalIndex < 256)
|
|
{
|
|
if (opc.Value == OpCodes.Ldloc.Value)
|
|
{
|
|
switch (local.LocalIndex)
|
|
{
|
|
case 0:
|
|
Emit(OpCodes.Ldloc_0);
|
|
break;
|
|
case 1:
|
|
Emit(OpCodes.Ldloc_1);
|
|
break;
|
|
case 2:
|
|
Emit(OpCodes.Ldloc_2);
|
|
break;
|
|
case 3:
|
|
Emit(OpCodes.Ldloc_3);
|
|
break;
|
|
default:
|
|
Emit(OpCodes.Ldloc_S);
|
|
code.Write((byte)local.LocalIndex);
|
|
break;
|
|
}
|
|
}
|
|
else if (opc.Value == OpCodes.Ldloca.Value)
|
|
{
|
|
Emit(OpCodes.Ldloca_S);
|
|
code.Write((byte)local.LocalIndex);
|
|
}
|
|
else if (opc.Value == OpCodes.Stloc.Value)
|
|
{
|
|
switch (local.LocalIndex)
|
|
{
|
|
case 0:
|
|
Emit(OpCodes.Stloc_0);
|
|
break;
|
|
case 1:
|
|
Emit(OpCodes.Stloc_1);
|
|
break;
|
|
case 2:
|
|
Emit(OpCodes.Stloc_2);
|
|
break;
|
|
case 3:
|
|
Emit(OpCodes.Stloc_3);
|
|
break;
|
|
default:
|
|
Emit(OpCodes.Stloc_S);
|
|
code.Write((byte)local.LocalIndex);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Emit(opc);
|
|
switch (opc.OperandType)
|
|
{
|
|
case OperandType.InlineVar:
|
|
code.Write((ushort)local.LocalIndex);
|
|
break;
|
|
case OperandType.ShortInlineVar:
|
|
code.Write((byte)local.LocalIndex);
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WriteToken(FieldToken token)
|
|
{
|
|
if (token.IsPseudoToken)
|
|
{
|
|
tokenFixups.Add(code.Position);
|
|
}
|
|
code.Write(token.Token);
|
|
}
|
|
|
|
private void WriteToken(MethodToken token)
|
|
{
|
|
if (token.IsPseudoToken)
|
|
{
|
|
tokenFixups.Add(code.Position);
|
|
}
|
|
code.Write(token.Token);
|
|
}
|
|
|
|
private void WriteToken(ConstructorToken token)
|
|
{
|
|
if (token.IsPseudoToken)
|
|
{
|
|
tokenFixups.Add(code.Position);
|
|
}
|
|
code.Write(token.Token);
|
|
}
|
|
|
|
public void Emit(OpCode opc, MethodInfo method)
|
|
{
|
|
Emit(opc);
|
|
WriteToken(moduleBuilder.GetMethodToken(method));
|
|
if (opc.FlowControl == FlowControl.Call)
|
|
{
|
|
int stackdiff = 0;
|
|
if (!method.IsStatic && opc.Value != OpCodes.Newobj.Value)
|
|
{
|
|
// pop this
|
|
stackdiff--;
|
|
}
|
|
// pop parameters
|
|
stackdiff -= GetParameterCount(method);
|
|
if (method.ReturnType != typeof(void))
|
|
{
|
|
// push return value
|
|
stackdiff++;
|
|
}
|
|
UpdateStack(stackdiff);
|
|
}
|
|
}
|
|
|
|
public void Emit(OpCode opc, ConstructorInfo constructor)
|
|
{
|
|
Emit(opc);
|
|
WriteToken(moduleBuilder.GetConstructorToken(constructor));
|
|
if (opc.FlowControl == FlowControl.Call)
|
|
{
|
|
int stackdiff = 0;
|
|
if (!constructor.IsStatic && opc.Value != OpCodes.Newobj.Value)
|
|
{
|
|
// pop this
|
|
stackdiff--;
|
|
}
|
|
// pop parameters
|
|
stackdiff -= GetParameterCount(constructor);
|
|
UpdateStack(stackdiff);
|
|
}
|
|
}
|
|
|
|
private static int GetParameterCount(MethodBase mb)
|
|
{
|
|
if (mb.IsGenericMethod)
|
|
{
|
|
return ((MethodInfo)mb).GetGenericMethodDefinition().GetParameters().Length;
|
|
}
|
|
else
|
|
{
|
|
return mb.GetParameters().Length;
|
|
}
|
|
}
|
|
|
|
public void Emit(OpCode opc, sbyte arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, SignatureHelper sig)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void Emit(OpCode opc, float arg)
|
|
{
|
|
Emit(opc);
|
|
code.Write(arg);
|
|
}
|
|
|
|
public void Emit(OpCode opc, string str)
|
|
{
|
|
Emit(opc);
|
|
code.Write(0x70000000 | moduleBuilder.UserStrings.Add(str));
|
|
}
|
|
|
|
public void Emit(OpCode opc, Type type)
|
|
{
|
|
Emit(opc);
|
|
if (opc.Value == OpCodes.Ldtoken.Value)
|
|
{
|
|
code.Write(moduleBuilder.GetTypeToken(type).Token);
|
|
}
|
|
else
|
|
{
|
|
code.Write(moduleBuilder.GetTypeTokenForMemberRef(type).Token);
|
|
}
|
|
}
|
|
|
|
public void EmitCall(OpCode opc, MethodInfo methodInfo, Type[] optionalParameterTypes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void EmitCalli(OpCode opc, CallingConvention callingConvention, Type returnType, Type[] parameterTypes)
|
|
{
|
|
Emit(opc);
|
|
ByteBuffer sig = new ByteBuffer(16);
|
|
switch (callingConvention)
|
|
{
|
|
case CallingConvention.Cdecl:
|
|
sig.Write((byte)0x01); // C
|
|
break;
|
|
case CallingConvention.StdCall:
|
|
case CallingConvention.Winapi:
|
|
sig.Write((byte)0x02); // STDCALL
|
|
break;
|
|
case CallingConvention.ThisCall:
|
|
sig.Write((byte)0x03); // THISCALL
|
|
break;
|
|
case CallingConvention.FastCall:
|
|
sig.Write((byte)0x04); // FASTCALL
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
sig.WriteCompressedInt(parameterTypes.Length);
|
|
SignatureHelper.WriteType(moduleBuilder, sig, returnType);
|
|
foreach (Type t in parameterTypes)
|
|
{
|
|
SignatureHelper.WriteType(moduleBuilder, sig, t);
|
|
}
|
|
code.Write(0x11000000 | moduleBuilder.Tables.StandAloneSig.Add(moduleBuilder.Blobs.Add(sig)));
|
|
}
|
|
|
|
public void EmitCalli(OpCode opc, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void EmitWriteLine(FieldInfo fld)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void EmitWriteLine(LocalBuilder localBuilder)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void EmitWriteLine(string text)
|
|
{
|
|
Emit(OpCodes.Ldstr, text);
|
|
Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
|
|
}
|
|
|
|
public void EndScope()
|
|
{
|
|
scope.endOffset = code.Position;
|
|
scope = scope.parent;
|
|
}
|
|
|
|
public void MarkLabel(Label loc)
|
|
{
|
|
Debug.Assert(stackHeight == -1 || labelStackHeight[loc.Index] == -1 || stackHeight == labelStackHeight[loc.Index]);
|
|
labels[loc.Index] = code.Position;
|
|
if (labelStackHeight[loc.Index] == -1)
|
|
{
|
|
if (stackHeight == -1)
|
|
{
|
|
// we're at a location that can only be reached by a backward branch,
|
|
// so according to the "backward branch constraint" that must mean the stack is empty
|
|
stackHeight = 0;
|
|
labelStackHeight[loc.Index] = 0;
|
|
}
|
|
else
|
|
{
|
|
labelStackHeight[loc.Index] = stackHeight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(stackHeight == -1 || stackHeight == labelStackHeight[loc.Index]);
|
|
stackHeight = labelStackHeight[loc.Index];
|
|
}
|
|
}
|
|
|
|
public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn)
|
|
{
|
|
SequencePoint sp = new SequencePoint();
|
|
sp.document = document;
|
|
sp.offset = code.Position;
|
|
sp.startLine = startLine;
|
|
sp.startColumn = startColumn;
|
|
sp.endLine = endLine;
|
|
sp.endColumn = endColumn;
|
|
sequencePoints.Add(sp);
|
|
}
|
|
|
|
public void ThrowException(Type excType)
|
|
{
|
|
Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes));
|
|
Emit(OpCodes.Throw);
|
|
}
|
|
|
|
public void UsingNamespace(string usingNamespace)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
internal int WriteBody()
|
|
{
|
|
if (moduleBuilder.symbolWriter != null)
|
|
{
|
|
Debug.Assert(scope != null && scope.parent == null);
|
|
scope.endOffset = code.Position;
|
|
}
|
|
|
|
ResolveBranches();
|
|
|
|
ByteBuffer bb = moduleBuilder.methodBodies;
|
|
|
|
int rva;
|
|
if (locals.Count == 0 && exceptions.Count == 0 && maxStack <= 8 && code.Length < 64)
|
|
{
|
|
rva = WriteTinyHeaderAndCode(bb);
|
|
}
|
|
else
|
|
{
|
|
rva = WriteFatHeaderAndCode(bb);
|
|
}
|
|
|
|
if (moduleBuilder.symbolWriter != null)
|
|
{
|
|
if (sequencePoints.Count != 0)
|
|
{
|
|
ISymbolDocumentWriter document = sequencePoints[0].document;
|
|
int[] offsets = new int[sequencePoints.Count];
|
|
int[] lines = new int[sequencePoints.Count];
|
|
int[] columns = new int[sequencePoints.Count];
|
|
int[] endLines = new int[sequencePoints.Count];
|
|
int[] endColumns = new int[sequencePoints.Count];
|
|
for (int i = 0; i < sequencePoints.Count; i++)
|
|
{
|
|
if (sequencePoints[i].document != document)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
offsets[i] = sequencePoints[i].offset;
|
|
lines[i] = sequencePoints[i].startLine;
|
|
columns[i] = sequencePoints[i].startColumn;
|
|
endLines[i] = sequencePoints[i].endLine;
|
|
endColumns[i] = sequencePoints[i].endColumn;
|
|
}
|
|
moduleBuilder.symbolWriter.DefineSequencePoints(document, offsets, lines, columns, endLines, endColumns);
|
|
}
|
|
|
|
WriteScope(scope);
|
|
}
|
|
return rva;
|
|
}
|
|
|
|
private void ResolveBranches()
|
|
{
|
|
foreach (LabelFixup fixup in labelFixups)
|
|
{
|
|
// is it a switch?
|
|
if (fixup.label == -1)
|
|
{
|
|
code.Position = fixup.offset;
|
|
int count = code.GetInt32AtCurrentPosition();
|
|
int offset = fixup.offset + 4 + 4 * count;
|
|
code.Position += 4;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
int index = code.GetInt32AtCurrentPosition();
|
|
code.Write(labels[index] - offset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
code.Position = fixup.offset;
|
|
byte size = code.GetByteAtCurrentPosition();
|
|
int branchOffset = labels[fixup.label] - (code.Position + size);
|
|
if (size == 1)
|
|
{
|
|
code.Write((byte)branchOffset);
|
|
}
|
|
else
|
|
{
|
|
code.Write(branchOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private int WriteTinyHeaderAndCode(ByteBuffer bb)
|
|
{
|
|
int rva = bb.Position;
|
|
const byte CorILMethod_TinyFormat = 0x2;
|
|
bb.Write((byte)(CorILMethod_TinyFormat | (code.Length << 2)));
|
|
WriteCode(bb);
|
|
return rva;
|
|
}
|
|
|
|
private int WriteFatHeaderAndCode(ByteBuffer bb)
|
|
{
|
|
// fat headers require 4-byte alignment
|
|
bb.Align(4);
|
|
int rva = bb.Position;
|
|
|
|
int localVarSigTok = 0;
|
|
if (locals.Count != 0)
|
|
{
|
|
const byte LOCAL_SIG = 0x07;
|
|
|
|
ByteBuffer localVarSig = new ByteBuffer(locals.Count + 2);
|
|
localVarSig.Write(LOCAL_SIG);
|
|
localVarSig.WriteCompressedInt(locals.Count);
|
|
foreach (LocalBuilder local in locals)
|
|
{
|
|
SignatureHelper.WriteType(moduleBuilder, localVarSig, local.LocalType);
|
|
}
|
|
|
|
localVarSigTok = 0x11000000 | moduleBuilder.Tables.StandAloneSig.Add(moduleBuilder.Blobs.Add(localVarSig));
|
|
}
|
|
|
|
const byte CorILMethod_FatFormat = 0x03;
|
|
const byte CorILMethod_MoreSects = 0x08;
|
|
const byte CorILMethod_InitLocals = 0x10;
|
|
|
|
short flagsAndSize = (short)(CorILMethod_FatFormat | CorILMethod_InitLocals | (3 << 12));
|
|
|
|
if (exceptions.Count > 0)
|
|
{
|
|
flagsAndSize |= CorILMethod_MoreSects;
|
|
}
|
|
|
|
bb.Write(flagsAndSize);
|
|
bb.Write(maxStack);
|
|
bb.Write(code.Length);
|
|
bb.Write(localVarSigTok);
|
|
|
|
WriteCode(bb);
|
|
|
|
if (exceptions.Count > 0)
|
|
{
|
|
bb.Align(4);
|
|
|
|
bool fat = false;
|
|
foreach (ExceptionBlock block in exceptions)
|
|
{
|
|
if (block.tryOffset > 65535 || block.tryLength > 255 || block.handlerOffset > 65535 || block.handlerLength > 255)
|
|
{
|
|
fat = true;
|
|
break;
|
|
}
|
|
}
|
|
exceptions.Sort(exceptions[0]);
|
|
if (exceptions.Count * 12 + 4 > 255)
|
|
{
|
|
fat = true;
|
|
}
|
|
const byte CorILMethod_Sect_EHTable = 0x1;
|
|
const byte CorILMethod_Sect_FatFormat = 0x40;
|
|
const short COR_ILEXCEPTION_CLAUSE_EXCEPTION = 0x0000;
|
|
const short COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002;
|
|
const short COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004;
|
|
|
|
if (fat)
|
|
{
|
|
bb.Write((byte)(CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat));
|
|
int dataSize = exceptions.Count * 24 + 4;
|
|
bb.Write((byte)dataSize);
|
|
bb.Write((short)(dataSize >> 8));
|
|
foreach (ExceptionBlock block in exceptions)
|
|
{
|
|
if (block.exceptionType == FAULT)
|
|
{
|
|
bb.Write((int)COR_ILEXCEPTION_CLAUSE_FAULT);
|
|
}
|
|
else if (block.exceptionType != null)
|
|
{
|
|
bb.Write((int)COR_ILEXCEPTION_CLAUSE_EXCEPTION);
|
|
}
|
|
else
|
|
{
|
|
bb.Write((int)COR_ILEXCEPTION_CLAUSE_FINALLY);
|
|
}
|
|
bb.Write(block.tryOffset);
|
|
bb.Write(block.tryLength);
|
|
bb.Write(block.handlerOffset);
|
|
bb.Write(block.handlerLength);
|
|
if (block.exceptionType != null && block.exceptionType != FAULT)
|
|
{
|
|
bb.Write(moduleBuilder.GetTypeToken(block.exceptionType).Token);
|
|
}
|
|
else
|
|
{
|
|
bb.Write(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bb.Write(CorILMethod_Sect_EHTable);
|
|
bb.Write((byte)(exceptions.Count * 12 + 4));
|
|
bb.Write((short)0);
|
|
foreach (ExceptionBlock block in exceptions)
|
|
{
|
|
if (block.exceptionType == FAULT)
|
|
{
|
|
bb.Write(COR_ILEXCEPTION_CLAUSE_FAULT);
|
|
}
|
|
else if (block.exceptionType != null)
|
|
{
|
|
bb.Write(COR_ILEXCEPTION_CLAUSE_EXCEPTION);
|
|
}
|
|
else
|
|
{
|
|
bb.Write(COR_ILEXCEPTION_CLAUSE_FINALLY);
|
|
}
|
|
bb.Write((short)block.tryOffset);
|
|
bb.Write((byte)block.tryLength);
|
|
bb.Write((short)block.handlerOffset);
|
|
bb.Write((byte)block.handlerLength);
|
|
if (block.exceptionType != null && block.exceptionType != FAULT)
|
|
{
|
|
bb.Write(moduleBuilder.GetTypeToken(block.exceptionType).Token);
|
|
}
|
|
else
|
|
{
|
|
bb.Write(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rva;
|
|
}
|
|
|
|
private void WriteCode(ByteBuffer bb)
|
|
{
|
|
int codeOffset = bb.Position;
|
|
foreach (int fixup in this.tokenFixups)
|
|
{
|
|
moduleBuilder.tokenFixupOffsets.Add(fixup + codeOffset);
|
|
}
|
|
bb.Write(code);
|
|
}
|
|
|
|
private void WriteScope(Scope scope)
|
|
{
|
|
moduleBuilder.symbolWriter.OpenScope(scope.startOffset);
|
|
ByteBuffer localVarSig = new ByteBuffer(6);
|
|
foreach (LocalBuilder local in scope.locals)
|
|
{
|
|
if (local.name != null)
|
|
{
|
|
localVarSig.Clear();
|
|
SignatureHelper.WriteType(moduleBuilder, localVarSig, local.LocalType);
|
|
moduleBuilder.symbolWriter.DefineLocalVariable(local.name, 0, localVarSig.ToArray(), SymAddressKind.ILOffset, local.LocalIndex, 0, 0, scope.startOffset, scope.endOffset);
|
|
}
|
|
}
|
|
foreach (Scope child in scope.children)
|
|
{
|
|
WriteScope(child);
|
|
}
|
|
moduleBuilder.symbolWriter.CloseScope(scope.endOffset);
|
|
}
|
|
}
|
|
}
|