/*
* PERWAPI - An API for Reading and Writing PE Files
*
* Copyright (c) Diane Corney, Queensland University of Technology, 2004.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the PERWAPI Copyright as included with this
* distribution in the file PERWAPIcopyright.rtf.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY as is explained in the copyright notice.
*
* The author may be contacted at d.corney@qut.edu.au
*
* Version Date: 26/01/07
*/
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using SCG = System.Collections.Generic;
namespace QUT.PERWAPI
{
/**************************************************************************/
// Classes to represent CIL Byte Codes
/**************************************************************************/
///
/// The IL instructions for a method
///
public class CILInstructions
{
private static readonly uint MaxClauses = 10;
private static readonly uint ExHeaderSize = 4;
private static readonly uint FatExClauseSize = 24;
private static readonly uint SmlExClauseSize = 12;
private static readonly sbyte maxByteVal = 127;
private static readonly sbyte minByteVal = -128;
private static readonly byte maxUByteVal = 255;
private static readonly int smallSize = 64;
internal static readonly ushort TinyFormat = 0x2;
internal static readonly ushort FatFormat = 0x03;
internal static readonly ushort FatFormatHeader = 0x3003;
internal static readonly ushort MoreSects = 0x8;
internal static readonly ushort InitLocals = 0x10;
private static readonly uint FatSize = 12;
private static readonly uint FatWords = FatSize / 4;
internal static readonly byte FatExceptTable = 0x41;
internal static readonly byte SmlExceptTable = 0x01;
internal static readonly uint EHTable = 0x1;
internal static readonly uint SectFatFormat = 0x40;
internal static readonly uint SectMoreSects = 0x80;
private ArrayList exceptions, sourceLines, defaultLines;
private SourceFile defaultSourceFile;
private Stack blockStack;
//private bool codeChecked = false;
private static readonly int INITSIZE = 5;
private CILInstruction[] buffer = new CILInstruction[INITSIZE];
// REPLACE with ArrayList for next version of .NET
private CILInstruction[] saveBuffer;
private int tide = 0, saveTide = 0;
private uint offset = 0;
private ushort headerFlags = 0;
private short maxStack;
private uint paddingNeeded = 0;
private byte exceptHeader = 0;
private int currI = -1;
uint localSigIx = 0;
int numReplace = 0;
uint codeSize = 0, exceptSize = 0;
bool tinyFormat, fatExceptionFormat = false, inserting = false;
MethodDef thisMeth;
internal Scope currentScope;
///
/// Shows if return statements in this code block require a value on the stack or not.
///
internal bool ReturnsVoid;
/*-------------------- Constructors ---------------------------------*/
internal CILInstructions(MethodDef meth)
{
thisMeth = meth;
}
/*--------------------- public general editing methods ---------------------------*/
///
/// The source file containing these IL instructions
///
public SourceFile DefaultSourceFile
{
get { return defaultSourceFile; }
set { defaultSourceFile = value; }
}
///
/// The number of instructions currently in the buffer.
///
public int NumInstructions()
{
if (inserting) return tide + saveTide;
return tide;
}
///
/// Get the next instruction in the instruction buffer in sequence.
/// An internal index is kept to keep track of which instruction was the last
/// retrieved by this method. On the first call, the first instruction in
/// the buffer is retrieved. The instruction index may be zeroed
/// using ResetInstCounter(). This method cannot be called when in "insert" mode.
///
///
public CILInstruction GetNextInstruction()
{
if (inserting) throw new Exception("Cannot access next instruction during insert");
if (currI + 1 < tide)
return buffer[++currI];
return null;
}
///
/// Get the previous instruction in the instruction buffer in sequence.
/// An internal index is kept to keep track of which instruction was the last
/// retrieved by this method. This method cannot be called when in "insert" mode.
///
///
public CILInstruction GetPrevInstruction()
{
if (inserting) throw new Exception("Cannot access previous instruction during insert");
if (currI > 0)
return buffer[--currI];
return null;
}
///
/// Reset the counter for GetNextInstuction to the first instruction.
/// This method cannot be called when in "insert" mode.
///
public void ResetInstCounter()
{
if (inserting) throw new Exception("Cannot reset instruction counter during insert");
currI = -1;
}
///
/// Reset the counter for GetNextInstuction to the first instruction.
/// This method cannot be called when in "insert" mode.
///
public void EndInstCounter()
{
if (inserting) throw new Exception("Cannot reset instruction counter during insert");
currI = tide;
}
///
/// Get all the IL instructions.
/// This method cannot be called when in "insert" mode.
///
///
public CILInstruction[] GetInstructions()
{
if (inserting) throw new Exception("Cannot get instructions during insert");
return buffer;
}
///
/// Set the instruction to be the new array of instructions, this will replace
/// any existing instructions. This method cannot be called when in "insert" mode.
///
/// The new instructions
public void SetInstructions(CILInstruction[] insts)
{
if (inserting) throw new Exception("Cannot replace instructions during insert.");
buffer = insts;
tide = buffer.Length;
for (int i = 0; i < tide; i++)
{
if (insts[i] == null)
tide = i;
insts[i].index = (uint)i;
}
}
///
/// This method should only be used to insert instructions into a buffer which
/// already contains some instructions.
/// Start inserting instructions into the instruction buffer ie. set the buffer
/// to "insert" mode. The position of the insertion will be directly after
/// the "current instruction" as used be GetNextInstruction(). The
/// instructions to be inserted are any calls to the instruction specific
/// methods - Inst, TypeInst, MethodInst, etc.
/// This method cannot be called if already in "insert" mode.
///
public void StartInsert()
{
if (inserting)
throw new Exception("Cannot insert into an instruction buffer already in insert mode");
inserting = true;
saveTide = tide;
saveBuffer = buffer;
tide = 0;
buffer = new CILInstruction[INITSIZE];
}
///
/// Stop inserting instructions into the buffer. Any instructions added after
/// this call will go at the end of the instruction buffer.
/// To be used with StartInsert().
/// This method cannot be called if not in "insert" mode.
///
public void EndInsert()
{
if (!inserting)
throw new Exception("Cannot stop inserting if not in insert mode");
CILInstruction[] newInsts = buffer;
buffer = saveBuffer;
int numNew = tide;
tide = saveTide;
int insPos = currI + 1;
if (numReplace > 0) insPos--;
InsertInstructions(insPos, newInsts, numNew);
inserting = false;
numReplace = 0;
}
///
/// Check if the buffer is ready for insertion of extra instructions.
/// The buffer only needs to be in insert mode when instructions need
/// to be added to existing instructions, not for addition of instructions
/// to the end of the buffer.
///
///
public bool InInsertMode() { return inserting; }
///
/// Remove the instruction at a specified position from the buffer. If you
/// remove the "current" instruction (from GetNext or GetPrev) then the
/// "current" instruction becomes the instruction before that in the buffer.
///
/// position of the instruction to be removed
public void RemoveInstruction(int pos)
{
if (pos < 0) return;
for (int i = pos; i < tide - 1; i++)
{
buffer[i] = buffer[i + 1];
buffer[i].index = (uint)i;
}
tide--;
if (pos == currI) currI = pos - 1;
}
///
/// Remove the instructions from position "startRange" to (and including)
/// position "endRange" from the buffer. If the range removed contains the
/// "current" instruction (from GetNext or GetPrev) then the "current"
/// instruction becomes the instruction before startRange in the buffer.
///
public void RemoveInstructions(int startRange, int endRange)
{
if (startRange < 0) startRange = 0;
if (endRange >= tide - 1)
{// cut to startRange
tide = startRange;
return;
}
int offset = endRange - startRange + 1;
for (int i = endRange + 1; i < tide; i++)
{
buffer[i - offset] = buffer[i];
buffer[i - offset].index = (uint)(i - offset);
}
tide -= offset;
if ((currI >= startRange) && (currI <= endRange)) currI = startRange - 1;
}
///
/// Replace a single IL instruction at position pos in the buffer
/// with some new instruction(s). This removes the instruction and puts
/// the instruction buffer into "insert" mode at the position of the removed
/// instruction. EndInsert must be called to insert the new instructions.
/// This method cannot be called when in "insert" mode.
///
/// position of the instruction to be replaced
public void ReplaceInstruction(int pos)
{
if (inserting) throw new Exception("Cannot replace instructions during insert.");
currI = pos;
if ((pos > 0) || (pos < tide))
{
numReplace = 1;
StartInsert();
}
}
///
/// Replace a number of IL instructions beginning at position pos in the buffer
/// with some new instruction(s). This removes the instructions and puts
/// the instruction buffer into "insert" mode at the position of the removed
/// instructions. EndInsert must be called to insert the new instructions.
/// The instructions from index "from" up to and including index "to" will
/// be replaced by the new instructions entered.
/// This method cannot be called when in "insert" mode.
///
/// the index to start replacing instruction from
/// the last index of the instructions to be replaced
public void ReplaceInstruction(int from, int to)
{
if (inserting) throw new Exception("Cannot replace instructions during insert.");
currI = from;
if ((from < 0) || (from >= tide) || (to < 0))
throw new Exception("replace index is out of range");
if (to >= tide) to = tide - 1;
numReplace = to - from + 1;
StartInsert();
}
/*---------------- public instruction specific methods ------------------------*/
///
/// Add a simple IL instruction
///
/// the IL instruction
public void Inst(Op inst)
{
AddToBuffer(new Instr(inst));
}
///
/// Add an IL instruction with an integer parameter
///
/// the IL instruction
/// the integer parameter value
public void IntInst(IntOp inst, int val)
{
if ((inst == IntOp.ldc_i4_s) || (inst == IntOp.ldc_i4))
{
if ((val < 9) && (val >= -1))
{
AddToBuffer(new Instr((Op)((int)Op.ldc_i4_0 + val)));
}
else
{
AddToBuffer(new IntInstr(inst, val));
}
}
else
AddToBuffer(new UIntInstr(inst, (uint)val));
}
///
/// Add the load long instruction
///
/// the long value
public void ldc_i8(long cVal)
{
AddToBuffer(new LongInstr(SpecialOp.ldc_i8, cVal));
}
///
/// Add the load float32 instruction
///
/// the float value
public void ldc_r4(float cVal)
{
AddToBuffer(new FloatInstr(SpecialOp.ldc_r4, cVal));
}
///
/// Add the load float64 instruction
///
/// the float value
public void ldc_r8(double cVal)
{
AddToBuffer(new DoubleInstr(SpecialOp.ldc_r8, cVal));
}
///
/// Add the load string instruction
///
/// the string value
public void ldstr(string str)
{
AddToBuffer(new StringInstr(SpecialOp.ldstr, str));
}
///
/// Add the calli instruction
///
/// the signature for the calli
public void calli(CalliSig sig)
{
AddToBuffer(new SigInstr(SpecialOp.calli, sig));
}
///
/// Create a new CIL label. To place the label in the CIL instruction
/// stream use CodeLabel.
///
/// a new CIL label
public CILLabel NewLabel()
{
return new CILLabel();
}
///
/// Create a new label at this position in the code buffer
///
/// the label at the current position
public CILLabel NewCodedLabel()
{
CILLabel lab = new CILLabel();
lab.Buffer = this;
AddToBuffer(lab);
return lab;
}
///
/// Add a label to the CIL instructions
///
/// the label to be added
public void CodeLabel(CILLabel lab)
{
if (lab.Buffer == null)
{
lab.Buffer = this;
}
else if (lab.Buffer != this)
{
throw new DescriptorException("Cannot add a label to two different code buffers");
}
AddToBuffer(lab);
}
///
/// Add an instruction with a field parameter
///
/// the CIL instruction
/// the field parameter
public void FieldInst(FieldOp inst, Field f)
{
Debug.Assert(f != null);
if (f is FieldDef)
if (((FieldDef)f).GetScope() != thisMeth.GetScope())
throw new DescriptorException();
AddToBuffer(new FieldInstr(inst, f));
}
///
/// Add an instruction with a method parameter
///
/// the CIL instruction
/// the method parameter
public void MethInst(MethodOp inst, Method m)
{
Debug.Assert(m != null);
if (m is MethodDef)
if (((MethodDef)m).GetScope() != thisMeth.GetScope())
throw new DescriptorException();
AddToBuffer(new MethInstr(inst, m));
}
///
/// Add an instruction with a type parameter
///
/// the CIL instruction
/// the type argument for the CIL instruction
public void TypeInst(TypeOp inst, Type aType)
{
Debug.Assert(aType != null);
if (aType is ClassDef)
{
if (((ClassDef)aType).GetScope() != thisMeth.GetScope())
throw new DescriptorException();
}
AddToBuffer(new TypeInstr(inst, aType));
}
///
/// Add a branch instruction
///
/// the branch instruction
/// the label that is the target of the branch
public void Branch(BranchOp inst, CILLabel lab)
{
Debug.Assert(lab != null);
AddToBuffer(new BranchInstr(inst, lab));
}
///
/// Add a switch instruction
///
/// the target labels for the switch
public void Switch(CILLabel[] labs)
{
AddToBuffer(new SwitchInstr(labs));
}
///
/// Add a byte to the CIL instructions (.emitbyte)
///
///
public void emitbyte(byte bVal)
{
AddToBuffer(new CILByte(bVal));
}
///
/// Add an instruction which puts an integer on TOS. This method
/// selects the correct instruction based on the value of the integer.
///
/// the integer value
public void PushInt(int i)
{
if (i == -1)
{
AddToBuffer(new Instr(Op.ldc_i4_m1));
}
else if ((i >= 0) && (i <= 8))
{
Op op = (Op)(Op.ldc_i4_0 + i);
AddToBuffer(new Instr(op));
}
else if ((i >= minByteVal) && (i <= maxByteVal))
{
AddToBuffer(new IntInstr(IntOp.ldc_i4_s, i));
}
else
{
AddToBuffer(new IntInstr(IntOp.ldc_i4, i));
}
}
///
/// Add the instruction to load a long on TOS
///
/// the long value
public void PushLong(long l)
{
AddToBuffer(new LongInstr(SpecialOp.ldc_i8, l));
}
///
/// Add an instruction to push the boolean value true on TOS
///
public void PushTrue()
{
AddToBuffer(new Instr(Op.ldc_i4_1));
}
///
/// Add an instruction to push the boolean value false on TOS
///
public void PushFalse()
{
AddToBuffer(new Instr(Op.ldc_i4_0));
}
///
/// Add the instruction to load an argument on TOS. This method
/// selects the correct instruction based on the value of argNo
///
/// the number of the argument
public void LoadArg(int argNo)
{
if (argNo < 4)
{
Op op = (Op)Op.ldarg_0 + argNo;
AddToBuffer(new Instr(op));
}
else if (argNo <= maxUByteVal)
{
AddToBuffer(new UIntInstr(IntOp.ldarg_s, (uint)argNo));
}
else
{
AddToBuffer(new UIntInstr(IntOp.ldarg, (uint)argNo));
}
}
///
/// Add the instruction to load the address of an argument on TOS.
/// This method selects the correct instruction based on the value
/// of argNo.
///
/// the number of the argument
public void LoadArgAdr(int argNo)
{
if (argNo <= maxUByteVal)
{
AddToBuffer(new UIntInstr(IntOp.ldarga_s, (uint)argNo));
}
else
{
AddToBuffer(new UIntInstr(IntOp.ldarga, (uint)argNo));
}
}
///
/// Add the instruction to load a local on TOS. This method selects
/// the correct instruction based on the value of locNo.
///
/// the number of the local to load
public void LoadLocal(int locNo)
{
if (locNo < 4)
{
Op op = (Op)Op.ldloc_0 + locNo;
AddToBuffer(new Instr(op));
}
else if (locNo <= maxUByteVal)
{
AddToBuffer(new UIntInstr(IntOp.ldloc_s, (uint)locNo));
}
else
{
AddToBuffer(new UIntInstr(IntOp.ldloc, (uint)locNo));
}
}
///
/// Add the instruction to load the address of a local on TOS.
/// This method selects the correct instruction based on the
/// value of locNo.
///
/// the number of the local
public void LoadLocalAdr(int locNo)
{
if (locNo <= maxUByteVal)
{
AddToBuffer(new UIntInstr(IntOp.ldloca_s, (uint)locNo));
}
else
{
AddToBuffer(new UIntInstr(IntOp.ldloca, (uint)locNo));
}
}
///
/// Add the instruction to store to an argument. This method
/// selects the correct instruction based on the value of argNo.
///
/// the argument to be stored to
public void StoreArg(int argNo)
{
if (argNo <= maxUByteVal)
{
AddToBuffer(new UIntInstr(IntOp.starg_s, (uint)argNo));
}
else
{
AddToBuffer(new UIntInstr(IntOp.starg, (uint)argNo));
}
}
///
/// Add the instruction to store to a local. This method selects
/// the correct instruction based on the value of locNo.
///
/// the local to be stored to
public void StoreLocal(int locNo)
{
if (locNo < 4)
{
Op op = (Op)Op.stloc_0 + locNo;
AddToBuffer(new Instr(op));
}
else if (locNo <= maxUByteVal)
{
AddToBuffer(new UIntInstr(IntOp.stloc_s, (uint)locNo));
}
else
{
AddToBuffer(new UIntInstr(IntOp.stloc, (uint)locNo));
}
}
public void IntLine(int num)
{
Line((uint)num, 1);
}
///
/// CLS compliant version of Line()
///
/// The start line
/// The start column
/// The end line
/// The end column
public void IntLine(int sLin, int sCol, int eLin, int eCol)
{
Line((uint)sLin, (uint)sCol, (uint)eLin, (uint)eCol);
}
///
/// Create a new line instruction.
///
/// The line for the given code segment.
/// The starting column for the code segment.
public void Line(uint num, uint startCol)
{
if (this.DefaultSourceFile == null) throw new Exception("Method can only be used if DefaultSourceFile has been set.");
AddToBuffer(new Line(num, startCol, this.DefaultSourceFile));
}
///
/// Create a new line instruction.
///
/// The line for the given code segment.
/// The starting column for the code segment.
/// The ending column for the code segment.
public void Line(uint num, uint startCol, uint endCol)
{
if (this.DefaultSourceFile == null) throw new Exception("Method can only be used if DefaultSourceFile has been set.");
AddToBuffer(new Line(num, startCol, num, endCol, this.DefaultSourceFile));
}
///
/// Create a new line instruction.
///
/// The starting line for the code segment.
/// The starting column for the code segment.
/// The ending line for the code segment.
/// The ending column for the code segment.
public void Line(uint startNum, uint startCol, uint endNum, uint endCol)
{
if (this.DefaultSourceFile == null) throw new Exception("Method can only be used if DefaultSourceFile has bene set.");
AddToBuffer(new Line(startNum, startCol, endNum, endCol, this.DefaultSourceFile));
}
///
/// Create a new line instruction.
///
/// The starting line for the code segment.
/// The starting column for the code segment.
/// The ending line for the code segment.
/// The ending column for the code segment.
/// The source file for the given code segment.
public void Line(uint startNum, uint startCol, uint endNum, uint endCol, SourceFile sFile)
{
AddToBuffer(new Line(startNum, startCol, endNum, endCol, sFile));
}
///
/// The current scope.
///
public Scope CurrentScope
{
get { return currentScope; }
}
///
/// Open a new scope.
///
public void OpenScope()
{
currentScope = new Scope(currentScope, thisMeth);
AddToBuffer(new OpenScope(currentScope));
//Console.WriteLine("Open scope on " + currentScope._thisMeth.Name());
}
///
/// Close the current scope.
///
public void CloseScope()
{
//Console.WriteLine("Close scope on " + currentScope._thisMeth.Name());
AddToBuffer(new CloseScope(currentScope));
currentScope = currentScope._parent;
}
///
/// Bind a local to the CIL instructions.
///
/// The name of the local variable..
/// The index of the local variable.
/// The LocalBinding object created with the given values.
public LocalBinding BindLocal(string name, int index)
{
if (currentScope == null)
throw new Exception("Scope must be opened before locals can be bound.");
return currentScope.AddLocalBinding(name, index);
}
///
/// Bind a local to the CIL instructions.
///
/// The local variable to load.
/// The LocalBinding object created for the given Local object.
public LocalBinding BindLocal(Local local)
{
return BindLocal(local.Name, local.GetIndex());
}
///
/// Bind a constant to the CIL instructions.
///
/// The name of the constant.
/// The value of the constant.
/// The type of the constant.
/// Return the ConstantBinding created with the given values.
public ConstantBinding BindConstant(string name, object value, Type type)
{
if (currentScope == null)
throw new Exception("Scope must be opened before constants can be bound.");
return currentScope.AddConstantBinding(name, value, type);
}
///
/// Mark this position as the start of a new block
/// (try, catch, filter, finally or fault)
///
public void StartBlock()
{
if (blockStack == null) blockStack = new Stack();
blockStack.Push(NewCodedLabel());
}
///
/// Mark this position as the end of the last started block and
/// make it a try block. This try block is added to the current
/// instructions (ie do not need to call AddTryBlock)
///
/// The try block just ended
public TryBlock EndTryBlock()
{
TryBlock tBlock = new TryBlock((CILLabel)blockStack.Pop(), NewCodedLabel());
AddTryBlock(tBlock);
return tBlock;
}
///
/// Mark this position as the end of the last started block and
/// make it a catch block. This catch block is associated with the
/// specified try block.
///
/// the exception type to be caught
/// the try block associated with this catch block
public void EndCatchBlock(Class exceptType, TryBlock tryBlock)
{
Catch catchBlock = new Catch(exceptType, (CILLabel)blockStack.Pop(), NewCodedLabel());
tryBlock.AddHandler(catchBlock);
}
///
/// Mark this position as the end of the last started block and
/// make it a filter block. This filter block is associated with the
/// specified try block. The format is:
/// filterLab: ...
/// ...
/// filterHandler : ...
/// ...
///
/// the label where the filter code is
/// the try block associated with this filter block
public void EndFilterBlock(CILLabel filterLab, TryBlock tryBlock)
{
Filter filBlock = new Filter(filterLab, (CILLabel)blockStack.Pop(), NewCodedLabel());
tryBlock.AddHandler(filBlock);
}
///
/// Mark this position as the end of the last started block and
/// make it a finally block. This finally block is associated with the
/// specified try block.
///
/// the try block associated with this finally block
public void EndFinallyBlock(TryBlock tryBlock)
{
Finally finBlock = new Finally((CILLabel)blockStack.Pop(), NewCodedLabel());
tryBlock.AddHandler(finBlock);
}
///
/// Mark this position as the end of the last started block and
/// make it a fault block. This fault block is associated with the
/// specified try block.
///
/// the try block associated with this fault block
public void EndFaultBlock(TryBlock tryBlock)
{
Fault fBlock = new Fault((CILLabel)blockStack.Pop(), NewCodedLabel());
tryBlock.AddHandler(fBlock);
}
public void AddTryBlock(TryBlock tryBlock)
{
if (exceptions == null)
exceptions = new ArrayList();
else if (exceptions.Contains(tryBlock)) return;
exceptions.Add(tryBlock);
}
/*------------------------- private methods ----------------------------*/
private void AddToBuffer(CILInstruction inst)
{
if (tide >= buffer.Length)
{
CILInstruction[] tmp = buffer;
buffer = new CILInstruction[tmp.Length * 2];
for (int i = 0; i < tide; i++)
{
buffer[i] = tmp[i];
}
}
//Console.WriteLine("Adding instruction at offset " + offset + " with size " + inst.size);
//inst.offset = offset;
//offset += inst.size;
inst.index = (uint)tide;
buffer[tide++] = inst;
}
private void UpdateIndexesFrom(int ix)
{
for (int i = ix; i < tide; i++)
{
buffer[i].index = (uint)i;
}
}
private void InsertInstructions(int ix, CILInstruction[] newInsts, int numNew)
{
CILInstruction[] newBuff = buffer, oldBuff = buffer;
int newSize = tide + numNew - numReplace;
if (buffer.Length < newSize)
{
newBuff = new CILInstruction[newSize];
for (int i = 0; i < ix; i++)
{
newBuff[i] = oldBuff[i];
}
}
// shuffle up
int offset = numNew - numReplace;
int end = ix + numReplace;
for (int i = tide - 1; i >= end; i--)
{
newBuff[i + offset] = oldBuff[i];
}
// insert new instructions
for (int i = 0; i < numNew; i++)
{
newBuff[ix + i] = newInsts[i];
}
buffer = newBuff;
tide += numNew - numReplace;
UpdateIndexesFrom(ix);
}
internal bool IsEmpty()
{
return tide == 0;
}
internal static CILLabel GetLabel(ArrayList labs, uint targetOffset)
{
CILLabel lab;
int i = 0;
while ((i < labs.Count) && (((CILLabel)labs[i]).offset < targetOffset)) i++;
if (i < labs.Count)
{
if (((CILLabel)labs[i]).offset == targetOffset) // existing label
lab = (CILLabel)labs[i];
else
{
lab = new CILLabel(targetOffset);
labs.Insert(i, lab);
}
}
else
{
lab = new CILLabel(targetOffset);
labs.Add(lab);
}
return lab;
}
internal void AddEHClause(EHClause ehc)
{
if (exceptions == null)
exceptions = new ArrayList();
exceptions.Add(ehc);
}
internal void SetAndResolveInstructions(CILInstruction[] insts)
{
offset = 0;
ArrayList labels = new ArrayList();
for (int i = 0; i < insts.Length; i++)
{
insts[i].offset = offset;
offset += insts[i].size;
if (insts[i] is BranchInstr)
{
((BranchInstr)insts[i]).MakeTargetLabel(labels);
}
else if (insts[i] is SwitchInstr)
{
((SwitchInstr)insts[i]).MakeTargetLabels(labels);
}
}
if (exceptions != null)
{
for (int i = 0; i < exceptions.Count; i++)
{
exceptions[i] = ((EHClause)exceptions[i]).MakeTryBlock(labels);
}
}
if (labels.Count == 0) { buffer = insts; tide = buffer.Length; return; }
buffer = new CILInstruction[insts.Length + labels.Count];
int currentPos = 0;
tide = 0;
for (int i = 0; i < labels.Count; i++)
{
CILLabel lab = (CILLabel)labels[i];
while ((currentPos < insts.Length) && (insts[currentPos].offset < lab.offset))
buffer[tide++] = insts[currentPos++];
buffer[tide++] = lab;
}
while (currentPos < insts.Length)
{
buffer[tide++] = insts[currentPos++];
}
}
internal uint GetCodeSize()
{
return codeSize + paddingNeeded + exceptSize;
}
internal void BuildTables(MetaDataOut md)
{
for (int i = 0; i < tide; i++)
{
buffer[i].BuildTables(md);
}
if (exceptions != null)
{
for (int i = 0; i < exceptions.Count; i++)
{
((TryBlock)exceptions[i]).BuildTables(md);
}
}
}
internal void BuildCILInfo(CILWriter output)
{
for (int i = 0; i < tide; i++)
{
buffer[i].BuildCILInfo(output);
}
if (exceptions != null)
{
for (int i = 0; i < exceptions.Count; i++)
{
((TryBlock)exceptions[i]).BuildCILInfo(output);
}
}
}
internal void ChangeRefsToDefs(ClassDef newType, ClassDef[] oldTypes)
{
for (int i = 0; i < tide; i++)
{
if (buffer[i] is SigInstr)
{
CalliSig sig = ((SigInstr)buffer[i]).GetSig();
sig.ChangeRefsToDefs(newType, oldTypes);
}
else if (buffer[i] is TypeInstr)
{
TypeInstr tinst = (TypeInstr)buffer[i];
if (tinst.GetTypeArg() is ClassDef)
{
ClassDef iType = (ClassDef)tinst.GetTypeArg();
bool changed = false;
for (int j = 0; (j < oldTypes.Length) && !changed; j++)
{
if (iType == oldTypes[j])
tinst.SetTypeArg(newType);
}
}
}
}
}
internal void AddToLines(Line line)
{
if ((line.sourceFile == null) || (line.sourceFile.Match(defaultSourceFile)))
{
if (defaultLines == null)
{
if (defaultSourceFile == null)
throw new Exception("No Default Source File Set");
defaultLines = new ArrayList();
}
defaultLines.Add(line);
return;
}
if (sourceLines == null)
{
sourceLines = new ArrayList();
}
else
{
for (int i = 0; i < sourceLines.Count; i++)
{
ArrayList lineList = (ArrayList)sourceLines[i];
if (((Line)lineList[0]).sourceFile.Match(line.sourceFile))
{
lineList.Add(line);
return;
}
}
ArrayList newList = new ArrayList();
newList.Add(line);
sourceLines.Add(newList);
}
}
internal void CheckCode(uint locSigIx, bool initLocals, int maxStack, MetaDataOut metaData)
{
if (tide == 0) return;
offset = 0;
for (int i = 0; i < tide; i++)
{
buffer[i].offset = offset;
offset += buffer[i].size;
if (buffer[i] is Line)
AddToLines((Line)buffer[i]);
}
bool changed = true;
while (changed)
{
changed = false;
Line prevLine = null;
for (int i = 0; i < tide; i++)
{
if (buffer[i] is Line)
{
if (prevLine != null)
prevLine.CalcEnd((Line)buffer[i]);
prevLine = (Line)buffer[i];
}
changed = buffer[i].Check(metaData) || changed;
}
if (prevLine != null) prevLine.Last();
if (changed)
{
for (int i = 1; i < tide; i++)
{
buffer[i].offset = buffer[i - 1].offset + buffer[i - 1].size;
}
offset = buffer[tide - 1].offset + buffer[tide - 1].size;
}
}
codeSize = offset;
if (Diag.DiagOn) Console.WriteLine("codeSize before header added = " + codeSize);
if (maxStack == 0) this.maxStack = 8;
else this.maxStack = (short)maxStack;
if ((offset < smallSize) && (maxStack <= 8) && (locSigIx == 0) && (exceptions == null))
{
// can use tiny header
if (Diag.DiagOn) Console.WriteLine("Tiny Header");
tinyFormat = true;
headerFlags = (ushort)(TinyFormat | ((ushort)codeSize << 2));
codeSize++;
if ((codeSize % 4) != 0) { paddingNeeded = 4 - (codeSize % 4); }
}
else
{
if (Diag.DiagOn) Console.WriteLine("Fat Header");
tinyFormat = false;
localSigIx = locSigIx;
//this.maxStack = (short)maxStack;
headerFlags = FatFormatHeader;
if (exceptions != null)
{
// Console.WriteLine("Got exceptions");
headerFlags |= MoreSects;
uint numExceptClauses = 0;
for (int i = 0; i < exceptions.Count; i++)
{
TryBlock tryBlock = (TryBlock)exceptions[i];
tryBlock.SetSize();
numExceptClauses += (uint)tryBlock.NumHandlers();
if (tryBlock.isFat()) fatExceptionFormat = true;
}
if (numExceptClauses > MaxClauses) fatExceptionFormat = true;
if (Diag.DiagOn) Console.WriteLine("numexceptclauses = " + numExceptClauses);
if (fatExceptionFormat)
{
if (Diag.DiagOn) Console.WriteLine("Fat exception format");
exceptHeader = FatExceptTable;
exceptSize = ExHeaderSize + numExceptClauses * FatExClauseSize;
}
else
{
if (Diag.DiagOn) Console.WriteLine("Tiny exception format");
exceptHeader = SmlExceptTable;
exceptSize = ExHeaderSize + numExceptClauses * SmlExClauseSize;
}
if (Diag.DiagOn) Console.WriteLine("exceptSize = " + exceptSize);
}
if (initLocals) headerFlags |= InitLocals;
if ((offset % 4) != 0) { paddingNeeded = 4 - (offset % 4); }
codeSize += FatSize;
}
if (Diag.DiagOn)
Console.WriteLine("codeSize = " + codeSize + " headerFlags = " + Hex.Short(headerFlags));
}
///
/// Returns the maximum stack depth required by these CIL instructions.
///
/// The integer value of the stck depth.
public int GetMaxStackDepthRequired()
{
if (tide == 0) return 0;
// Store the code blocks we find
SCG.List codeBlocks = new SCG.List();
SCG.Dictionary cbTable = new SCG.Dictionary();
SCG.List extraStartingBlocks = new SCG.List();
// Start a default code block
CodeBlock codeBlock = new CodeBlock(this);
codeBlock.StartIndex = 0;
//
// Identify the code blocks
//
for (int i = 0; i < tide; i++)
{
/* Handling the tail instruction:
* The tail instruction has not been handled even though
* it indicates the end of a code block is coming. The
* reason for this is because any valid tail instruction
* must be followed by a call* instruction and then a ret
* instruction. Given a ret instruction must be the second
* next instruction anyway it has been decided to just let
* the end block be caught then.
*/
// If we reach a branch instruction or a switch instruction
// then end the current code block inclusive of the instruction.
if ((buffer[i] is BranchInstr) || (buffer[i] is SwitchInstr))
{
// Close the old block
codeBlock.EndIndex = i;
if (codeBlock.EndIndex >= codeBlock.StartIndex) // Don't add empty blocks
codeBlocks.Add(codeBlock);
// Open a new block
codeBlock = new CodeBlock(this);
codeBlock.StartIndex = i + 1;
// If we reach a label then we need to start a new
// code block as the label is an entry point.
}
else if (buffer[i] is CILLabel)
{
// Close the old block
codeBlock.EndIndex = i - 1;
if (codeBlock.EndIndex >= codeBlock.StartIndex) // Don't add empty blocks
codeBlocks.Add(codeBlock);
// Open a new block
codeBlock = new CodeBlock(this);
codeBlock.StartIndex = i;
// Set this label as the entry point for the code block
codeBlock.EntryLabel = (CILLabel)buffer[i];
// AND ... list in the dictionary.
cbTable.Add(codeBlock.EntryLabel, codeBlock);
// Check for the ret, throw, rethrow, or jmp instruction as they also end a block
}
else if (buffer[i] is Instr)
{
if (
(((Instr)buffer[i]).GetOp() == Op.ret) ||
(((Instr)buffer[i]).GetOp() == Op.throwOp) ||
(((Instr)buffer[i]).GetOp() == Op.rethrow) ||
((buffer[i] is MethInstr) && (((MethInstr)buffer[i]).GetMethodOp() == MethodOp.jmp))
)
{
// Close the old block
codeBlock.EndIndex = i;
if (codeBlock.EndIndex >= codeBlock.StartIndex) // Don't add empty blocks
codeBlocks.Add(codeBlock);
// Open a new block
// In theory this should never happen but just in case
// someone feels like adding dead code it is supported.
codeBlock = new CodeBlock(this);
codeBlock.StartIndex = i + 1;
}
}
}
// Close the last block
codeBlock.EndIndex = tide - 1;
if (codeBlock.EndIndex >= codeBlock.StartIndex) // Don't add empty blocks
codeBlocks.Add(codeBlock);
codeBlock = null;
// Check how many code blocks there are. If an blocks return 0.
if (codeBlocks.Count == 0) return 0;
//
// Loop through each code block and calculate the delta distance
//
for (int j = 0; j < codeBlocks.Count; j++)
{
CodeBlock block = codeBlocks[j];
int maxDepth = 0;
int currentDepth = 0;
// Loop through each instruction to work out the max depth
for (int i = block.StartIndex; i <= block.EndIndex; i++)
{
// Get the depth after the next instruction
currentDepth += buffer[i].GetDeltaDistance();
// If the new current depth is greater then the maxDepth adjust the maxDepth to reflect
if (currentDepth > maxDepth)
maxDepth = currentDepth;
}
// Set the depth of the block
block.MaxDepth = maxDepth;
block.DeltaDistance = currentDepth;
//
// Link up the next blocks
//
// If the block ends with a branch statement set the jump and fall through.
if (buffer[block.EndIndex] is BranchInstr)
{
BranchInstr branchInst = (BranchInstr)buffer[block.EndIndex];
// If this is not a "br" or "br.s" then set the fall through code block
if ((branchInst.GetBranchOp() != BranchOp.br) &&
(branchInst.GetBranchOp() != BranchOp.br_s))
// If there is a following code block set it as the fall through
if (j < (codeBlocks.Count - 1))
block.NextBlocks.Add(codeBlocks[j + 1]);
// Set the code block we are jumping to
CodeBlock cb = null;
cbTable.TryGetValue(branchInst.GetDest(), out cb);
if (cb == null)
throw new Exception("Missing Branch Label");
block.NextBlocks.Add(cb);
// If the block ends in a switch instruction work out the possible next blocks
}
else if (buffer[block.EndIndex] is SwitchInstr)
{
SwitchInstr switchInstr = (SwitchInstr)buffer[block.EndIndex];
// If there is a following code block set it as the fall through
if (j < (codeBlocks.Count - 1))
block.NextBlocks.Add(codeBlocks[j + 1]);
// Add each destination block
foreach (CILLabel label in switchInstr.GetDests())
{
// Check all of the code blocks to find the jump destination
CodeBlock cb = null;
cbTable.TryGetValue(label, out cb);
if (cb == null) throw new Exception("Missing Case Label");
block.NextBlocks.Add(cb);
}
// So long as the block doesn't end with a terminating instruction like ret or throw, just fall through to the next block
}
else if (!IsTerminatingInstruction(buffer[block.EndIndex]))
{
// If there is a following code block set it as the fall through
if (j < (codeBlocks.Count - 1))
block.NextBlocks.Add(codeBlocks[j + 1]);
}
}
//
// Join up any exception blocks
//
if (exceptions != null)
{
foreach (TryBlock tryBlock in exceptions)
{
// Try to find the code block where this try block starts
CodeBlock tryCodeBlock;
cbTable.TryGetValue(tryBlock.Start, out tryCodeBlock);
// Declare that the entry to this code block must be empty
tryCodeBlock.RequireEmptyEntry = true;
// Work with each of the handlers
foreach (HandlerBlock hb in tryBlock.GetHandlers())
{
// Find the code block where this handler block starts.
CodeBlock handlerCodeBlock;
cbTable.TryGetValue(hb.Start, out handlerCodeBlock);
// If the code block is a catch or filter block increment the delta
// distance by 1. This is to factor in the exception object that will
// be secretly placed on the stack by the runtime engine.
// However, this also means that the MaxDepth is up by one also!
if (hb is Catch || hb is Filter)
{
handlerCodeBlock.DeltaDistance++;
handlerCodeBlock.MaxDepth++;
}
// If the code block is a filter block increment the delta distance by 1
// This is to factor in the exception object that will be placed on the stack.
// if (hb is Filter) handlerCodeBlock.DeltaDistance++;
// Add this handler to the list of starting places
extraStartingBlocks.Add(handlerCodeBlock);
}
}
}
//
// Traverse the code blocks and get the depth
//
// Get the max depth at the starting entry point
int finalMaxDepth = this.TraverseMaxDepth(codeBlocks[0]);
// Check the additional entry points
// If the additional points have a greater depth update the max depth
foreach (CodeBlock cb in extraStartingBlocks)
{
// int tmpMaxDepth = cb.TraverseMaxDepth();
int tmpMaxDepth = this.TraverseMaxDepth(cb);
if (tmpMaxDepth > finalMaxDepth) finalMaxDepth = tmpMaxDepth;
}
// Return the max depth we have found
return finalMaxDepth;
}
int TraverseMaxDepth(CodeBlock entryBlock)
{
int max = 0;
SCG.Queue worklist = new SCG.Queue();
entryBlock.Visited = true;
entryBlock.LastVisitEntryDepth = 0;
worklist.Enqueue(entryBlock);
while (worklist.Count > 0)
{
int count = worklist.Count;
CodeBlock unit = worklist.Dequeue();
int maxDepth = unit.LastVisitEntryDepth + unit.MaxDepth;
int exitDepth = unit.LastVisitEntryDepth + unit.DeltaDistance;
if (maxDepth > max) max = maxDepth;
foreach (CodeBlock succ in unit.NextBlocks)
{
if (succ.Visited)
{
if (succ.LastVisitEntryDepth != exitDepth)
throw new InvalidStackDepth("inconsistent stack depth at offset " + succ.StartIndex.ToString());
}
else
{
succ.Visited = true;
succ.LastVisitEntryDepth = exitDepth;
worklist.Enqueue(succ);
}
}
}
return max;
}
private bool IsTerminatingInstruction(CILInstruction cilInstr)
{
// Return or throw instructions are terminating instructions
if (cilInstr is Instr)
{
if (((Instr)cilInstr).GetOp() == Op.ret) return true;
if (((Instr)cilInstr).GetOp() == Op.throwOp) return true;
if (((Instr)cilInstr).GetOp() == Op.rethrow) return true;
}
// jmp is a terminating instruction
if (cilInstr is MethInstr)
{
if (((MethInstr)cilInstr).GetMethodOp() == MethodOp.jmp) return true;
}
return false;
}
internal void Write(PEWriter output)
{
if (Diag.DiagOn) Console.WriteLine("Writing header flags = " + Hex.Short(headerFlags));
if (tinyFormat)
{
if (Diag.DiagOn) Console.WriteLine("Writing tiny code");
output.Write((byte)headerFlags);
}
else
{
if (Diag.DiagOn) Console.WriteLine("Writing fat code");
output.Write(headerFlags);
output.Write((ushort)maxStack);
output.Write(offset);
output.Write(localSigIx);
}
if (Diag.DiagOn)
{
Console.WriteLine(Hex.Int(tide) + " CIL instructions");
Console.WriteLine("starting instructions at " + output.Seek(0, SeekOrigin.Current));
}
// Added to enable PDB generation
if (output.pdbWriter != null)
{
// Open the method
output.pdbWriter.OpenMethod((int)thisMeth.Token());
// Check if this is the entry point method
if (thisMeth.HasEntryPoint()) output.pdbWriter.SetEntryPoint((int)thisMeth.Token());
}
// Write out each memember of the buffer
for (int i = 0; i < tide; i++)
{
buffer[i].Write(output);
}
// Added to enable PDB generation
if (output.pdbWriter != null && tide > 0)
{
output.pdbWriter.CloseMethod();
}
if (Diag.DiagOn) Console.WriteLine("ending instructions at " + output.Seek(0, SeekOrigin.Current));
for (int i = 0; i < paddingNeeded; i++) { output.Write((byte)0); }
if (exceptions != null)
{
// Console.WriteLine("Writing exceptions");
// Console.WriteLine("header = " + Hex.Short(exceptHeader) + " exceptSize = " + Hex.Int(exceptSize));
output.Write(exceptHeader);
output.Write3Bytes((uint)exceptSize);
for (int i = 0; i < exceptions.Count; i++)
{
TryBlock tryBlock = (TryBlock)exceptions[i];
tryBlock.Write(output, fatExceptionFormat);
}
}
}
internal void Write(CILWriter output)
{
for (int i = 0; i < tide; i++)
{
if (!(buffer[i] is CILLabel))
{
output.Write(" ");
}
output.Write(" ");
buffer[i].Write(output);
}
if (exceptions != null)
{
throw new NotYetImplementedException("Exceptions not yet implemented for CIL Instructions");
// Console.WriteLine("Writing exceptions");
// Console.WriteLine("header = " + Hex.Short(exceptHeader) + " exceptSize = " + Hex.Int(exceptSize));
//output.Write(exceptHeader);
//output.Write3Bytes((uint)exceptSize);
//for (int i = 0; i < exceptions.Count; i++) {
// TryBlock tryBlock = (TryBlock)exceptions[i];
// tryBlock.Write(output, fatExceptionFormat);
//}
}
}
///
/// Stores the details of a given code block
///
private class CodeBlock
{
internal int StartIndex;
internal int EndIndex;
internal int DeltaDistance;
internal int MaxDepth;
internal CILLabel EntryLabel;
internal ArrayList NextBlocks = new ArrayList(); // List of CodeBlock objects
// internal int Visits;
internal int LastVisitEntryDepth;
internal bool RequireEmptyEntry;
internal bool Visited = false;
private CILInstructions cilInstr;
///
/// Create a new code block definition
///
/// The buffer the code block relates to
internal CodeBlock(CILInstructions instructions)
{
cilInstr = instructions;
}
}
}
/**************************************************************************/
///
/// Descriptor for an IL instruction
///
public abstract class CILInstruction
{
protected static readonly sbyte maxByteVal = 127;
protected static readonly sbyte minByteVal = -128;
protected static readonly byte leadByte = 0xFE;
protected static readonly uint USHeapIndex = 0x70000000;
protected static readonly uint longInstrStart = (uint)Op.arglist;
protected static readonly string[] opcode = {
"nop", "break", "ldarg.0", "ldarg.1", "ldarg.2", "ldarg.3", "ldloc.0", "ldloc.1",
"ldloc.2", "ldloc.3", "stloc.0", "stloc.1", "stloc.2", "stloc.3", "ldarg.s", "ldarga.s",
"starg.s", "ldloc.s", "ldloca.s","stloc.s", "ldnull", "ldc.i4.m1","ldc.i4.0","ldc.i4.1",
"ldc.i4.2","ldc.i4.3","ldc.i4.4","ldc.i4.5","ldc.i4.6","ldc.i4.7","ldc.i4.8","ldc.i4.s",
"ldc.i4", "ldc.i8", "ldc.r4", "ldc.r8", "ERROR", "dup", "pop", "jmp",
"call", "calli", "ret", "br.s", "brfalse.s","brtrue.s","beq.s", "bge.s",
"bgt.s", "ble.s", "blt.s", "bne.un.s","bge.un.s","bgt.un.s","ble.un.s","blt.un.s",
"br", "brfalse", "brtrue", "beq", "bge", "bgt", "ble", "blt",
"bne.un", "bge.un", "bgt.un", "ble.un", "blt.un", "switch", "ldind.i1","ldind.u1",
"ldind.i2","ldind.u2","ldind.i4","ldind.u4","ldind.i8","ldind.i", "ldind.r4","ldind.r8",
"ldind.ref","stind.ref","stind.i1","stind.i2","stind.i4","stind.i8","stind.r4","stind.r8",
"add", "sub", "mul", "div", "div.un", "rem", "rem.un", "and",
"or", "xor", "shl", "shr", "shr.un", "neg", "not", "conv.i1",
"conv.i2", "conv.i4", "conv.i8", "conv.r4", "conv.r8", "conv.u4", "conv.u8", "callvirt",
"cpobj", "ldobj", "ldstr", "newobj", "castclass","isinst", "conv.r.un","ERROR",
"ERROR", "unbox", "throw", "ldfld", "ldflda", "stfld", "ldsfld", "ldsflda",
"stsfld", "stobj", "conv.ovf.i1.un", "conv.ovf.i2.un",
"conv.ovf.i4.un", "conv.ovf.i8.un", "conv.ovf.u1.un", "conv.ovf.u2.un",
"conv.ovf.u4.un", "conv.ovf.u8.un", "conv.ovf.i.un", "conv.ovf.u.un",
"box", "newarr", "ldlen", "ldelema",
"ldelem.i1", "ldelem.u1", "ldelem.i2", "ldelem.u2",
"ldelem.i4", "ldelem.u4", "ldelem.i8", "ldelem.i",
"ldelem.r4", "ldelem.r8", "ldelem.ref", "stelem.i",
"stelem.i1", "stelem.i2", "stelem.i4", "stelem.i8",
"stelem.r4", "stelem.r8", "stelem.ref", "ERROR",
"ERROR", "ERROR", "ERROR", "ERROR",
"ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR",
"ERROR", "ERROR", "ERROR", "conv.ovf.i1",
"conv.ovf.u1", "conv.ovf.i2", "conv.ovf.u2", "conv.ovf.i4",
"conv.ovf.u4", "conv.ovf.i8", "conv.ovf.u8", "ERROR",
"ERROR", "ERROR", "ERROR", "ERROR",
"ERROR", "ERROR", "refanyval","ckfinite","ERROR", "ERROR", "mkrefany","ERROR",
"ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR",
"ldtoken","conv.u2","conv.u1","conv.i","conv.ovf.i","conv.ovf.u","add.ovf","add.ovf.un",
"mul.ovf","mul.ovf.un","sub.ovf","sub.ovf.un","endfinally","leave","leave.s","stind.i",
"conv.u"};
protected static readonly int[] opDeltaDistance = {
0 /* nop */, 0 /* break */, 1 /* ldarg.0 */, 1 /* ldarg.1 */, 1 /* ldarg.2 */, 1 /* ldarg.3 */, 1 /* ldloc.0 */, 1 /* ldloc.1 */,
1 /* ldloc.2 */, 1 /* ldloc.3 */, -1 /* stloc.0 */, -1 /* stloc.1 */, -1 /* stloc.2 */, -1 /* stloc.3 */, 1 /* ldarg.s */, 1 /* ldarga.s */,
-1 /* starg.s */, 1 /* ldloc.s */, 1 /* ldloca.s */, -1 /* stloc.s */, 1 /* ldnull */, 1 /* ldc.i4.m1 */, 1 /* ldc.i4.0 */, 1 /* ldc.i4.1 */,
1 /* ldc.i4.2 */, 1 /* ldc.i4.3 */, 1 /* ldc.i4.4 */, 1 /* ldc.i4.5 */, 1 /* ldc.i4.6 */, 1 /* ldc.i4.7 */, 1 /* ldc.i4.8 */, 1 /* ldc.i4.s */,
1 /* ldc.i4 */, 1 /* ldc.i8 */, 1 /* ldc.r4 */, 1 /* ldc.r8 */, -99 /* ERROR */, 1 /* dup */, -1 /* pop */, 0 /* jmp */,
-99 /* call */, -99 /* calli */, 0 /* ret */, 0 /* br.s */, -1 /* brfalse.s */,-1 /* brtrue.s */, -2 /* beq.s */, -2 /* bge.s */,
-2 /* bgt.s */, -2 /* ble.s */, -2 /* blt.s */, -2 /* bne.un.s */, -2 /* bge.un.s */, -2 /* bgt.un.s */, -2 /* ble.un.s */, -2 /* blt.un.s */,
0 /* br */, -1 /* brfalse */, -1 /* brtrue */, -2 /* beq */, -2 /* bge */, -2 /* bgt */, -2 /* ble */, -2 /* blt */,
-2 /* bne.un */, -2 /* bge.un */, -2 /* bgt.un */, -2 /* ble.un */, -2 /* blt.un */, -1 /* switch */, 0 /* ldind.i1 */, 0 /* ldind.u1 */,
0 /* ldind.i2 */, 0 /* ldind.u2 */, 0 /* ldind.i4 */, 0 /* ldind.u4 */, 0 /* ldind.i8 */, 0 /* ldind.i */, 0 /* ldind.r4 */, 0 /* ldind.r8 */,
0 /* ldind.ref */, -2 /* stind.ref */, -2 /* stind.i1 */, -2 /* stind.i2 */, -2 /* stind.i4 */, -2 /* stind.i8 */, -2 /* stind.r4 */, -2 /* stind.r8 */,
-1 /* add */, -1 /* sub */, -1 /* mul */, -1 /* div */, -1 /* div.un */, -1 /* rem */, -1 /* rem.un */, -1 /* and */,
-1 /* or */, -1 /* xor */, -1 /* shl */, -1 /* shr */, -1 /* shr.un */, 0 /* neg */, 0 /* not */, 0 /* conv.i1 */,
0 /* conv.i2 */, 0 /* conv.i4 */, 0 /* conv.i8 */, 0 /* conv.r4 */, 0 /* conv.r8 */, 0 /* conv.u4 */, 0 /* conv.u8 */, -99 /* callvirt */,
-2 /* cpobj */, 0 /* ldobj */, 1 /* ldstr */, -99 /* newobj */, 0 /* castclass */, 0 /* isinst */, 0 /* conv.r.un */, -99 /* ERROR */,
-99 /* ERROR */, 0 /* unbox */, -1 /* throw */, 0 /* ldfld */, 0 /* ldflda */, -2 /* stfld */, 1 /* ldsfld */, 1 /* ldsflda */,
-1 /* stsfld */, -2 /* stobj */, 0 /* conv.ovf.i1.un */, 0 /* conv.ovf.i2.un */,
0 /* conv.ovf.i4.un */, 0 /* conv.ovf.i8.un */, 0 /* conv.ovf.u1.un */, 0 /* conv.ovf.u2.un */,
0 /* conv.ovf.u4.un */, 0 /* conv.ovf.u8.un */, 0 /* conv.ovf.i.un */, 0 /* conv.ovf.u.un */,
0 /* box */, 0 /* newarr */, 0 /* ldlen */, -1 /* ldelema */,
-1 /* ldelem.i1 */, -1 /* ldelem.u1 */, -1 /* ldelem.i2 */, -1 /* ldelem.u2 */,
-1 /* ldelem.i4 */, -1 /* ldelem.u4 */, -1 /* ldelem.i8 */, -1 /* ldelem.i */,
-1 /* ldelem.r4 */, -1 /* ldelem.r8 */, -1 /* ldelem.ref */, -3 /* stelem.i */,
-3 /* stelem.i1 */, -3 /* stelem.i2 */, -3 /* stelem.i4 */, -3 /* stelem.i8 */,
-3 /* stelem.r4 */, -3 /* stelem.r8 */, -3 /* stelem.ref */, -99 /* ERROR */,
-99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */,
-99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */,
-99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, 0 /* conv.ovf.i1 */,
0 /* conv.ovf.u1 */, 0 /* conv.ovf.i2 */, 0 /* conv.ovf.u2 */, 0 /* conv.ovf.i4 */,
0 /* conv.ovf.u4 */, 0 /* conv.ovf.i8 */, 0 /* conv.ovf.u8 */, -99 /* ERROR */,
-99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */,
-99 /* ERROR */, -99 /* ERROR */, 0 /* refanyval */, 0 /* ckfinite */, -99 /* ERROR */, -99 /* ERROR */, 0 /* mkrefany */, -99 /* ERROR */,
-99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */, -99 /* ERROR */,
1 /* ldtoken */, 0 /* conv.u2 */, 0 /* conv.u1 */, 0 /* conv.i */, 0 /* conv.ovf.i */,0 /* conv.ovf.u */,-1 /* add.ovf */, -1 /* add.ovf.un */,
-1 /* mul.ovf */, -1 /* mul.ovf.un */, -1 /* sub.ovf */, -1 /* sub.ovf.un */, 0 /* endfinally */,0 /* leave */, 0 /* leave.s */, -2 /* stind.i */,
0 /* conv.u */};
///
/// A list of the delta distances for the given CIL instructions.
///
protected static readonly string[] FEopcode = {
"arglist", "ceq", "cgt", "cgt.un", "clt", "clt.un", "ldftn", "ldvirtftn",
"ERROR", "ldarg", "ldarga", "starg", "ldloc", "ldloca", "stloc", "localloc",
"ERROR", "endfilter", "unaligned", "volatile", "tail", "initobj", "ERROR", "cpblk",
"initblk", "ERROR", "rethrow", "ERROR", "sizeof", "refanytype", "readonly"};
///
/// A list of the delta distances for the given FE CIL instructions.
///
protected static readonly int[] FEopDeltaDistance = {
1 /* arglist */, -1 /* ceq */, -1 /* cgt */, -1 /* cgt.un */, -1 /* clt */, -1 /* clt.un */, 1 /* ldftn */, 0 /* ldvirtftn */,
-99 /* ERROR */, 1 /* ldarg */, 1 /* ldarga */, -1 /* starg */, 1 /* ldloc */, 1 /* ldloca */, -1 /* stloc */, 0 /* localloc */,
-99 /* ERROR */, -1 /* endfilter */, 0 /* unaligned */, 0 /* volatile */, 0 /* tail */, -1 /* initobj */, -99 /* ERROR */, -3 /* cpblk */,
-3 /* initblk */, -99 /* ERROR */, 0 /* rethrow */, -99 /* ERROR */, 1 /* sizeof */, 0 /* refanytype */, 0 /* readonly */};
internal bool twoByteInstr = false;
internal uint size = 1;
internal uint offset, index;
internal virtual bool Check(MetaDataOut md)
{
return false;
}
internal virtual void Resolve() { }
public int GetPos() { return (int)index; }
internal abstract string GetInstName();
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal abstract int GetDeltaDistance();
internal virtual void BuildTables(MetaDataOut md) { }
internal virtual void BuildCILInfo(CILWriter output) { }
internal virtual void Write(PEWriter output) { }
internal virtual void Write(CILWriter output) { }
}
/**************************************************************************/
public class CILByte : CILInstruction
{
byte byteVal;
/*-------------------- Constructors ---------------------------------*/
internal CILByte(byte bVal)
{
byteVal = bVal;
}
public byte GetByte() { return byteVal; }
internal override string GetInstName()
{
return Hex.Byte(byteVal);
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// Zero, the delta distance for a CILByte
internal override int GetDeltaDistance()
{
return 0;
}
internal override void Write(PEWriter output)
{
output.Write(byteVal);
}
internal override void Write(CILWriter output)
{
output.WriteLine(".emitbyte " + Hex.Byte(byteVal)); // ???? CHECK THIS ????
}
}
/**************************************************************************/
public class Instr : CILInstruction
{
protected uint instr;
/*-------------------- Constructors ---------------------------------*/
public Instr(Op inst)
{
instr = (uint)inst;
if (instr >= longInstrStart)
{
instr -= longInstrStart;
twoByteInstr = true;
size++;
}
}
internal Instr(uint inst)
{
instr = (uint)inst;
if (instr >= longInstrStart)
{
instr -= longInstrStart;
twoByteInstr = true;
size++;
}
}
public Op GetOp()
{
if (twoByteInstr)
return (Op)(longInstrStart + instr);
return (Op)instr;
}
internal override string GetInstName()
{
Op opInst = GetOp();
return "" + opInst;
}
internal override void Write(PEWriter output)
{
//Console.WriteLine("Writing instruction " + instr + " with size " + size);
if (twoByteInstr) output.Write(leadByte);
output.Write((byte)instr);
}
internal string GetInstrString()
{
if (twoByteInstr)
{
return FEopcode[instr] + " ";
}
else
{
return opcode[instr] + " ";
}
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
if (twoByteInstr)
{
return FEopDeltaDistance[instr];
}
else
{
return opDeltaDistance[instr];
}
}
internal override void Write(CILWriter output)
{
if (twoByteInstr)
{
output.WriteLine(FEopcode[instr]);
}
else
{
output.WriteLine(opcode[instr]);
}
}
}
/**************************************************************************/
public class IntInstr : Instr
{
int val;
bool byteNum;
/*-------------------- Constructors ---------------------------------*/
public IntInstr(IntOp inst, int num)
: base((uint)inst)
{
byteNum = inst == IntOp.ldc_i4_s;
val = num;
if (byteNum) size++;
else size += 4;
}
public int GetInt() { return val; }
public void SetInt(int num) { val = num; }
internal sealed override void Write(PEWriter output)
{
base.Write(output);
if (byteNum)
output.Write((sbyte)val);
else
output.Write(val);
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
return opDeltaDistance[instr];
}
internal override void Write(CILWriter output)
{
output.WriteLine(opcode[instr] + " " + val);
}
}
/**************************************************************************/
public class UIntInstr : Instr
{
uint val;
bool byteNum;
/*-------------------- Constructors ---------------------------------*/
public UIntInstr(IntOp inst, uint num)
: base((uint)inst)
{
byteNum = (inst < IntOp.ldc_i4_s) || (inst == IntOp.unaligned);
val = num;
if (byteNum) size++;
else size += 2;
}
public uint GetUInt() { return val; }
public void SetUInt(uint num) { val = num; }
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
if (twoByteInstr)
{
return FEopDeltaDistance[instr];
}
else
{
return opDeltaDistance[instr];
}
}
internal sealed override void Write(PEWriter output)
{
base.Write(output);
if (byteNum)
output.Write((byte)val);
else
output.Write((ushort)val);
}
internal override void Write(CILWriter output)
{
if (twoByteInstr)
{
output.Write(FEopcode[instr]);
}
else
{
output.Write(opcode[instr]);
}
output.WriteLine(" " + val);
}
}
/**************************************************************************/
public class LongInstr : Instr
{
long val;
/*-------------------- Constructors ---------------------------------*/
public LongInstr(SpecialOp inst, long l)
: base((uint)inst)
{
val = l;
size += 8;
}
public long GetLong() { return val; }
public void SetLong(long num) { val = num; }
internal sealed override void Write(PEWriter output)
{
base.Write(output);
output.Write(val);
}
internal override void Write(CILWriter output)
{
output.WriteLine("ldc.i8 " + val);
}
}
/**************************************************************************/
public class FloatInstr : Instr
{
float fVal;
/*-------------------- Constructors ---------------------------------*/
public FloatInstr(SpecialOp inst, float f)
: base((uint)inst)
{
fVal = f;
size += 4;
}
public float GetFloat() { return fVal; }
public void SetFloat(float num) { fVal = num; }
internal sealed override void Write(PEWriter output)
{
output.Write((byte)0x22);
output.Write(fVal);
}
internal override void Write(CILWriter output)
{
output.WriteLine("ldc.r4 " + fVal);
}
}
/**************************************************************************/
public class DoubleInstr : Instr
{
double val;
/*-------------------- Constructors ---------------------------------*/
public DoubleInstr(SpecialOp inst, double d)
: base((uint)inst)
{
val = d;
size += 8;
}
public double GetDouble() { return val; }
public void SetDouble(double num) { val = num; }
internal sealed override void Write(PEWriter output)
{
base.Write(output);
output.Write(val);
}
internal override void Write(CILWriter output)
{
output.WriteLine("ldc.r8 " + val);
}
}
/**************************************************************************/
public class StringInstr : Instr
{
string val;
uint strIndex;
/*-------------------- Constructors ---------------------------------*/
public StringInstr(SpecialOp inst, string str)
: base((uint)inst)
{
val = str;
size += 4;
}
public string GetString() { return val; }
public void SetString(string str) { val = str; }
internal sealed override void BuildTables(MetaDataOut md)
{
if (Diag.DiagOn) Console.WriteLine("Adding a code string to the US heap");
strIndex = md.AddToUSHeap(val);
}
internal sealed override void Write(PEWriter output)
{
base.Write(output);
output.Write(USHeapIndex | strIndex);
}
internal override void Write(CILWriter output)
{
output.WriteLine("ldstr \"" + val + "\"");
}
}
/**************************************************************************/
public class CILLabel : CILInstruction
{
private static int labelNum = 0;
private int num = -1;
private CILInstructions buffer;
/*-------------------- Constructors ---------------------------------*/
public CILLabel()
{
size = 0;
}
internal CILLabel(uint offs)
{
size = 0;
offset = offs;
}
internal uint GetLabelOffset()
{
return offset;
}
internal override string GetInstName()
{
return "Label" + num;
}
internal CILInstructions Buffer
{
get { return buffer; }
set { buffer = value; }
}
internal override void BuildCILInfo(CILWriter output)
{
if (num == -1)
{
num = labelNum++;
}
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
return 0;
}
internal override void Write(CILWriter output)
{
output.WriteLine("Label" + num + ":");
}
}
/**************************************************************************/
///
/// Abstract model for debug instructions.
///
public abstract class DebugInst : CILInstruction { }
/**************************************************************************/
///
/// Defines a line instruction.
///
public class Line : DebugInst
{
private static uint MaxCol = 100;
uint startLine, startCol, endLine, endCol;
bool hasEnd = false;
internal SourceFile sourceFile;
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new line instruction.
///
/// Start of the line in the source file.
/// Starting column in the source file.
/// The filename of the souce file.
internal Line(uint sLine, uint sCol, SourceFile sFile)
{
startLine = sLine;
startCol = sCol;
sourceFile = sFile;
size = 0;
}
///
/// Create a new line instruction.
///
/// Start of the line in the source file.
/// Starting column in the source file.
/// Ending line in the source file.
/// Ending column in the source file.
/// The filename of the souce file.
internal Line(uint sLine, uint sCol, uint eLine, uint eCol, SourceFile sFile)
{
startLine = sLine;
startCol = sCol;
endLine = eLine;
endCol = eCol;
hasEnd = true;
sourceFile = sFile;
size = 0;
}
public int LineNum
{
get { return (int)startLine; }
}
internal void CalcEnd(Line next)
{
if (hasEnd) return;
if (sourceFile != next.sourceFile)
{
endLine = startLine;
endCol = MaxCol;
}
else
{
endLine = next.startLine;
endCol = next.startCol;
if (endCol < 0) endCol = MaxCol;
}
hasEnd = true;
}
internal void Last()
{
if (hasEnd) return;
endLine = startLine;
endCol = MaxCol;
hasEnd = true;
}
///
/// Get the name of this instruction.
///
/// A string with the value ".line".
internal override string GetInstName()
{
return ".line";
}
///
/// Write this instruction to a PDB file.
///
/// The PE writer being used to write the PE and PDB files.
internal override void Write(PEWriter output)
{
string sf = "";
Guid doclang = Guid.Empty;
Guid docvend = Guid.Empty;
Guid doctype = Guid.Empty;
if (sourceFile != null)
{
sf = sourceFile.name;
doclang = sourceFile.language;
docvend = sourceFile.vendor;
doctype = sourceFile.document;
}
if (output.pdbWriter != null)
output.pdbWriter.AddSequencePoint(sf, doclang, docvend, doctype, (int)offset,
(int)startLine, (int)startCol, (int)endLine, (int)endCol);
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
return 0;
}
///
/// Write out a line instruction to the CIL file.
///
/// The CIL instruction writer to use to write this instruction.
internal override void Write(CILWriter output)
{
if (output.Debug)
{
string lineDetails = startLine + ", " + startCol;
if (hasEnd)
{
lineDetails += ", " + endLine + ", " + endCol;
if (sourceFile != null)
{
lineDetails += ", " + sourceFile.Name;
}
}
output.WriteLine(".line " + lineDetails);
}
}
}
/**************************************************************************/
///
/// A local binding instruction that can be added to a list of CILInstructions.
///
public class LocalBinding : DebugInst
{
internal int _index;
internal string _name;
internal DebugLocalSig _debugsig;
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new local binding object.
///
/// The index of the local in the locals tables.
/// The name of the local.
internal LocalBinding(int index, string name)
{
_index = index;
_name = name;
}
///
/// The index of the local in the locals table.
///
public int Index
{
get { return _index; }
}
///
/// The name of the local binding.
///
public string Name
{
get { return _name; }
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
return 0;
}
///
/// Get the name of this instruction.
///
/// A string with the name of this instruction.
internal override string GetInstName()
{
return "debug - local binding";
}
}
/**************************************************************************/
///
/// Used to delcare constants that exist in a given scope.
///
public class ConstantBinding : DebugInst
{
private string _name;
private object _value;
private Type _type;
private uint _token;
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new constant binding.
///
/// The name of the constant.
/// The value of the constant.
/// The data type of the constant.
internal ConstantBinding(string name, object value, Type type, uint token)
{
_value = value;
_name = name;
_type = type;
_token = token;
}
///
/// Value of the constant.
///
public object Value
{
get { return _value; }
}
///
/// The name of the constant.
///
public string Name
{
get { return _name; }
}
///
/// The data type of the constant.
///
public Type Type
{
get { return _type; }
}
///
/// The token for this constant.
///
public uint Token
{
get { return _token; }
}
///
/// Get the type signature for this constant.
///
/// A byte array of the type signature.
public byte[] GetSig()
{
MemoryStream str = new MemoryStream();
_type.TypeSig(str);
return str.ToArray();
}
///
/// Get the name of this instruction.
///
/// A string with the name of this instruction.
internal override string GetInstName()
{
return "debug - constant binding";
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance()
{
return 0;
}
}
/**************************************************************************/
public class SwitchInstr : Instr
{
CILLabel[] cases;
uint numCases = 0;
int[] targets;
/*-------------------- Constructors ---------------------------------*/
public SwitchInstr(CILLabel[] dsts)
: base(0x45)
{
cases = dsts;
if (cases != null) numCases = (uint)cases.Length;
size += 4 + (numCases * 4);
}
internal SwitchInstr(int[] offsets)
: base(0x45)
{
numCases = (uint)offsets.Length;
targets = offsets;
size += 4 + (numCases * 4);
}
public CILLabel[] GetDests() { return cases; }
public void SetDests(CILLabel[] dests) { cases = dests; }
internal override string GetInstName()
{
return "switch";
}
internal void MakeTargetLabels(ArrayList labs)
{
cases = new CILLabel[numCases];
for (int i = 0; i < numCases; i++)
{
cases[i] = CILInstructions.GetLabel(labs, (uint)(offset + size + targets[i]));
}
}
internal sealed override void Write(PEWriter output)
{
base.Write(output);
output.Write(numCases);
for (int i = 0; i < numCases; i++)
{
int target = (int)cases[i].GetLabelOffset() - (int)(offset + size);
output.Write(target);
}
}
internal override void Write(CILWriter output)
{
throw new NotImplementedException("Switch instruction for CIL");
}
}
public class Scope {
private ArrayList _localBindings = new ArrayList();
private ArrayList _constantBindings = new ArrayList();
internal Scope _parent;
internal MethodDef _thisMeth;
internal Scope(MethodDef thisMeth) : this(null, thisMeth) {
}
internal Scope(Scope parent, MethodDef thisMeth) {
_thisMeth = thisMeth;
_parent = parent;
}
///
/// Add a constant to this scope.
///
/// The name of the constant.
/// The value of the constant.
/// The type of the constant.
/// The ConstantBinding object for the new constant.
internal ConstantBinding AddConstantBinding(string name, object value, Type type) {
ConstantBinding binding;
if ((binding = FindConstantBinding(name)) != null)
return binding;
binding = new ConstantBinding(name, value, type, _thisMeth.locToken);
_constantBindings.Add(binding);
return binding;
}
///
/// Find a constant in this scope.
///
/// The name of the constant.
/// The ConstantBinding object of this constant.
internal ConstantBinding FindConstantBinding(string name) {
foreach (ConstantBinding binding in _constantBindings)
if (binding.Name == name)
return binding;
return null;
}
///
/// Provide a complete list of all constants bound in this scope.
///
public ConstantBinding[] ConstantBindings {
get { return (ConstantBinding[])_constantBindings.ToArray(typeof(ConstantBinding)); }
}
internal LocalBinding AddLocalBinding(string name, int index) {
LocalBinding binding;
if ((binding = FindLocalBinding(name)) != null)
return binding;
binding = new LocalBinding(index, name);
_localBindings.Add(binding);
return binding;
}
internal LocalBinding FindLocalBinding(string name) {
foreach (LocalBinding binding in _localBindings)
if (binding._name == name)
return binding;
return null;
}
internal LocalBinding FindLocalBinding(int index) {
foreach (LocalBinding binding in _localBindings)
if (binding._index == index)
return binding;
return null;
}
public LocalBinding[] LocalBindings {
get { return (LocalBinding[]) _localBindings.ToArray(typeof(LocalBinding)); }
}
internal void BuildSignatures(MetaDataOut md) {
if (!md.Debug) return;
try {
Local[] locals = _thisMeth.GetLocals();
foreach (LocalBinding binding in _localBindings) {
if (binding._debugsig == null) {
locals[binding._index].BuildTables(md);
binding._debugsig = md.GetDebugSig(locals[binding._index]);
}
binding._debugsig.BuildMDTables(md);
}
} catch (Exception e) {
throw new Exception("Exception while writing debug info for: " +
this._thisMeth.NameString()+"\r\n"+e.ToString());
}
}
internal void WriteLocals(PDBWriter writer) {
try {
Local[] locals = _thisMeth.GetLocals();
foreach (LocalBinding binding in _localBindings) {
writer.BindLocal(binding._name, binding._index, _thisMeth.locToken,0,0);
}
} catch (Exception e) {
throw new Exception("Exception while writing debug info for: " +
this._thisMeth.NameString()+"\r\n"+e.ToString(),e);
}
}
/* Constants does not work. AKB 2007-02-03
internal void WriteConstants(PDBWriter writer) {
try {
// Add each constant to the current scope
foreach (ConstantBinding binding in _constantBindings)
writer.BindConstant(binding);
} catch (Exception e) {
throw new Exception("Exception while writing debug info for: " +
this._thisMeth.NameString() + "\r\n" + e.ToString(), e);
}
}
*/
}
/*************************************************************************/
///
/// A marker instruction for when a scope should be opened in the sequence of instructions.
///
public class OpenScope : DebugInst {
internal Scope _scope;
///
/// Create a new OpenScope instruction.
///
/// The scope that is being opened.
public OpenScope(Scope scope) {
size = 0;
_scope = scope;
}
///
/// Get the name for this instruction.
///
/// A string with the name of the instruction.
internal override string GetInstName() {
return "debug - open scope";
}
///
/// Build the signatures for this instruction.
///
/// The meta data table to write the instructions to.
internal void BuildSignatures(MetaDataOut md) {
_scope.BuildSignatures(md);
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
///
internal override int GetDeltaDistance() {
return 0;
}
///
/// Write this instruction to the PDB file.
///
/// The PEWriter being used to write the PE and PDB files.
internal override void Write(PEWriter output) {
if (output.pdbWriter != null) {
output.pdbWriter.OpenScope((int) offset);
_scope.WriteLocals(output.pdbWriter);
/* Constants do not work. AKB 2007-02-03
* _scope.WriteConstants(output.pdbWriter);
*/
}
}
}
/************************************************************************/
///
/// A marker instruction for when a scope should be closed.
///
public class CloseScope : DebugInst {
internal Scope _scope;
///
/// The constructor to build a new CloseScope instruction.
///
/// The scope to close.
public CloseScope(Scope scope) {
size = 0;
_scope = scope;
}
///
/// Provide access to the name of this instruction.
///
/// A string containing the name of this instruction.
internal override string GetInstName() {
return "debug - close scope";
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance() {
return 0;
}
///
/// Write this instruction. This instruction does not get written
/// to the PE file. It only applys to the PDB file.
///
/// The PEWriter that is writing the PE file.
internal override void Write(PEWriter output) {
if (output.pdbWriter != null)
output.pdbWriter.CloseScope((int) offset);
}
}
/**************************************************************************/
public class FieldInstr : Instr {
Field field;
/*-------------------- Constructors ---------------------------------*/
public FieldInstr(FieldOp inst, Field f) : base((uint)inst) {
field = f;
size += 4;
}
public Field GetField() { return field; }
public void SetField(Field fld) { field = fld; }
internal override string GetInstName() {
return "" + (FieldOp)instr;
}
internal sealed override void BuildTables(MetaDataOut md) {
if (field == null) throw new InstructionException(IType.fieldOp,instr);
if (field is FieldRef) field.BuildMDTables(md);
}
internal override void BuildCILInfo(CILWriter output) {
if (field == null) throw new InstructionException(IType.fieldOp,instr);
if (field is FieldRef) field.BuildCILInfo(output);
}
internal sealed override void Write(PEWriter output) {
base.Write(output);
output.Write(field.Token());
}
internal override void Write(CILWriter output) {
output.Write(GetInstrString());
field.WriteType(output);
output.WriteLine();
}
}
/**************************************************************************/
public class MethInstr : Instr {
Method meth;
/*-------------------- Constructors ---------------------------------*/
public MethInstr(MethodOp inst, Method m) : base((uint)inst) {
meth = m;
size += 4;
}
public Method GetMethod() { return meth; }
public void SetMethod(Method mth) { meth = mth; }
internal override string GetInstName() {
return "" + (MethodOp)instr;
}
internal sealed override void BuildTables(MetaDataOut md) {
if (meth == null)
throw new InstructionException(IType.methOp,instr);
if ((meth is MethodRef) || (meth is MethodSpec)) meth.BuildMDTables(md);
}
internal override void BuildCILInfo(CILWriter output) {
if (meth == null) throw new InstructionException(IType.methOp,instr);
if ((meth is MethodRef) || (meth is MethodSpec)) meth.BuildCILInfo(output);
}
///
/// Get the MethodOp this instruction represents.
///
/// The method operator from the MethodOp enum.
public MethodOp GetMethodOp() {
return (MethodOp)instr;
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance() {
switch ((MethodOp)instr) {
case MethodOp.callvirt:
case MethodOp.call: {
// Add the parameter count to the depth
int depth = (int)meth.GetSig().numPars * -1;
// Check to see if this is an instance method
if (meth.GetSig().HasCallConv(CallConv.Instance)) depth--;
// Check to see if this method uses the optional parameters
if (meth.GetSig().HasCallConv(CallConv.Vararg)) depth += (int)meth.GetSig().numOptPars * -1;
// Check to see if this method uses the generic parameters
if (meth.GetSig().HasCallConv(CallConv.Generic)) depth += (int)meth.GetSig().numGenPars * -1;
// Check if a return value will be placed on the stack.
if (!meth.GetRetType().SameType(PrimitiveType.Void)) depth++;
return depth;
}
case MethodOp.newobj: {
// Add the parameter count to the depth
int depth = (int)meth.GetSig().numPars * -1;
// Check to see if this method uses the optional parameters
if (meth.GetSig().HasCallConv(CallConv.Vararg)) depth += (int)meth.GetSig().numOptPars * -1;
// Check to see if this method uses the generic parameters
if (meth.GetSig().HasCallConv(CallConv.Generic)) depth += (int)meth.GetSig().numGenPars * -1;
// Add the object reference that is loaded onto the stack
depth++;
return depth;
}
case MethodOp.ldtoken:
case MethodOp.ldftn:
return 1;
case MethodOp.jmp:
case MethodOp.ldvirtfn:
return 0;
default:
// Someone has added a new MethodOp and not added a case for it here.
throw new Exception("The MethodOp for this MethoInstr is not supported.");
}
}
internal sealed override void Write(PEWriter output) {
base.Write(output);
output.Write(meth.Token());
}
internal override void Write(CILWriter output) {
output.Write(GetInstrString());
meth.WriteType(output);
output.WriteLine();
}
}
/**************************************************************************/
public class SigInstr : Instr {
CalliSig signature;
/*-------------------- Constructors ---------------------------------*/
public SigInstr(SpecialOp inst, CalliSig sig) : base((uint)inst) {
signature = sig;
size += 4;
}
public CalliSig GetSig() { return signature; }
public void SetSig(CalliSig sig) { signature = sig; }
internal override string GetInstName() {
return "" + (SpecialOp)instr;
}
///
/// Get the delta distance for this instruction.
///
///
/// The delta distance is the resulting difference of items
/// left on the stack after calling this instruction.
///
/// An integer value representing the delta distance.
internal override int GetDeltaDistance() {
// Add the parameter count to the depth
int depth = (int)signature.NumPars * -1;
// Check to see if this is an instance method
if (signature.HasCallConv(CallConv.Instance)) depth--;
// Check to see if this method uses the optional parameters
if (signature.HasCallConv(CallConv.Vararg)) depth += (int)signature.NumOptPars * -1;
// Check if a return value will be placed on the stack.
if (signature.ReturnType.SameType(PrimitiveType.Void)) depth++;
return depth;
}
internal sealed override void BuildTables(MetaDataOut md) {
if (signature == null) throw new InstructionException(IType.specialOp,instr);
signature.BuildMDTables(md);
}
internal override void BuildCILInfo(CILWriter output) {
if (signature == null) throw new InstructionException(IType.specialOp,instr);
signature.BuildCILInfo(output);
}
internal sealed override void Write(PEWriter output) {
base.Write(output);
output.Write(signature.Token());
}
internal override void Write(CILWriter output) {
output.Write(GetInstrString());
signature.Write(output);
output.WriteLine();
}
}
/**************************************************************************/
public class TypeInstr : Instr {
Type theType;
/*-------------------- Constructors ---------------------------------*/
public TypeInstr(TypeOp inst, Type aType) : base((uint)inst) {
theType = aType;
size += 4;
}
public Type GetTypeArg() { return theType; }
public void SetTypeArg(Type ty) { theType = ty; }
internal override string GetInstName() {
return "" + (TypeOp)instr;
}
internal sealed override void BuildTables(MetaDataOut md) {
if (theType == null) throw new InstructionException(IType.typeOp,instr);
theType = theType.AddTypeSpec(md);
}
internal override void BuildCILInfo(CILWriter output) {
if (theType == null) throw new InstructionException(IType.typeOp,instr);
if (!theType.isDef()) {
theType.BuildCILInfo(output);
}
}
internal sealed override void Write(PEWriter output) {
base.Write(output);
output.Write(theType.Token());
}
internal override void Write(CILWriter output) {
output.Write(GetInstrString());
theType.WriteName(output);
output.WriteLine();
}
}
/**************************************************************************/
public class BranchInstr : Instr {
CILLabel dest;
private bool shortVer = true;
private static readonly byte longInstrOffset = 13;
private int target = 0;
/*-------------------- Constructors ---------------------------------*/
public BranchInstr(BranchOp inst, CILLabel dst) : base((uint)inst) {
dest = dst;
shortVer = (inst < BranchOp.br) || (inst == BranchOp.leave_s);
if (shortVer)
size++;
else
size += 4;
}
internal BranchInstr(uint inst, int dst) : base(inst) {
target = dst;
shortVer = (inst < (uint)BranchOp.br) || (inst == (uint)BranchOp.leave_s);
if (shortVer)
size++;
else
size += 4;
}
public CILLabel GetDest() { return dest; }
public void SetDest(CILLabel lab) { dest = lab; }
///
/// Provide access to the branch operator
///
/// The branch operator from the BranchOp enum that this instruction represents.
public BranchOp GetBranchOp() {
return (BranchOp)instr;
}
internal override string GetInstName() {
return "" + (BranchOp)instr;
}
internal void MakeTargetLabel(ArrayList labs) {
uint targetOffset = (uint)(offset + size + target);
dest = CILInstructions.GetLabel(labs,targetOffset);
}
internal sealed override bool Check(MetaDataOut md) {
target = (int)dest.GetLabelOffset() - (int)(offset + size);
if ((target < minByteVal) || (target > maxByteVal)) { // check for longver
if (shortVer) {
if (instr == (uint)BranchOp.leave_s)
instr = (uint)BranchOp.leave;
else
instr = instr += longInstrOffset;
size += 3;
shortVer = false;
return true;
}
} else if (!shortVer) { // check for short ver
if (instr == (uint)BranchOp.leave)
instr = (uint)BranchOp.leave_s;
else
instr = instr -= longInstrOffset;
size -= 3;
shortVer = true;
return true;
}
/*
if (shortVer && ((target < minByteVal) || (target > maxByteVal))) {
if (instr < (int)BranchOp.leave) instr += longInstrOffset;
else instr--;
shortVer = false;
size += 3;
return true;
}
*/
return false;
}
internal sealed override void Write(PEWriter output) {
base.Write(output);
if (shortVer)
output.Write((sbyte)target);
else
output.Write(target);
}
internal override void Write(CILWriter output) {
output.WriteLine(GetInstrString() + dest.GetInstName());
}
}
/*************************************************************************/
}