/* * 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()); } } /*************************************************************************/ }