.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

726 lines
31 KiB

// 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'.",
Peeking, 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. <c>goto target;</c>",
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch),
// 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. <c>if (condition) trueExpr else falseExpr</c>",
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, ResultType("Void")),
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 float on top of the stack is not NaN or infinite.",
Peeking, NoArguments, 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"), VoidResult, HasVariableOperand),
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.FieldType.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, VoidResult, HasFieldOperand),
new OpCode("ldsfld", "Load static field",
CustomClassName("LdsFld"), NoArguments, MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix,
HasFieldOperand, ResultType("field.FieldType.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, VoidResult, HasFieldOperand),
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 Mono.Cecil;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Enum representing the type of an <see cref="ILInstruction"/>.
/// </summary>
public enum OpCode
{
<# foreach (OpCode opCode in opCodes) { #>
/// <summary><#=opCode.Description#></summary>
<#=opCode.Name#>,
<# } #>
}
<# foreach (OpCode opCode in baseClasses.Concat(opCodes)) { #>
/// <summary><#=opCode.Description#></summary>
<#=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<T>(ILVisitor<T> visitor)
{
return visitor.Visit<#=opCode.Name#>(this);
}
<# } #>
}
<# } #>
/// <summary>
/// Base class for visitor pattern.
/// </summary>
public abstract class ILVisitor<T>
{
/// <summary>Called by Visit*() methods that were not overridden</summary>
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)
{
switch (opCode) {
<# foreach (OpCode opCode in opCodes.Where(c => c.BaseClass == "BinaryNumericInstruction")) { #>
case OpCode.<#=opCode.Name#>:
return new <#=opCode.Name#>(left, right, checkForOverflow, sign);
<# } #>
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<string> 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<OpCode>[] 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<string> ConstructorParameters = new List<string>();
public List<string> ConstructorBody = new List<string>();
public string BaseClass = "ILInstruction";
public List<string> Interfaces = new List<string>();
public List<string> BaseConstructorArguments = new List<string>();
public List<string> Members = new List<string>();
public List<string> Flags = new List<string>();
public bool GenerateComputeFlags = true;
public bool GenerateAcceptVisitor = true;
public bool GenerateWriteTo = false;
public List<string> WriteOpCodePrefix = new List<string>();
public List<string> WriteOpCodeSuffix = new List<string>();
public List<string> WriteOperand = new List<string>();
public List<string> WriteArguments = new List<string>();
public IEnumerable<string> 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<OpCode> CustomClassName(string name)
{
return opCode => {
opCode.Name = name;
};
}
static Action<OpCode> CustomVariableName(string name)
{
return opCode => {
opCode.VariableName = name;
};
}
static Action<OpCode> CustomConstructor = opCode => {
opCode.GenerateConstructor = false;
};
static Action<OpCode> CustomWriteTo = opCode => {
opCode.GenerateWriteTo = false;
};
static Action<OpCode> CustomComputeFlags = opCode => {
opCode.GenerateComputeFlags = false;
};
static Action<OpCode> HasFlag(string name)
{
return opCode => {
opCode.Flags.Add(name);
};
}
// Peeking trait: the instruction looks at the top-of-stack without popping
static Action<OpCode> Peeking = HasFlag("InstructionFlags.MayPeek");
// ResultType trait: the instruction has the specified result type.
static Action<OpCode> 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<OpCode> VoidResult = ResultType("Void");
// ResultTypeParam trait: the instruction takes its result type as ctor parameter
static Action<OpCode> 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<OpCode> MayThrow = HasFlag("InstructionFlags.MayThrow");
// MayBranch trait: the instruction may cause control flow to branch (e.g. branch, conditionalbranch, return)
static Action<OpCode> 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<OpCode> UnconditionalBranch = VoidResult + HasFlag("InstructionFlags.EndPointUnreachable");
static Action<OpCode> BaseClass(string name)
{
return opCode => {
opCode.BaseClass = name;
opCode.Flags.Add("base.ComputeFlags()");
};
}
// NoArguments trait: the instruction no arguments
static Action<OpCode> NoArguments = opCode => {
opCode.BaseClass = "SimpleInstruction";
};
// Unary trait: the instruction has a single argument
static Action<OpCode> 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<OpCode> 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<OpCode> BinaryNumeric = opCode => {
Binary(opCode);
opCode.BaseClass = "BinaryNumericInstruction";
opCode.ConstructorParameters.Add("bool checkForOverflow");
opCode.BaseConstructorArguments.Add("checkForOverflow");
opCode.WriteOpCodeSuffix.Add("if (checkForOverflow) output.Write(\".ovf\");");
opCode.ConstructorParameters.Add("Sign sign");
opCode.BaseConstructorArguments.Add("sign");
opCode.WriteOpCodeSuffix.Add("if (sign == Sign.Unsigned) output.Write(\".un\");");
};
// BinaryNumeric trait: the instruction is derived from BinaryComparisonInstruction. Implies Binary and I4Result.
static Action<OpCode> BinaryComparison = opCode => {
Binary(opCode);
opCode.BaseClass = "BinaryComparisonInstruction";
opCode.WriteOpCodeSuffix.Add("output.Write('.');");
opCode.WriteOpCodeSuffix.Add("output.Write(OpType);");
};
static Action<OpCode> 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<OpCode> 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<TSource, TAccumulate>(TAccumulate initial, ILVisitor<TSource> visitor, Func<TAccumulate, TSource, TAccumulate> 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<ILInstruction> 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<ILInstruction> 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, Stack<ILInstruction> instructionStack, out bool finished)");
b.AppendLine("{");
for (int i = children.Length - 1; i >= 0; i--) {
string arg = children[i].Name;
if (i < children.Length - 1) {
if (children[i].IsOptional)
b.AppendLine("\tif (finished && this." + arg + " != null)");
else
b.AppendLine("\tif (finished)");
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(", instructionStack, out finished);");
}
b.AppendLine("\treturn this;");
b.Append("}");
opCode.Members.Add(b.ToString());
}
};
}
static Action<OpCode> 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<OpCode> SideEffect = HasFlag("InstructionFlags.SideEffect");
static Action<OpCode> MemoryAccess = SideEffect;
// Call trait: the instruction performs a method call
static Action<OpCode> Call = opCode => {
opCode.BaseClass = "CallInstruction";
opCode.ConstructorParameters.Add("MethodReference method");
opCode.BaseConstructorArguments.Add("method");
};
// HasVariableOperand trait: the instruction refers to a local variable
static Action<OpCode> HasVariableOperand = opCode => {
opCode.ConstructorParameters.Add("ILVariable variable");
opCode.Members.Add("readonly ILVariable variable;");
opCode.ConstructorBody.Add("this.variable = variable;");
opCode.Members.Add("/// <summary>Returns the variable operand.</summary>" + Environment.NewLine
+ "public ILVariable Variable { get { return variable; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("variable.WriteTo(output);");
};
static Action<OpCode> HasFieldOperand = opCode => {
opCode.ConstructorParameters.Add("FieldReference field");
opCode.Members.Add("readonly FieldReference field;");
opCode.ConstructorBody.Add("this.field = field;");
opCode.Members.Add("/// <summary>Returns the field operand.</summary>" + Environment.NewLine
+ "public FieldReference Field { get { return field; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, field);");
};
static Action<OpCode> HasTypeOperand = opCode => {
opCode.ConstructorParameters.Add("TypeReference type");
opCode.Members.Add("readonly TypeReference type;");
opCode.ConstructorBody.Add("this.type = type;");
opCode.Members.Add("/// <summary>Returns the type operand.</summary>" + Environment.NewLine
+ "public TypeReference Type { get { return type; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, type);");
};
static Action<OpCode> HasMethodOperand = opCode => {
opCode.ConstructorParameters.Add("MethodReference method");
opCode.Members.Add("readonly MethodReference method;");
opCode.ConstructorBody.Add("this.method = method;");
opCode.Members.Add("/// <summary>Returns the method operand.</summary>" + Environment.NewLine
+ "public MethodReference Method { get { return method; } }");
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, method);");
};
static Action<OpCode> HasTokenOperand = opCode => {
opCode.ConstructorParameters.Add("MemberReference member");
opCode.Members.Add("readonly MemberReference member;");
opCode.ConstructorBody.Add("this.member = member;");
opCode.Members.Add("/// <summary>Returns the token operand.</summary>" + Environment.NewLine
+ "public 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<OpCode> 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<OpCode> SupportsVolatilePrefix = opCode => {
opCode.Interfaces.Add("ISupportsVolatilePrefix");
opCode.Members.Add("/// <summary>Gets/Sets whether the memory access is volatile.</summary>" + Environment.NewLine
+ "public bool IsVolatile { get; set; }");
opCode.GenerateWriteTo = true;
opCode.WriteOpCodePrefix.Add("if (IsVolatile)" + Environment.NewLine + "\toutput.Write(\"volatile.\");");
};
static Action<OpCode> SupportsUnalignedPrefix = opCode => {
opCode.Interfaces.Add("ISupportsUnalignedPrefix");
opCode.Members.Add("/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>" + Environment.NewLine
+ "public byte UnalignedPrefix { get; set; }");
opCode.GenerateWriteTo = true;
opCode.WriteOpCodePrefix.Add("if (UnalignedPrefix > 0)" + Environment.NewLine + "\toutput.Write(\"unaligned(\" + UnalignedPrefix + \").\");");
};
static Action<OpCode> SupportsReadonlyPrefix = opCode => {
opCode.Members.Add("/// <summary>Gets whether the 'readonly' prefix was applied to this instruction.</summary>" + Environment.NewLine
+ "public bool IsReadOnly { get; set; }");
opCode.GenerateWriteTo = true;
opCode.WriteOpCodePrefix.Add("if (IsReadOnly)" + Environment.NewLine + "\toutput.Write(\"readonly.\");");
};
#>