/*
The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language
governing rights and limitations under the License.
The Original Code is RAIL(Runtime Assembly Instrumentation Library) Alpha Version.
The Initial Developer of the Original Code is University of Coimbra,
Computer Science Department, Dependable Systems Group. Copyright (C) University of Coimbra.
All Rights Reserved.
*/
using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
using Rail.Reflect;
namespace Rail.MSIL{
/// <summary>
/// Represents an instruction that targets other instructions
/// (branches and swithces)
/// </summary>
public interface ITargeter
{
/// <summary>
/// If the given instruction is a target of this instruction.
/// </summary>
/// <param name="ins"></param>
/// <returns></returns>
bool ContainsTarget(Instruction ins);
// TODO: Not sure if it will be needed.
}
/// <summary>
/// Represents an MSIL instruction, i.e., an opcode and its operand.
/// </summary>
public abstract class Instruction
{
/// <summary>
///
/// </summary>
internal int index;
/// <summary>
/// The OpCode associated with the instruction
/// </summary>
readonly protected OpCode op;
/// <summary>
/// The instructions that target this one.
/// </summary>
/// <remarks>
/// Most instructions will not be targeted by others. Therefore, in
/// order to save space, this arraylist is only initialized when
/// a target is added.
/// </remarks>
protected IList targeters;
/// <summary>
///
/// </summary>
/// <param name="op"></param>
protected Instruction(OpCode op)
{
this.op = op;
}
/// <summary>
/// Gets or sets the opcode of this instruction
/// </summary>
public OpCode OpCode
{
get
{
return op;
}
}
//PS_Change
/// <summary>
/// Gets the index of this instruction relative to the beginning of the method
/// </summary>
public int Index
{
get
{
return index;
}
}
//End of PS_Change
#region Targeting...
/// <summary>
/// Checks if this instruction is a target of another.
/// </summary>
/// <param name="ins"></param>
/// <returns></returns>
public bool IsTargetOf(ITargeter ins)
{
if (targeters == null)
{
return false;
}
else
{
return targeters.Contains(ins);
}
}
/// <summary>
/// Returns the list of instructions that target this instruction.
/// </summary>
/// <remarks>
/// If this instruction has no targeters, null is returned.
/// Otherwise, a enumerator of ITargeter instances is returned.
/// </remarks>
/// <returns></returns>
public IEnumerator GetTargeters()
{
if (targeters == null)
{
return null;
}
else
{
return targeters.GetEnumerator();
}
}
/// <summary>
/// Adds a new targeter to this instruction.
/// </summary>
/// <remarks>
/// If the given instruction is already a targeter of this one,
/// this method returns silently.
/// </remarks>
/// <param name="ins"></param>
public void AddTargeter(ITargeter ins)
{
if (targeters == null)
{
targeters = new ArrayList();
}
else if (targeters.Contains(ins))
{
// Already present
return;
}
targeters.Add(ins);
}
/// <summary>
/// Removes the given targeter
/// </summary>
/// <remarks>
/// If the given instruction is not a targeter, the method returns silently.
/// </remarks>
/// <param name="ins"></param>
internal void RemoveTargeter(Instruction ins)
{
if (targeters != null)
{
targeters.Remove(ins);
}
}
#endregion
/// <summary>
/// Resolves the opcode for a better replacement.
/// </summary>
/// <returns>
/// If shorter, faster or other form of the current opcode exist and it is possible
/// to replace such under circumstances it is returned, otherwise the current
/// opcode is returned.
/// </returns>
/// <remarks>
/// This implementation just returns the current opcode. It is up to subtypes
/// to override this method.
/// </remarks>
protected virtual OpCode ResolveOpcode()
{
return this.op;
}
/// <summary>
/// Returns a string representation of this instruction.
/// </summary>
/// <returns></returns>
public override String ToString()
{
// A textual representation, like: "ldc 25"
return String.Format("{0,-3} {1,-12} pop:{2,-20} push:{3,-20}",new Object[] {this.index,this.op.Name,this.op.StackBehaviourPop,this.op.StackBehaviourPush });
}
/// <summary>
/// Writes the instruction
/// </summary>
/// <param name="wCtx"></param>
internal virtual void Write(WriteContext wCtx) {
if (targeters != null && targeters.Count > 0) {
// This instruction is tageted by another one. We must mark a label
Label l;
if (wCtx.labels.Contains(this)) {
l = (Label)wCtx.labels[this];
}
else {
l = wCtx.ig.DefineLabel();
wCtx.labels.Add(this, l);
}
// The label must be marked before emiting the instruction that it refers to
if (!wCtx.markedLabels.Contains(this)) {
wCtx.ig.MarkLabel(l);
wCtx.markedLabels.Add(this,l);
}
}
}
/// <summary>
/// Look at all the Instructions that target orig, and change them to point to replace.
/// (Supports retargeting ILBranches and ILSwitches).
/// Also see TransferExceptions
/// </summary>
/// <param name="orig">The original instruction</param>
/// <param name="replace">The new instruction that will replace orig</param>
public static void TransferTargeters (Instruction orig, Instruction replace) {
// KIRK: Go through all of orig's targeters, and change to replace
IEnumerator iEnum = orig.GetTargeters();
if (iEnum!=null) {
while(iEnum.MoveNext()) {
replace.AddTargeter((ITargeter)iEnum.Current);
if (iEnum.Current is ILBranch) {
((ILBranch)iEnum.Current).Target = replace;
} else {
ILSwitch sw = iEnum.Current as ILSwitch;
if (sw != null) {
Instruction[] switchTargets = sw.Targets;
for (int k = 0; k < switchTargets.Length; k++) {
switchTargets[k] = replace;
}
sw.Targets = switchTargets;
}
}
}
}
}
/// <summary>
/// Change start and finish pointers from within the ExceptionBlock to point at replace instead of orig.
/// Also see TransferTargeters
/// </summary>
/// <param name="et">The ExceptionTable to traverse</param>
/// <param name="orig">The original instruction</param>
/// <param name="replace">The new instruction that will replace orig</param>
public static void TransferExceptions (ExceptionTable et, Instruction orig, Instruction replace) {
// KIRK: Go through the ExceptionTable, and replace all references of orig with replace
if (et != null) {
foreach (ExceptionBlock eb in et.excpBlocks) {
if (eb.start == orig) {
eb.start = replace;
}
if (eb.end == orig) {
eb.end = replace;
}
if (eb.FaultHandler != null) {
if (eb.FaultHandler.start == orig) {
eb.FaultHandler.start = replace;
}
if (eb.FaultHandler.end == orig) {
eb.FaultHandler.end = replace;
}
}
if (eb.FinallyHandler != null) {
if (eb.FinallyHandler.start == orig) {
eb.FinallyHandler.start = replace;
}
if (eb.FinallyHandler.end == orig) {
eb.FinallyHandler.end = replace;
}
}
for (int j = 0; j < eb.GetCatchHandlerCount(); j++) {
ExceptionHandler ch = eb.GetCatchHandler(j);
if (ch.start == orig) {
ch.start = replace;
}
if (ch.end == orig) {
ch.end = replace;
}
}
}
}
}
}
/// <summary>
/// A "virtual" instruction that is inserted by MSIL.Code.Write to represent a BeginExceptionBlock,
/// BeginCatchBlock, BeginFinallyBlock, BeginFaultBlock, BeginFilterBlock, EndExceptionBlock
/// (to hopefully simplify Instruction.Write)
/// </summary>
public class ILVirtualException : Instruction {
private ExceptionBlock _eb;
private ExceptionHandlerType _handlerType = 0;
private RType _exceptionType = null;
private bool _beginBlock = false;
private bool _endBlock = false;
/// <summary>
/// Create a BeginExceptionBlock (if true), or EndExceptionBlock (if false)
/// </summary>
/// <param name="beginBlock"></param>
public ILVirtualException(ExceptionBlock eb, bool beginBlock) : base(OpCodes.Nop) {
_eb = eb;
if (beginBlock) {
_beginBlock = true;
} else {
_endBlock = true;
}
}
/// <summary>
/// Create a Fault, Filter or Finally block
/// </summary>
/// <param name="handlerType"></param>
public ILVirtualException(ExceptionBlock eb, ExceptionHandlerType handlerType) : base(OpCodes.Nop) {
_eb = eb;
_handlerType = handlerType;
}
/// <summary>
/// Create a CatchBlock
/// </summary>
/// <param name="handlerType"></param>
/// <param name="exceptionType"></param>
public ILVirtualException(ExceptionBlock eb, ExceptionHandlerType handlerType, RType exceptionType) : base(OpCodes.Nop) {
_eb = eb;
_handlerType = handlerType;
_exceptionType = exceptionType;
}
public int LastInstructionIndex {
get {
return _eb.LastInstruction();
}
}
public bool WillEmitLeave() {
// All the EndBlah blocks will emit a leave, except for BeginExceptionBlock
// (and sometimes EndExceptionBlock, if the previous handler was a Finally or Fault, but I'll
// assume that there won't be leaves in those)
return (! _beginBlock);
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx) {
base.Write(wCtx);
//return;
if (_beginBlock) {
wCtx.ig.BeginExceptionBlock();
} else if (_endBlock) {
wCtx.ig.EndExceptionBlock();
} else if (_handlerType == ExceptionHandlerType.Catch) {
wCtx.ig.BeginCatchBlock(wCtx.tResolver.ResolveRType(_exceptionType));
} else if (_handlerType == ExceptionHandlerType.Fault) {
wCtx.ig.BeginFaultBlock();
} else if (_handlerType == ExceptionHandlerType.Filter) {
wCtx.ig.BeginExceptFilterBlock();
} else if (_handlerType == ExceptionHandlerType.Finally) {
wCtx.ig.BeginFinallyBlock();
} else {
throw new ArgumentException("Invalid ILVirtualException type");
}
}
public override String ToString() {
string ret = this.Index.ToString();
if (_beginBlock) {
ret += " BeginExceptionBlock";
} else if (_endBlock) {
ret += " EndExceptionBlock";
} else if (_handlerType == ExceptionHandlerType.Catch) {
ret += " BeginCatchBlock " + _exceptionType.FullName;
} else if (_handlerType == ExceptionHandlerType.Fault) {
ret += " BeginFaultBlock";
} else if (_handlerType == ExceptionHandlerType.Filter) {
ret += " BeginExceptFilterBlock";
} else if (_handlerType == ExceptionHandlerType.Finally) {
ret += " BeginFinallyBlock";
}
return ret;
}
}
#region Instruction implementation
/// <summary>
/// Represents instructions that take no operands.
/// </summary>
/// <remarks>
/// <p>This class can only be used with one of the following OpCodes:</p>
/// <code>
/// add, add.ovf, add.ovf.un, and, arglist, break, ceq, cgt, cgt.un, ckfinite,
/// clt, clt.un, conv.i, conv.i1, conv.i2, conv.i4, conv.i8, conv.ovf.i,
/// conv.ovf.i.un, conv.ovf.i1, conv.ovf.i1.un, conv.ovf.i2, conv.ovf.i2.un,
/// conv.ovf.i4,
/// conv.ovf.i4.un, conv.ovf.i8, conv.ovf.i8.un, conv.ovf.u, conv.ovf.u.un,
/// conv.ovf.u1, conv.ovf.u1.un, conv.ovf.u2, conv.ovf.u2.un, conv.ovf.u4,
/// conv.ovf.u4.un, conv.ovf.u8, conv.ovf.u8.un, conv.r.un, conv.r4,
/// conv.r8, conv.u, conv.u1, conv.u2, conv.u4, conv.u8, cpblk, div, div.un,
/// dup, endfault, endfilter, endfinally, initblk, 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.M1, ldelem.i, ldelem.i1,
/// ldelem.i2, ldelem.i4, ldelem.i8, ldelem.r4, ldelem.r8, ldelem.ref,
/// ldelem.u1, ldelem.u2, ldelem.u4, ldind.i, ldind.i1, ldind.i2, ldind.i4,
/// ldind.i8, ldind.r4, ldind.r8, ldind.ref, ldind.u1, ldind.u2, ldind.u4,
/// ldlen, ldnull, localloc, mul, mul.ovf, mul.ovf.un, neg, nop, not, or,
/// pop, refanytype, rem, rem.un, ret, rethrow, shl, shr, shr.un, stelem.i,
/// stelem.i1, stelem.i2, stelem.i4, stelem.i8,
/// stelem.r4, stelem.r8, stelem.ref, stind.i, stind.i1, stind.i2, stind.i4,
/// stind.i8, stind.r4, stind.r8, stind.ref, , sub, sub.ovf, sub.ovf.un, tail.,
/// throw, volatile., xor
/// </code>
/// </remarks>
public class ILNone : Instruction
{
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">If the opcode's operand type is not
/// <code>OperandType.InlineNone</code>
/// </exception>
/// <param name="op">The opcode for this instruction.</param>
public ILNone(OpCode op) : base(op)
{
if (!OpCodesClassifier.NO_ARGS_SET.Contains(op))
{
throw new ArgumentException("Invalid opcode: " + op);
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
if (op.Value!=OpCodes.Endfinally.Value)
{
base.Write(wCtx);
wCtx.ig.Emit(op);
}
else
{
//TODO:verify this
// if (this.GetTargeters()!=null &&
// this.GetTargeters().MoveNext())
// wCtx.ig.MarkLabel((Label)wCtx.labels[this]);
base.Write(wCtx);
}
}
}
/// <summary>
/// Represents branch instructions.
/// </summary>
/// <remarks>
/// <p>OpCode must be one of:</p>
/// <code>beq, beq.s, bge, bge.s, bge.un, bge.un.s, bgt, bgt.s, bgt.un,
/// bgt.un.s, ble, ble.s, ble.un, ble.un.s, blt, blt.s, blt.un, blt.un.s,
/// bne.un, bne.un.s, br, br.s, brfalse, brfalse.s, brtrue, brtrue.s,
/// leave, leave.s</code>
/// </remarks>
public class ILBranch : Instruction, ITargeter
{
/* TODO: Targets are represented as instances of Instruction, therefore
* loosing the information of the offset, but keeping the information
* about the original form (short or normal). When saving, the offset has
* to be calculated and might be different from the original one. Sometimes,
* the short for of the branch instruction might no longer be appropriate.
* Solutions:
* 1. Always issue the normal form.
* 2. Always issue the shortest form that is acceptable.
* 3. Always issue the original form, unless the target no longer fits
* in the short form.
*
* I think the last form is the best. If an assembly is loaded and saved
* without being manipulated, the last solution would guarante that the IL
* is preserved. We can build optimizers that do the transformation by
* request.
*/
private Instruction target;
/// <summary>
/// Creates a new branch instruction, with the given target instruction
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not
/// one of <code>OperandType.InlineBrTarget</code> or
/// <code>OperandType.ShortInlineBrTarget</code>
/// </exception>
/// <param name="op">The branch opcode.</param>
/// <param name="target">The target instruction</param>
public ILBranch(OpCode op, Instruction target) : base(op)
{
if (op.OperandType != OperandType.InlineBrTarget &&
op.OperandType != OperandType.ShortInlineBrTarget)
{
throw new ArgumentException("Expecting a branch opcode. Found: " + op);
}
this.Target = target;
}
/// <summary>
/// The Instruction of branch destination
/// </summary>
public Instruction Target
{
get
{
return target;
}
set
{
this.target = value;
if (null != this.target)
this.target.AddTargeter(this);
}
}
/// <summary>
/// Resolves the length of the opcode.
/// </summary>
/// <returns>A shorter form of the opcode if exitant and possible, otherwise inparam opcode.</returns>
/// <remarks>
/// This method examines the relative length to the target instruction to determine if
/// short form of the brNN code can be used instead.
/// </remarks>
protected override OpCode ResolveOpcode()
{
int iRel = target.index - base.index;
if (iRel > -129 && iRel < 128)
{
//Short form allowed.
if (OpCodes.Br.Equals(base.op))
return OpCodes.Br_S;
else
if (OpCodes.Brfalse.Equals(base.op))
return OpCodes.Brfalse_S;
else
if (OpCodes.Brtrue.Equals(base.op))
return OpCodes.Brtrue_S;
else
if (OpCodes.Beq.Equals(base.op))
return OpCodes.Beq_S;
else
if (OpCodes.Bne_Un.Equals(base.op))
return OpCodes.Bne_Un_S;
else
if (OpCodes.Bge.Equals(base.op))
return OpCodes.Bge_S;
else
if (OpCodes.Bge_Un.Equals(base.op))
return OpCodes.Bge_Un_S;
else
if (OpCodes.Bgt.Equals(base.op))
return OpCodes.Bgt_S;
else
if (OpCodes.Bgt_Un.Equals(base.op))
return OpCodes.Bgt_Un_S;
else
if (OpCodes.Ble.Equals(base.op))
return OpCodes.Ble_S;
else
if (OpCodes.Ble_Un.Equals(base.op))
return OpCodes.Ble_Un_S;
else
if (OpCodes.Blt.Equals(base.op))
return OpCodes.Blt_S;
else
if (OpCodes.Blt_Un.Equals(base.op))
return OpCodes.Blt_Un_S;
}
return base.op;
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
// Must obtain a label for the target instruction
Label l;
if (wCtx.labels.Contains(target))
{
l = (Label)wCtx.labels[target];
}
else
{
// There was no previous label for that instruction.
// Create a new one and adds it
l = wCtx.ig.DefineLabel();
wCtx.labels.Add(target, l);
}
// Emits the instruction
if (op.Value == OpCodes.Leave.Value || op.Value == OpCodes.Leave_S.Value)
if (wCtx.IsExBlockEnd)
return;
wCtx.ig.Emit(this.ResolveOpcode(), l);
}
/// <summary>
/// Verifies if the instruction has a matching target
/// </summary>
/// <param name="ins">The instruction to verify</param>
/// <returns>True if the target matches ins</returns>
public bool ContainsTarget(Instruction ins)
{
return target == ins;
}
}
/// <summary>
/// An instruction taking an Int16 as operand.
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldc.i4.s, unaligned.</code>
/// </remarks>
public class ILOperandI2 : Instruction
{
/// <summary>
///
/// </summary>
private Int16 operand;
/// <summary>
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.ShortInlineI</code>
/// </exception>
/// <param name="op"></param>
/// <param name="operand"></param>
public ILOperandI2(OpCode op, Int16 operand) : base(op)
{
if (op.OperandType != OperandType.ShortInlineI)
{
throw new ArgumentException("Expecting an opcode with an Int16 as operand. Found: " + op);
}
//BC_CHANGE
this.operand = operand;
}
/// <summary>
/// Gets or sets the operand of this instruction.
/// </summary>
public Int16 Operand
{
get
{
return operand;
}
set
{
this.operand = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
//BC_CHANGE
wCtx.ig.Emit(op, (sbyte)operand);
}
}
/// <summary>
/// An instruction taking an Int32 as operand.
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldc.i4</code>
/// </remarks>
public class ILOperandI4 : Instruction
{
/// <summary>
///
/// </summary>
private Int32 operand;
/// <summary>
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineI</code>
/// </exception>
/// <param name="op"></param>
/// <param name="operand"></param>
public ILOperandI4(OpCode op, Int32 operand) : base(op)
{
if (op.OperandType != OperandType.InlineI)
{
throw new ArgumentException("Expecting an opcode with an Int32 as operand. Found: " + op);
}
//BC_CHANGE
this.operand = operand;
}
/// <summary>
/// Gets or sets the operand of the instruction
/// </summary>
public Int32 Operand
{
get
{
return operand;
}
set
{
this.operand = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
wCtx.ig.Emit(op, operand);
}
}
/// <summary>
/// An instruction taking an Int64 as operand
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldc.i8</code>
/// </remarks>
public class ILOperandI8 : Instruction
{
/// <summary>
///
/// </summary>
private Int64 operand;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineI8</code>
/// </exception>
public ILOperandI8(OpCode op, Int64 operand) : base(op)
{
if (op.OperandType != OperandType.InlineI8)
{
throw new ArgumentException("Expecting an opcode with an Int64 as operand. Found: " + op);
}
this.operand = operand;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public Int64 Operand
{
get
{
return operand;
}
set
{
this.operand = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
wCtx.ig.Emit(op, operand);
}
}
/// <summary>
/// An instruction taking Single (R4) as operand
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldc.r4</code>
/// </remarks>
public class ILOperandR4 : Instruction
{
/// <summary>
///
/// </summary>
private Single operand;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.ShortInlineR</code>
/// </exception>
public ILOperandR4(OpCode op, Single operand) : base(op)
{
if (op.OperandType != OperandType.ShortInlineR)
{
throw new ArgumentException("Expecting an opcode with an R4 as operand. Found: " + op);
}
this.operand = operand;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public Single Operand
{
get
{
return operand;
}
set
{
this.operand = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
wCtx.ig.Emit(op, operand);
}
}
/// <summary>
/// An instruction taking Double (R8) as operand
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldc.r8</code>
/// </remarks>
public class ILOperandR8 : Instruction
{
/// <summary>
///
/// </summary>
private Double operand;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.ShortInlineR</code>
/// </exception>
public ILOperandR8(OpCode op, Double operand) : base(op)
{
if (op.OperandType != OperandType.InlineR)
{
throw new ArgumentException("Expecting an opcode with a R8 as operand. Found: " + op);
}
this.operand = operand;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public Double Operand
{
get
{
return operand;
}
set
{
this.operand = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
wCtx.ig.Emit(op, operand);
}
}
/// <summary>
/// An instruction taking an index to a local variable
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code> ldloc, ldloc.s, ldloca ldloca.s, stloc, stloc.s, ldloc.0,
/// ldloc.1, ldloc.2, ldloc.3, stloc.0, stloc.1, stloc.2, stloc.3</code>
/// </remarks>
public class ILLocalVariable : Instruction
{
/// <summary>
///
/// </summary>
private LocalVariable locVar;
/// <summary>
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.ShortInlineR</code>
/// </exception>
public ILLocalVariable(OpCode op, LocalVariable loc) : base(op)
{
if (!OpCodesClassifier.IsLocalVariable(op))
{
throw new ArgumentException("Expecting an opcode related to a local variable. Found: " + op);
}
this.locVar = loc;
}
/// <summary>
/// Overrides the Equals method
/// </summary>
/// <param name="obj">The object to compare</param>
/// <returns>True if the objects are a match</returns>
public override bool Equals(object obj)
{
return base.Equals (obj);
}
/// <summary>
/// Override of the GetHashCode method
/// </summary>
/// <returns>The HashCode</returns>
public override int GetHashCode()
{
return base.GetHashCode ();
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public LocalVariable Local
{
get
{
return locVar;
}
set
{
this.locVar = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
// Check if a LocalBuilder for this variable was already created
if (locVar == null)
{
throw new NullReferenceException("Local variable not defined");
}
LocalBuilder lBuilder = (LocalBuilder) wCtx.locals[locVar];
wCtx.ig.Emit(op, lBuilder);
}
}
/// <summary>
/// An instruction operating on a parameter
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldarg, ldarg.s, ldarga ldarga.s, starg, starg.s,
/// ldarg.0, ldarg.1, ldarg.2, ldarg.3</code>
/// </remarks>
public class ILArgument : Instruction
{
/// <summary>
///
/// </summary>
private RParameter parameter;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.ShortInlineR</code>
/// </exception>
public ILArgument(OpCode op, RParameter param) : base(op)
{
if (!OpCodesClassifier.IsArgs(op))
{
throw new ArgumentException("Expecting an opcode related to an argument. Found: " + op);
}
this.parameter = param;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public RParameter Parameter
{
get
{
return parameter;
}
set
{
this.parameter = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
if (this.parameter==null)
wCtx.ig.Emit(op);
else if (op.Value == OpCodes.Ldarg_0.Value ||
op.Value == OpCodes.Ldarg_1.Value ||
op.Value == OpCodes.Ldarg_2.Value ||
op.Value == OpCodes.Ldarg_3.Value)
wCtx.ig.Emit(op);
// else if ((wCtx.MethodCode.MethodBody.parent.Attributes & MethodAttributes.Static)!=0)
// {
// if (op.Value==OpCodes.Ldarga_S.Value ||
// op.Value==OpCodes.Starg_S.Value ||
// op.Value==OpCodes.Ldarg_S.Value)
// wCtx.ig.Emit(op,(byte)(this.parameter.Sequence-1));
// else
// wCtx.ig.Emit(op,this.parameter.Sequence-1);
// }
else
wCtx.ig.Emit(op,this.parameter.Sequence);
//TODO: verify this!!!!
}
}
/// <summary>
/// An instruction taking an method as an argument
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>call, callvirt, jmp, ldftn, ldvirtftn, newobj </code>
/// </remarks>
public class ILMethod : Instruction
{
/// <summary>
///
/// </summary>
private RMethodBase method;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineMethod</code>
/// </exception>
public ILMethod(OpCode op, RMethodBase method) : base(op)
{
if (op.OperandType != OperandType.InlineMethod)
{
throw new ArgumentException("Expecting an opcode operating on a method. Found: " + op);
}
this.method = method;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public RMethodBase Method
{
get
{
return method;
}
set
{
this.method = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
if (op.Value == OpCodes.Newobj.Value)
{
// Uses a ConstructorInfo object.
ConstructorInfo ci = wCtx.tResolver.ResolveRConstructor((RConstructor)method);
wCtx.ig.Emit(op, ci);
}
else
{
// Verify why a RConstructor is getting here
if ((op.Value==OpCodes.Call.Value ||
op.Value==OpCodes.Callvirt.Value) && (method is RMethod))
{
MethodInfo mi = wCtx.tResolver.ResolveRMethod((RMethod)method);
wCtx.ig.EmitCall(op, mi,null);
}
else if (op.Value==OpCodes.Calli.Value)
{
MethodInfo mi = wCtx.tResolver.ResolveRMethod((RMethod)method);
Type [] pars = null;
ParameterInfo [] parsInfo = mi.GetParameters();
if (parsInfo!=null)
if (parsInfo.Length!=0)
{
pars = new Type[parsInfo.Length];
for (int i=0 ;i<parsInfo.Length;i++)
pars[i] = parsInfo[i].ParameterType;
}
wCtx.ig.EmitCalli(op,(System.Runtime.InteropServices.CallingConvention) mi.CallingConvention,mi.ReturnType,pars);
}
else
{
if (method is RConstructor)
{
ConstructorInfo mi = wCtx.tResolver.ResolveRConstructor((RConstructor)method);
wCtx.ig.Emit(op, mi);
}
else
{
MethodInfo mi = wCtx.tResolver.ResolveRMethod((RMethod)method);
wCtx.ig.Emit(op, mi);
}
}
}
}
}
/// <summary>
/// An instruction taking a field as an argument
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldfld, ldflda, ldsfld, ldsflda, stfld, stsfld</code>
/// </remarks>
public class ILField : Instruction
{
/// <summary>
///
/// </summary>
private RField field;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineField</code>
/// </exception>
public ILField(OpCode op, RField field) : base(op)
{
if (op.OperandType != OperandType.InlineField)
{
throw new ArgumentException("Expecting an opcode operating on a field. Found: " + op);
}
this.field = field;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public RField Field
{
get
{
return field;
}
set
{
this.field = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
FieldInfo fi = wCtx.tResolver.ResolveRField(field);
wCtx.ig.Emit(op, fi);
}
}
/// <summary>
/// An instruction taking a type as an argument
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>box, castclass, cpobj, initobj, isinst, ldelema, ldobj, mkrefany,
/// newarr, refanyval, sizeof, stobj, unbox</code>
/// </remarks>
public class ILType : Instruction
{
/// <summary>
///
/// </summary>
private RType type;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineField</code>
/// </exception>
public ILType(OpCode op, RType typed) : base(op)
{
if (op.OperandType != OperandType.InlineType)
{
throw new ArgumentException("Expecting an opcode operating on a type. Found: " + op);
}
this.type = typed;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public RType Type
{
get
{
return type;
}
set
{
this.type = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
Type t = wCtx.tResolver.ResolveRType(type);
wCtx.ig.Emit(op, t);
}
}
/// <summary>
/// An instruction taking a String as an argument
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldstr</code>
/// </remarks>
public class ILString : Instruction
{
/// <summary>
///
/// </summary>
private String str;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineField</code>
/// </exception>
public ILString(OpCode op, String str) : base(op)
{
if (op.OperandType != OperandType.InlineString)
{
throw new ArgumentException("Expecting ldstr. Found: " + op);
}
this.str = str;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public String String
{
get
{
return str;
}
set
{
this.str = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
wCtx.ig.Emit(op, str);
}
}
/// <summary>
/// An instruction taking a signature as argument.
/// Not supported by this version.
/// </summary>
/// <remarks>
/// Can only be <code>calli</code>
/// </remarks>
public class ILSig : Instruction
{
/// <summary>
/// </summary>
/// <exception cref="NotSupportedException">
/// Always!
/// </exception>
public ILSig(OpCode op, String str) :base(op)
{
throw new NotSupportedException();
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
throw new NotImplementedException();
}
}
/// <summary>
/// An instruction taking a metadata token as argument
/// </summary>
/// <remarks>
/// <p>Can be one of the following opcodes:</p>
/// <code>ldtoken</code>
/// </remarks>
public class ILToken : Instruction
{
/// <summary>
///
/// </summary>
private RMember token;
/**
* Weird stuff. The overloads of the ILGEnerator.Emit that can be used with LDToken
* include one that accepts MethodInfo, but not MethodBase. This means that a standard
* method can be used with ldtoken but a constructor can't.
*/
/// <summary>
/// Creates a ILToken instruction
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineTok</code>
/// </exception>
/// <param name="op">The OpCode of the instruction</param>
/// <param name="token">The MethodBase operand for the instruction</param>
public ILToken(OpCode op, RMember token) : base(op)
{
checkOperand(op);
this.token = token;
}
/// <summary>
/// Verify if the OpCode is correct for a ILToken instruction
/// </summary>
/// <param name="op">The OpCode to verify</param>
private static void checkOperand(OpCode op)
{
if (op.OperandType != OperandType.InlineTok)
{
throw new ArgumentException("Expecting ldtoken. Found: " + op);
}
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public RMember Token
{
get
{
return token;
}
set
{
this.token = value;
}
}
/// <summary>
/// Write the instruction into the new Assembly
/// </summary>
/// <param name="wCtx">The assembly writing context</param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
// token can be a type, a field or a method
RField field = token as RField;
if (field != null)
{
wCtx.ig.Emit(op, wCtx.tResolver.ResolveRField(field));
return;
}
RMethod method = token as RMethod;
if (method != null)
{
// A token can only be used with a method object, never a constructor
wCtx.ig.Emit(op,wCtx.tResolver.ResolveRMethod(method));
return;
}
RType type = token as RType;
if (type != null)
{
wCtx.ig.Emit(op, wCtx.tResolver.ResolveRType(type));
return;
}
throw new InvalidProgramException("Invalid member: " + token);
}
}
/// <summary>
/// Represents a switch instruction
/// </summary>
/// <remarks>
/// Can only be the opcode <code>switch</code>
/// </remarks>
public class ILSwitch : Instruction, ITargeter
{
/// <summary>
///
/// </summary>
internal Instruction[] targets;
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentException">
/// If the opcode's operand type is not <code>OperandType.InlineSwitch</code>
/// </exception>
public ILSwitch(OpCode op, Instruction[] targets) : base(op)
{
if (op.OperandType != OperandType.InlineSwitch)
{
throw new ArgumentException("Expecting the switch opcode. Found: " + op);
}
this.targets = targets;
}
/// <summary>
/// Gets or sets the operand for this instruction
/// </summary>
public Instruction[] Targets
{
get
{
return targets;
}
set
{
this.targets = value;
if (this.targets!=null &&
this.targets.Length>0)
foreach (Instruction i in this.targets)
i.AddTargeter(this);
}
}
/// <summary>
///
/// </summary>
/// <param name="wCtx"></param>
internal override void Write(WriteContext wCtx)
{
base.Write(wCtx);
Label[] labels = new Label[targets.Length];
for (int i=0; i<labels.Length; i++)
{
// Must obtain a label for the target instruction
if (wCtx.labels.Contains(targets[i]))
{
labels[i] = (Label)wCtx.labels[targets[i]];
}
else
{
// There was no previous label for that instruction.
// Create a new one and adds it
labels[i] = wCtx.ig.DefineLabel();
wCtx.labels.Add(targets[i], labels[i]);
}
}
// Emits the instruction
wCtx.ig.Emit(op, labels);
}
/// <summary>
/// Verify if the instruction is one of the targets
/// </summary>
/// <param name="ins">The instruction to check</param>
/// <returns>True if the instruction is one of the targets</returns>
public bool ContainsTarget(Rail.MSIL.Instruction ins)
{
return (targeters != null && targeters.Contains(ins));
}
}
#endregion
/// <summary>
/// Wraps information necessary to emit instructions
/// </summary>
internal class WriteContext
{
/// <summary>
///
/// </summary>
public readonly Hashtable locals = null;
/// <summary>
///
/// </summary>
public readonly Hashtable labels = new Hashtable();
/// <summary>
///
/// </summary>
internal readonly Hashtable markedLabels = new Hashtable();
/// <summary>
///
/// </summary>
public readonly ExceptionTable exTable;
/// <summary>
///
/// </summary>
public readonly TypeResolver tResolver;
/// <summary>
///
/// </summary>
public readonly ILGenerator ig;
/// <summary>
///
/// </summary>
public int nextExBlock;
/// <summary>
///
/// </summary>
public Stack activeExBlocks = new Stack();
/// <summary>
///
/// </summary>
internal bool IsExBlockEnd = false;
/// <summary>
///
/// </summary>
internal bool IsInFinnalyBlock = false;
internal Code MethodCode;
public WriteContext(ILGenerator ig, TypeResolver tResolver, ExceptionTable exTable)
{
if (tResolver == null)
throw new InvalidProgramException();
if (ig == null)
throw new InvalidProgramException();
this.ig = ig;
this.tResolver = tResolver;
this.exTable = exTable;
nextExBlock = 0;
locals = new Hashtable();
}
public WriteContext(ILGenerator ig, TypeResolver tResolver, ExceptionTable exTable,Hashtable lLocals,Code methodCode)
{
if (tResolver == null)
throw new InvalidProgramException();
if (ig == null)
throw new InvalidProgramException();
this.ig = ig;
this.tResolver = tResolver;
this.exTable = exTable;
nextExBlock = 0;
this.locals = lLocals;
this.MethodCode = methodCode;
}
}
}
|