// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
OpCode[] baseClasses = {
new OpCode("UnaryInstruction", "Instruction with a single argument",
AbstractBaseClass, CustomArguments("argument")),
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right",
AbstractBaseClass, CustomArguments("left", "right")),
};
OpCode[] opCodes = {
new OpCode("nop", "No operation. Takes 0 arguments and returns void.",
VoidResult, NoArguments),
new OpCode("pop", "Pops the top of the evaluation stack and returns the value.",
NoArguments, ResultTypeParam),
new OpCode("peek", "Peeks at the top of the evaluation stack and returns the value. Corresponds to IL 'dup'.",
NoArguments, ResultTypeParam),
new OpCode("void", "Ignore the arguments and produce void. Used to prevent the end result of an instruction from being pushed to the evaluation stack.",
VoidResult, Unary),
new OpCode("ILFunction", "A container of IL blocks.",
CustomChildren(new [] {
new ChildInfo("body")
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")),
new OpCode("BlockContainer", "A container of IL blocks.",
VoidResult, CustomConstructor, CustomVariableName("container")),
new OpCode("Block", "A block of IL instructions.",
CustomConstructor, CustomVariableName("block")),
new OpCode("logic.not", "Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).",
ResultType("I4"), Unary),
new OpCode("add", "Adds two numbers.", BinaryNumeric),
new OpCode("sub", "Subtracts two numbers", BinaryNumeric),
new OpCode("mul", "Multiplies two numbers", BinaryNumeric),
new OpCode("div", "Divides two numbers", BinaryNumeric, MayThrow),
new OpCode("rem", "Division remainder", BinaryNumeric, MayThrow),
new OpCode("bit.and", "Bitwise AND", BinaryNumeric),
new OpCode("bit.or", "Bitwise OR", BinaryNumeric),
new OpCode("bit.xor", "Bitwise XOR", BinaryNumeric),
new OpCode("bit.not", "Bitwise NOT", Unary),
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
new OpCode("br", "Unconditional branch. goto target;",
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags),
// TODO: get rid of endfinally, we can represent those with a normal branch to the end of the filter/finally block instead
new OpCode("endfinally", "Marks the end of an finally, fault or exception filter block.",
CustomClassName("EndFinally"), NoArguments, UnconditionalBranch, MayBranch),
new OpCode("if", "If statement / conditional expression. if (condition) trueExpr else falseExpr",
CustomClassName("IfInstruction"),
CustomChildren(new []{
new ChildInfo("condition") { IsArgument = true },
new ChildInfo("trueInst"),
new ChildInfo("falseInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("try.catch", "Try-catch statement.",
BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("try.catch.handler", "Catch handler within a try-catch statement.",
CustomChildren(new [] {
new ChildInfo("filter") { IsArgument = true },
new ChildInfo("body"),
}), HasVariableOperand, CustomWriteTo, CustomComputeFlags),
new OpCode("try.finally", "Try-finally statement",
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("try.fault", "Try-fault statement",
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("debug.break", "Breakpoint instruction",
NoArguments, VoidResult, SideEffect),
new OpCode("ceq", "Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.",
BinaryComparison),
new OpCode("cgt", "Compare greater than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.",
BinaryComparison),
new OpCode("cgt.un", "Compare greater than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.",
CustomClassName("Cgt_Un"), BinaryComparison),
new OpCode("clt", "Compare less than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.",
BinaryComparison),
new OpCode("clt.un", "Compare less than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.",
CustomClassName("Clt_Un"), BinaryComparison),
new OpCode("call", "Non-virtual method call.", Call),
new OpCode("callvirt", "Virtual method call.",
CustomClassName("CallVirt"), Call),
new OpCode("ckfinite", "Checks that the input float is not NaN or infinite.",
Unary, MayThrow, VoidResult),
new OpCode("conv", "Numeric cast.",
Unary, CustomConstructor),
new OpCode("ldloc", "Loads the value of a local variable. (ldarg/ldloc)",
CustomClassName("LdLoc"), NoArguments, HasVariableOperand, ResultType("variable.Type.GetStackType()")),
new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)",
CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand),
new OpCode("stloc", "Stores a value into a local variable. (starg/stloc)",
CustomClassName("StLoc"), CustomArguments("value"), HasVariableOperand,
ResultType("variable.Type.GetStackType()")),
new OpCode("ldstr", "Loads a constant string.",
CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")),
new OpCode("ldc.i4", "Loads a constant 32-bit integer.",
LoadConstant("int"), ResultType("I4")),
new OpCode("ldc.i8", "Loads a constant 64-bit integer.",
LoadConstant("long"), ResultType("I8")),
new OpCode("ldc.f", "Loads a constant floating-point number.",
LoadConstant("double"), ResultType("F")),
new OpCode("ldnull", "Loads the null reference.",
CustomClassName("LdNull"), NoArguments, ResultType("O")),
new OpCode("ldftn", "Load method pointer",
CustomClassName("LdFtn"), NoArguments, HasMethodOperand, ResultType("I")),
new OpCode("ldvirtftn", "Load method pointer",
CustomClassName("LdVirtFtn"), Unary, HasMethodOperand, MayThrow, ResultType("I")),
new OpCode("ldtoken", "Loads runtime representation of metadata token",
CustomClassName("LdToken"), NoArguments, HasTokenOperand, ResultType("O")),
new OpCode("localloc", "Allocates space in the stack frame",
CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow),
new OpCode("ret", "Returns from the current method or lambda.",
CustomClassName("Return"), CustomConstructor, CustomComputeFlags, MayBranch, UnconditionalBranch),
new OpCode("shl", "Shift left", BinaryNumeric),
new OpCode("shr", "Shift right", BinaryNumeric),
new OpCode("ldfld", "Load instance field",
CustomClassName("LdFld"), CustomArguments("target"), MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow,
HasFieldOperand, ResultType("field.Type.GetStackType()")),
new OpCode("ldflda", "Load address of instance field",
CustomClassName("LdFlda"), CustomArguments("target"), MayThrow, HasFieldOperand, ResultType("Ref")),
new OpCode("stfld", "Store value to instance field",
CustomClassName("StFld"), CustomArguments("target", "value"),
MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, HasFieldOperand,
ResultType("field.Type.GetStackType()")),
new OpCode("ldsfld", "Load static field",
CustomClassName("LdsFld"), NoArguments, MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix,
HasFieldOperand, ResultType("field.Type.GetStackType()")),
new OpCode("ldsflda", "Load static field address",
CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand),
new OpCode("stsfld", "Store value to static field",
CustomClassName("StsFld"), CustomArguments("value"),
MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix, HasFieldOperand,
ResultType("field.Type.GetStackType()")),
new OpCode("castclass", "Casts an object to a class.",
CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")),
new OpCode("isinst", "Test if object is instance of class or interface.",
CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")),
new OpCode("ldobj", "Indirect load (ref/pointer dereference).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("stobj", "Indirect store (store to ref/pointer).",
CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("box", "Boxes a value.",
Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("O")),
new OpCode("unbox", "Compute address inside box.",
Unary, HasTypeOperand, MayThrow, ResultType("Ref")),
new OpCode("unbox.any", "Unbox a value.",
Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("type.GetStackType()")),
new OpCode("newobj", "Creates an object instance and calls the constructor.",
CustomClassName("NewObj"), Call, ResultType("O")),
new OpCode("initobj", "Initializes the value at an address.",
CustomClassName("InitObj"), Unary, HasTypeOperand, VoidResult),
new OpCode("default.value", "Returns the default value for a type.",
NoArguments, HasTypeOperand, ResultType("type.GetStackType()")),
new OpCode("throw", "Throws an exception.",
Unary, MayThrow, UnconditionalBranch),
new OpCode("rethrow", "Rethrows the current exception.",
NoArguments, MayThrow, UnconditionalBranch),
new OpCode("sizeof", "Gets the size of a type in bytes.",
CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("I4")),
new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.",
CustomClassName("LdLen"), CustomArguments("target"), MayThrow, ResultType("I")),
new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), CustomArguments("array", "index"), HasTypeOperand,
MayThrow, ResultType("Ref"), SupportsReadonlyPrefix),
};
#>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
///
/// Enum representing the type of an .
///
public enum OpCode
{
<# foreach (OpCode opCode in opCodes) { #>
/// <#=opCode.Description.Replace("\n", "\n\t\t/// ")#>
<#=opCode.Name#>,
<# } #>
}
<# foreach (OpCode opCode in baseClasses.Concat(opCodes)) { #>
/// <#=opCode.Description.Replace("\n", "\n\t/// ")#>
<#=opCode.ClassModifiers#> partial class <#=opCode.Name#> : <#=string.Join(", ", new[]{opCode.BaseClass}.Concat(opCode.Interfaces))#>
{
<# if (opCode.GenerateConstructor) { #>
<#=opCode.ConstructorModifier#> <#=opCode.Name#>(<#=string.Join(", ", opCode.ConstructorParameters)#>) : base(<#=string.Join(", ", opCode.BaseConstructorArguments)#>)
{<#=Body(opCode.ConstructorBody)#>}
<# } #>
<#=string.Join(Environment.NewLine, opCode.Members.Select(m => "\t\t" + m.Replace("\n", "\n\t\t")))#>
<# if (opCode.GenerateComputeFlags && opCode.Flags.Any(f => f != "base.ComputeFlags()")) { #>
protected override InstructionFlags ComputeFlags()
{
return <#=string.Join(" | ", opCode.Flags)#>;
}
<# } #>
<# if (opCode.GenerateWriteTo) { #>
public override void WriteTo(ITextOutput output)
{<#=Body(opCode.WriteToBody)#>}
<# } #>
<# if (opCode.GenerateAcceptVisitor) { #>
public override T AcceptVisitor(ILVisitor visitor)
{
return visitor.Visit<#=opCode.Name#>(this);
}
<# } #>
}
<# } #>
///
/// Base class for visitor pattern.
///
public abstract class ILVisitor
{
/// Called by Visit*() methods that were not overridden
protected abstract T Default(ILInstruction inst);
<# foreach (OpCode opCode in opCodes) { #>
protected internal virtual T Visit<#=opCode.Name#>(<#=opCode.Name#> <#=opCode.VariableName#>)
{
return Default(<#=opCode.VariableName#>);
}
<# } #>
}
partial class BinaryNumericInstruction
{
public static BinaryNumericInstruction Create(OpCode opCode, ILInstruction left, ILInstruction right, bool checkForOverflow, Sign sign, CompoundAssignmentType compoundAssignmentType = CompoundAssignmentType.None)
{
switch (opCode) {
<# foreach (OpCode opCode in opCodes.Where(c => c.BaseClass == "BinaryNumericInstruction")) { #>
case OpCode.<#=opCode.Name#>:
return new <#=opCode.Name#>(left, right, checkForOverflow, sign, compoundAssignmentType);
<# } #>
default:
throw new ArgumentException("opCode is not a binary numeric instruction");
}
}
}
partial class BinaryComparisonInstruction
{
public static BinaryComparisonInstruction Create(OpCode opCode, ILInstruction left, ILInstruction right)
{
switch (opCode) {
<# foreach (OpCode opCode in opCodes.Where(c => c.BaseClass == "BinaryComparisonInstruction")) { #>
case OpCode.<#=opCode.Name#>:
return new <#=opCode.Name#>(left, right);
<# } #>
default:
throw new ArgumentException("opCode is not a binary comparison instruction");
}
}
}
partial class InstructionOutputExtensions
{
static readonly string[] originalOpCodeNames = {
<# foreach (OpCode opCode in opCodes) { #>
"<#=opCode.OriginalName#>",
<# } #>
};
}
}
<#+
static string Body(IEnumerable statements)
{
StringBuilder b = new StringBuilder();
foreach (var st in statements) {
b.AppendLine();
b.Append("\t\t\t");
b.Append(st.Replace("\n", "\n\t\t\t"));
}
b.AppendLine();
b.Append("\t\t");
return b.ToString();
}
static string MakeName(string originalName)
{
StringBuilder name = new StringBuilder();
bool nextUpper = true;
foreach (char c in originalName) {
if (c == '.')
nextUpper = true;
else if (nextUpper) {
name.Append(char.ToUpper(c));
nextUpper = false;
} else
name.Append(c);
}
return name.ToString();
}
class OpCode
{
public readonly string OriginalName;
public string Name;
public readonly string Description;
public string VariableName = "inst";
public OpCode(string originalName, string description, params Action[] traits)
{
this.OriginalName = originalName;
this.Name = MakeName(originalName);
this.Description = description;
foreach (var trait in traits)
trait(this);
if (this.BaseConstructorArguments.FirstOrDefault() != "opCode")
this.BaseConstructorArguments.Insert(0, "OpCode." + this.Name);
}
public string ClassModifiers = "public sealed";
public bool GenerateConstructor = true;
public string ConstructorModifier = "public";
public List ConstructorParameters = new List();
public List ConstructorBody = new List();
public string BaseClass = "ILInstruction";
public List Interfaces = new List();
public List BaseConstructorArguments = new List();
public List Members = new List();
public List Flags = new List();
public bool GenerateComputeFlags = true;
public bool GenerateAcceptVisitor = true;
public bool GenerateWriteTo = false;
public List WriteOpCodePrefix = new List();
public List WriteOpCodeSuffix = new List();
public List WriteOperand = new List();
public List WriteArguments = new List();
public IEnumerable WriteToBody {
get {
foreach (string line in WriteOpCodePrefix)
yield return line;
yield return "output.Write(OpCode);";
foreach (string line in WriteOpCodeSuffix.Concat(WriteOperand).Concat(WriteArguments))
yield return line;
}
}
}
static Action CustomClassName(string name)
{
return opCode => {
opCode.Name = name;
};
}
static Action CustomVariableName(string name)
{
return opCode => {
opCode.VariableName = name;
};
}
static Action CustomConstructor = opCode => {
opCode.GenerateConstructor = false;
};
static Action CustomWriteTo = opCode => {
opCode.GenerateWriteTo = false;
};
static Action CustomComputeFlags = opCode => {
opCode.GenerateComputeFlags = false;
};
static Action HasFlag(string name)
{
return opCode => {
opCode.Flags.Add(name);
};
}
// ResultType trait: the instruction has the specified result type.
static Action ResultType(string type)
{
if (!type.Contains("."))
type = "StackType." + type;
return opCode => {
opCode.Members.Add("public override StackType ResultType { get { return " + type + "; } }");
};
}
// VoidResult trait: the instruction has no result and is not usable as an argument
static Action VoidResult = ResultType("Void");
// ResultTypeParam trait: the instruction takes its result type as ctor parameter
static Action ResultTypeParam = opCode => {
opCode.ConstructorParameters.Add("StackType resultType");
opCode.ConstructorBody.Add("this.resultType = resultType;");
opCode.Members.Add("StackType resultType;");
opCode.Members.Add("public override StackType ResultType { get { return resultType; } }");
};
// MayThrow trait: the instruction may throw exceptions
static Action MayThrow = HasFlag("InstructionFlags.MayThrow");
// MayBranch trait: the instruction may cause control flow to branch (e.g. branch, conditionalbranch, return)
static Action MayBranch = HasFlag("InstructionFlags.MayBranch");
// UnconditionalBranch trait: the instruction does not produce a result normally; it always branches or throws an exception. Implies VoidResult.
static Action UnconditionalBranch = VoidResult + HasFlag("InstructionFlags.EndPointUnreachable");
static Action BaseClass(string name)
{
return opCode => {
opCode.BaseClass = name;
opCode.Flags.Add("base.ComputeFlags()");
};
}
// NoArguments trait: the instruction no arguments
static Action NoArguments = opCode => {
opCode.BaseClass = "SimpleInstruction";
};
// Unary trait: the instruction has a single argument
static Action Unary = opCode => {
opCode.BaseClass = "UnaryInstruction";
opCode.Flags.Add("base.ComputeFlags()");
opCode.ConstructorParameters.Add("ILInstruction argument");
opCode.BaseConstructorArguments.Add("argument");
opCode.WriteArguments.Add("output.Write('(');");
opCode.WriteArguments.Add("Argument.WriteTo(output);");
opCode.WriteArguments.Add("output.Write(')');");
};
// Binary trait: the instruction has two arguments named 'Left' and 'Right'
static Action Binary = opCode => {
opCode.BaseClass = "BinaryInstruction";
opCode.Flags.Add("base.ComputeFlags()");
opCode.ConstructorParameters.Add("ILInstruction left");
opCode.ConstructorParameters.Add("ILInstruction right");
opCode.BaseConstructorArguments.Add("left");
opCode.BaseConstructorArguments.Add("right");
opCode.WriteArguments.Add("output.Write('(');");
opCode.WriteArguments.Add("Left.WriteTo(output);");
opCode.WriteArguments.Add("output.Write(\", \");");
opCode.WriteArguments.Add("Right.WriteTo(output);");
opCode.WriteArguments.Add("output.Write(')');");
};
// BinaryNumeric trait: the instruction is derived from BinaryNumericInstruction. Implies Binary; and implies MayThrow if the overflow mode is checked.
static Action BinaryNumeric = opCode => {
Binary(opCode);
opCode.BaseClass = "BinaryNumericInstruction";
opCode.ConstructorParameters.Add("bool checkForOverflow");
opCode.BaseConstructorArguments.Add("checkForOverflow");
opCode.ConstructorParameters.Add("Sign sign");
opCode.BaseConstructorArguments.Add("sign");
opCode.ConstructorParameters.Add("CompoundAssignmentType compoundAssignmentType = CompoundAssignmentType.None");
opCode.BaseConstructorArguments.Add("compoundAssignmentType");
};
// BinaryNumeric trait: the instruction is derived from BinaryComparisonInstruction. Implies Binary and I4Result.
static Action BinaryComparison = opCode => {
Binary(opCode);
opCode.BaseClass = "BinaryComparisonInstruction";
opCode.WriteOpCodeSuffix.Add("output.Write('.');");
opCode.WriteOpCodeSuffix.Add("output.Write(OpType);");
};
static Action CustomArguments(params string[] arguments)
{
return CustomChildren(arguments.Select(arg => new ChildInfo(arg) { IsArgument = true}).ToArray(), generateInline: true);
}
class ChildInfo
{
public readonly string PropertyName;
public readonly string Name;
public bool IsArgument;
public bool IsOptional;
public ChildInfo(string name)
{
this.Name = name;
this.PropertyName = MakeName(name);
}
}
static Action CustomChildren(ChildInfo[] children, bool generateInline = false)
{
return opCode => {
opCode.GenerateWriteTo = true;
opCode.WriteArguments.Add("output.Write('(');");
StringBuilder transformChildren = new StringBuilder();
for (int i = 0; i < children.Length; i++) {
string arg = children[i].Name;
string argProp = children[i].PropertyName;
opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = " + arg + ";");
if (i > 0)
opCode.WriteArguments.Add("output.Write(\", \");");
if (children[i].IsOptional)
opCode.WriteArguments.Add("if (this." + arg + " != null) this." + arg + ".WriteTo(output);");
else
opCode.WriteArguments.Add("this." + arg + ".WriteTo(output);");
opCode.Members.Add("ILInstruction " + arg + ";");
opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine
+ "\tget { return this." + arg + "; }" + Environment.NewLine
+ "\tset {" + Environment.NewLine
+ (children[i].IsOptional ? "\t\tif (value != null)" + Environment.NewLine + "\t" : "")
+ "\t\t" + (children[i].IsArgument ? "ValidateArgument" : "ValidateChild") + "(value);" + Environment.NewLine
+ "\t\tSetChildInstruction(ref this." + arg + ", value);" + Environment.NewLine
+ "\t}" + Environment.NewLine
+ "}");
}
opCode.WriteArguments.Add("output.Write(')');");
StringBuilder b;
/*
b = new StringBuilder();
b.AppendLine("public override TAccumulate AggregateChildren(TAccumulate initial, ILVisitor visitor, Func func)");
b.AppendLine("{");
b.AppendLine("\tTAccumulate value = initial;");
foreach (var child in children) {
if (child.IsOptional) {
b.AppendLine("\tif (this." + child.Name + " != null)");
b.Append('\t');
}
b.AppendLine("\tvalue = func(value, this." + child.Name + ".AcceptVisitor(visitor));");
}
b.AppendLine("\treturn value;");
b.Append("}");
opCode.Members.Add(b.ToString());*/
b = new StringBuilder();
b.AppendLine("public override IEnumerable Children {");
b.AppendLine("\tget {");
foreach (var child in children) {
if (child.IsOptional) {
b.AppendLine("\t\tif (this." + child.Name + " != null)");
b.Append('\t');
}
b.AppendLine("\t\tyield return this." + child.Name + ";");
}
b.AppendLine("\t}");
b.Append("}");
opCode.Members.Add(b.ToString());
b = new StringBuilder();
b.AppendLine("public override void TransformChildren(ILVisitor visitor)");
b.AppendLine("{");
foreach (var child in children) {
if (child.IsOptional) {
b.AppendLine("\tif (this." + child.Name + " != null)");
b.Append('\t');
}
b.AppendLine("\tthis." + child.PropertyName + " = this." + child.Name + ".AcceptVisitor(visitor);");
}
b.Append("}");
opCode.Members.Add(b.ToString());
if (generateInline) {
b = new StringBuilder();
b.AppendLine("internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)");
b.AppendLine("{");
for (int i = children.Length - 1; i >= 0; i--) {
string arg = children[i].Name;
if (children[i].IsOptional) {
b.AppendLine("\tif (this." + arg + " != null)");
b.Append("\t");
}
b.Append("\tthis." + MakeName(arg) + " = this." + arg + ".Inline(flagsBefore");
if (i > 0) {
b.Append(" | ((");
b.Append(string.Join(" | ", children.Take(i).Select(child2 => "this." + child2.Name + ".Flags")));
b.Append(") & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop))");
}
b.AppendLine(", context);");
}
b.AppendLine("\treturn this;");
b.Append("}");
opCode.Members.Add(b.ToString());
b.Clear();
b.AppendLine("internal override void TransformStackIntoVariables(TransformStackIntoVariablesState state)");
b.AppendLine("{");
for (int i = 0; i < children.Length; i++) {
string arg = children[i].Name;
b.AppendLine("\t" + MakeName(arg) + ".TransformStackIntoVariables(state);");
}
b.Append("}");
opCode.Members.Add(b.ToString());
}
};
}
static Action AbstractBaseClass = opCode => {
opCode.GenerateAcceptVisitor = false;
opCode.ClassModifiers = "public abstract";
opCode.ConstructorModifier = "protected";
opCode.ConstructorParameters.Add("OpCode opCode");
opCode.BaseConstructorArguments.Add("opCode");
};
// SideEffect trait: the instruction has a non-local side effect
static Action SideEffect = HasFlag("InstructionFlags.SideEffect");
static Action MemoryAccess = SideEffect;
// Call trait: the instruction performs a method call
static Action Call = opCode => {
opCode.BaseClass = "CallInstruction";
opCode.ConstructorParameters.Add("IMethod method");
opCode.BaseConstructorArguments.Add("method");
};
// HasVariableOperand trait: the instruction refers to a local variable
static Action HasVariableOperand = opCode => {
opCode.ConstructorParameters.Add("ILVariable variable");
opCode.Members.Add("readonly ILVariable variable;");
opCode.ConstructorBody.Add("Debug.Assert(variable != null);");
opCode.ConstructorBody.Add("this.variable = variable;");
opCode.Members.Add("/// Returns the variable operand." + Environment.NewLine
+ "public ILVariable Variable { get { return variable; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("variable.WriteTo(output);");
};
static Action HasFieldOperand = opCode => {
opCode.ConstructorParameters.Add("IField field");
opCode.Members.Add("readonly IField field;");
opCode.ConstructorBody.Add("this.field = field;");
opCode.Members.Add("/// Returns the field operand." + Environment.NewLine
+ "public IField Field { get { return field; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, field);");
};
static Action HasTypeOperand = opCode => {
opCode.ConstructorParameters.Add("IType type");
opCode.Members.Add("readonly IType type;");
opCode.ConstructorBody.Add("this.type = type;");
opCode.Members.Add("/// Returns the type operand." + Environment.NewLine
+ "public IType Type { get { return type; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, type);");
};
static Action HasMethodOperand = opCode => {
opCode.ConstructorParameters.Add("IMethod method");
opCode.Members.Add("readonly IMethod method;");
opCode.ConstructorBody.Add("this.method = method;");
opCode.Members.Add("/// Returns the method operand." + Environment.NewLine
+ "public IMethod Method { get { return method; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, method);");
};
static Action HasTokenOperand = opCode => {
opCode.ConstructorParameters.Add("Mono.Cecil.MemberReference member");
opCode.Members.Add("readonly Mono.Cecil.MemberReference member;");
opCode.ConstructorBody.Add("this.member = member;");
opCode.Members.Add("/// Returns the token operand." + Environment.NewLine
+ "public Mono.Cecil.MemberReference Member { get { return member; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, member);");
};
// LoadConstant trait: the instruction loads a compile-time constant. Implies NoArguments.
static Action LoadConstant(string operandType)
{
return opCode => {
NoArguments(opCode);
opCode.ConstructorParameters.Add(operandType + " value");
opCode.Members.Add("public readonly " + operandType + " Value;");
opCode.ConstructorBody.Add("this.Value = value;");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, Value);");
};
}
static Action SupportsVolatilePrefix = opCode => {
opCode.Interfaces.Add("ISupportsVolatilePrefix");
opCode.Members.Add("/// Gets/Sets whether the memory access is volatile." + Environment.NewLine
+ "public bool IsVolatile { get; set; }");
opCode.GenerateWriteTo = true;
opCode.WriteOpCodePrefix.Add("if (IsVolatile)" + Environment.NewLine + "\toutput.Write(\"volatile.\");");
};
static Action SupportsUnalignedPrefix = opCode => {
opCode.Interfaces.Add("ISupportsUnalignedPrefix");
opCode.Members.Add("/// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix." + Environment.NewLine
+ "public byte UnalignedPrefix { get; set; }");
opCode.GenerateWriteTo = true;
opCode.WriteOpCodePrefix.Add("if (UnalignedPrefix > 0)" + Environment.NewLine + "\toutput.Write(\"unaligned(\" + UnalignedPrefix + \").\");");
};
static Action SupportsReadonlyPrefix = opCode => {
opCode.Members.Add("/// Gets whether the 'readonly' prefix was applied to this instruction." + Environment.NewLine
+ "public bool IsReadOnly { get; set; }");
opCode.GenerateWriteTo = true;
opCode.WriteOpCodePrefix.Add("if (IsReadOnly)" + Environment.NewLine + "\toutput.Write(\"readonly.\");");
};
#>