mirror of https://github.com/icsharpcode/ILSpy.git
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.
880 lines
38 KiB
880 lines
38 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("SimpleInstruction", "Instruction without any arguments", |
|
AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")), |
|
new OpCode("UnaryInstruction", "Instruction with a single argument", |
|
AbstractBaseClass, CustomArguments("argument"), HasFlag("InstructionFlags.None")), |
|
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right", |
|
AbstractBaseClass, CustomArguments("left", "right"), HasFlag("InstructionFlags.None")), |
|
new OpCode("CallInstruction", "Instruction with a list of arguments.", |
|
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), |
|
CustomWriteTo, MayThrow, SideEffect), |
|
}; |
|
|
|
OpCode[] opCodes = { |
|
new OpCode("invalid", "Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.", |
|
CustomClassName("InvalidInstruction"), NoArguments, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable")), |
|
new OpCode("nop", "No operation. Takes 0 arguments and returns void.", |
|
VoidResult, NoArguments), |
|
new OpCode("ILFunction", "A container of IL blocks.", |
|
CustomChildren(new [] { |
|
new ChildInfo("body") |
|
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O") |
|
) { BaseClass = "ILVariableScope" }, |
|
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("PinnedRegion", "A region where a pinned variable is used (initial representation of future fixed statement)", |
|
ResultType("Void"), |
|
CustomChildren(new []{ |
|
new ChildInfo("pin") { CanInlineInto = true }, |
|
new ChildInfo("body") |
|
})), |
|
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, CustomComputeFlags), |
|
new OpCode("leave", "Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c>", |
|
NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags), |
|
new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>", |
|
CustomClassName("IfInstruction"), |
|
CustomChildren(new []{ |
|
new ArgumentInfo("condition"), |
|
new ChildInfo("trueInst"), |
|
new ChildInfo("falseInst"), |
|
}), CustomConstructor, CustomComputeFlags, CustomWriteTo), |
|
new OpCode("switch", "Switch statement", |
|
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void")), |
|
new OpCode("switch.section", "Switch section within a switch statement", |
|
CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }), |
|
CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void")), |
|
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"), |
|
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("comp", "Comparison. The inputs must be both integers; or both floats; or both object references. " |
|
+ "Object references can only be compared for equality or inequality. " |
|
+ "Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which " |
|
+ "evaluates to 1 (true).", |
|
Binary, CustomConstructor, CustomWriteTo, ResultType("I4")), |
|
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.StackType")), |
|
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"), HasVariableOperand, CustomArguments("value"), |
|
ResultType("variable.StackType")), |
|
new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", |
|
CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")), |
|
|
|
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("ldc.decimal", "Loads a constant decimal.", |
|
LoadConstant("decimal"), ResultType("O")), |
|
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("ldtypetoken", "Loads runtime representation of metadata token", |
|
CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")), |
|
new OpCode("ldmembertoken", "Loads runtime representation of metadata token", |
|
CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, 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("Method.DeclaringType.GetStackType()")), |
|
new OpCode("newarr", "Creates an array instance.", |
|
CustomClassName("NewArr"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("indices") { IsCollection = true } }, true), MayThrow, ResultType("O")), |
|
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("array"), CustomConstructor, CustomWriteTo, MayThrow), |
|
new OpCode("ldelema", "Load address of array element.", |
|
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), |
|
MayThrow, ResultType("Ref"), SupportsReadonlyPrefix), |
|
new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.", |
|
CustomArguments("array"), ResultType("Ref")), |
|
|
|
new OpCode("mkrefany", "Push a typed reference of type class onto the stack.", |
|
CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")), |
|
new OpCode("refanytype", "Push the type token stored in a typed reference.", |
|
CustomClassName("RefAnyType"), Unary, ResultType("O")), |
|
new OpCode("refanyval", "Push the address stored in a typed reference.", |
|
CustomClassName("RefAnyValue"), Unary, HasTypeOperand, MayThrow, ResultType("Ref")), |
|
}; |
|
#> |
|
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
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.Replace("\n", "\n\t\t/// ")#></summary> |
|
<#=opCode.Name#>, |
|
<# } #> |
|
} |
|
|
|
<# foreach (OpCode opCode in baseClasses.Concat(opCodes)) { #> |
|
/// <summary><#=opCode.Description.Replace("\n", "\n\t/// ")#></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.GenerateComputeFlags && opCode.Flags.Any(f => f != "base.ComputeFlags()")) { #> |
|
public override InstructionFlags DirectFlags { |
|
get { |
|
return <#=opCode.DirectFlags.Count > 0 ? string.Join(" | ", opCode.DirectFlags) : "InstructionFlags.None"#>; |
|
} |
|
} |
|
<# } #> |
|
<# if (opCode.GenerateWriteTo) { #> |
|
public override void WriteTo(ITextOutput output) |
|
{<#=Body(opCode.WriteToBody)#>} |
|
<# } #> |
|
<# if (opCode.GenerateAcceptVisitor) { #> |
|
public override void AcceptVisitor(ILVisitor visitor) |
|
{ |
|
visitor.Visit<#=opCode.Name#>(this); |
|
} |
|
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 |
|
{ |
|
/// <summary>Called by Visit*() methods that were not overridden</summary> |
|
protected abstract void Default(ILInstruction inst); |
|
|
|
<# foreach (OpCode opCode in opCodes) { #> |
|
protected internal virtual void Visit<#=opCode.Name#>(<#=opCode.Name#> <#=opCode.VariableName#>) |
|
{ |
|
Default(<#=opCode.VariableName#>); |
|
} |
|
<# } #> |
|
} |
|
|
|
/// <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#>", |
|
<# } #> |
|
}; |
|
} |
|
|
|
partial class ILInstruction |
|
{ |
|
<# foreach (OpCode opCode in opCodes) { #> |
|
<# if (opCode.GenerateMatch) { #> |
|
public bool Match<#=opCode.Name#>(<#=string.Join(", ", opCode.MatchParameters)#>) |
|
{ |
|
var inst = this as <#=opCode.Name#>; |
|
if (inst != null) { |
|
<# foreach (var parameter in opCode.MatchParameters) {#> |
|
<#=parameter.Name#> = inst.<#=parameter.FieldName#>; |
|
<# }#> |
|
return true; |
|
} |
|
<# foreach (var parameter in opCode.MatchParameters) {#> |
|
<#=parameter.Name#> = default(<#=parameter.TypeName#>); |
|
<# }#> |
|
return false; |
|
} |
|
<# } |
|
} #> |
|
} |
|
} |
|
|
|
<#+ |
|
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 bool GenerateMatch = true; |
|
public List<MatchParamInfo> MatchParameters = new List<MatchParamInfo>(); |
|
|
|
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 List<string> DirectFlags = 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; |
|
} |
|
} |
|
} |
|
|
|
class MatchParamInfo |
|
{ |
|
public string TypeName; |
|
public string Name; |
|
public string FieldName; |
|
|
|
public override string ToString() |
|
{ |
|
return "out " + TypeName + " " + Name; |
|
} |
|
} |
|
|
|
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; |
|
opCode.GenerateMatch = 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); |
|
opCode.DirectFlags.Add(name); |
|
}; |
|
} |
|
|
|
// 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. |
|
// UnconditionalBranch should be paired with either MayBranch or MayThrow (or both). |
|
static Action<OpCode> UnconditionalBranch = VoidResult + HasFlag("InstructionFlags.EndPointUnreachable"); |
|
|
|
// ControlFlow trait: the instruction involves some form of internal control flow |
|
// Instructions without this trait must evaluate all arguments left-to-right before having any effect of their own. |
|
static Action<OpCode> ControlFlow = HasFlag("InstructionFlags.ControlFlow"); |
|
|
|
static Action<OpCode> BaseClass(string name) |
|
{ |
|
return opCode => { |
|
opCode.BaseClass = name; |
|
opCode.Flags.Add("base.ComputeFlags()"); |
|
opCode.DirectFlags.Add("base.DirectFlags"); |
|
}; |
|
} |
|
|
|
// 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 => { |
|
BaseClass("UnaryInstruction")(opCode); |
|
opCode.ConstructorParameters.Add("ILInstruction argument"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "argument", FieldName = "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 => { |
|
BaseClass("BinaryInstruction")(opCode); |
|
opCode.ConstructorParameters.Add("ILInstruction left"); |
|
opCode.ConstructorParameters.Add("ILInstruction right"); |
|
opCode.BaseConstructorArguments.Add("left"); |
|
opCode.BaseConstructorArguments.Add("right"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "left", FieldName = "Left" }); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "right", FieldName = "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.ConstructorParameters.Add("Sign sign"); |
|
opCode.BaseConstructorArguments.Add("sign"); |
|
}; |
|
|
|
static Action<OpCode> CustomArguments(params string[] arguments) |
|
{ |
|
return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg)).ToArray(), generateInline: true); |
|
} |
|
|
|
class ChildInfo |
|
{ |
|
public readonly string PropertyName; |
|
public readonly string Name; |
|
public readonly string SlotName; |
|
|
|
public bool IsCollection; |
|
public bool CanInlineInto; |
|
|
|
public ChildInfo(string name) |
|
{ |
|
this.Name = name; |
|
this.PropertyName = MakeName(name); |
|
this.SlotName = this.PropertyName + "Slot"; |
|
} |
|
|
|
public string GetSlotInit() |
|
{ |
|
StringBuilder b = new StringBuilder(); |
|
b.Append("new SlotInfo(\"" + PropertyName + "\""); |
|
if (CanInlineInto) |
|
b.Append(", canInlineInto: true"); |
|
b.Append(")"); |
|
return b.ToString(); |
|
} |
|
} |
|
|
|
class ArgumentInfo : ChildInfo |
|
{ |
|
public ArgumentInfo(string name) : base(name) |
|
{ |
|
this.CanInlineInto = true; |
|
} |
|
} |
|
|
|
static Action<OpCode> CustomChildren(ChildInfo[] children, bool generateInline = false) |
|
{ |
|
return opCode => { |
|
opCode.GenerateWriteTo = true; |
|
opCode.WriteArguments.Add("output.Write('(');"); |
|
StringBuilder transformChildren = new StringBuilder(); |
|
ChildInfo collection = null; |
|
int childCount = children.Length; |
|
for (int i = 0; i < children.Length; i++) { |
|
string arg = children[i].Name; |
|
string argProp = children[i].PropertyName; |
|
if (children[i].IsCollection && i + 1 == children.Length) { |
|
collection = children[i]; |
|
childCount = children.Length - 1; |
|
opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)"); |
|
opCode.ConstructorParameters.Add("params ILInstruction[] " + arg); |
|
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<ILInstruction>(this, " + i + ");"); |
|
opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");"); |
|
if (i == 0) |
|
opCode.WriteArguments.Add("bool first = true;"); |
|
opCode.WriteArguments.Add("foreach (var " + arg + " in " + argProp + ") {"); |
|
if (i > 0) |
|
opCode.WriteArguments.Add("\toutput.Write(\", \");"); |
|
else |
|
opCode.WriteArguments.Add("\tif (!first) output.Write(\", \"); else first = false;"); |
|
opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output);"); |
|
opCode.WriteArguments.Add("}"); |
|
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); |
|
opCode.Members.Add("public InstructionCollection<ILInstruction> " + argProp + " { get; private set; }"); |
|
} else { |
|
opCode.Flags.Add(arg + ".Flags"); |
|
opCode.ConstructorParameters.Add("ILInstruction " + arg); |
|
opCode.ConstructorBody.Add("this." + argProp + " = " + arg + ";"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = arg, FieldName = argProp }); |
|
if (i > 0) |
|
opCode.WriteArguments.Add("output.Write(\", \");"); |
|
opCode.WriteArguments.Add("this." + arg + ".WriteTo(output);"); |
|
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); |
|
opCode.Members.Add("ILInstruction " + arg + ";"); |
|
opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine |
|
+ "\tget { return this." + arg + "; }" + Environment.NewLine |
|
+ "\tset {" + Environment.NewLine |
|
+ "\t\tValidateChild(value);" + Environment.NewLine |
|
+ "\t\tSetChildInstruction(ref this." + arg + ", value, " + i + ");" + Environment.NewLine |
|
+ "\t}" + Environment.NewLine |
|
+ "}"); |
|
} |
|
} |
|
opCode.WriteArguments.Add("output.Write(')');"); |
|
StringBuilder b; |
|
b = new StringBuilder(); |
|
b.AppendLine("protected sealed override int GetChildCount()"); |
|
b.AppendLine("{"); |
|
b.Append("\treturn "); |
|
if (childCount > 0 || collection == null) |
|
b.Append(childCount); |
|
if (collection != null) { |
|
if (childCount > 0) b.Append(" + "); |
|
b.Append(collection.PropertyName + ".Count"); |
|
} |
|
b.AppendLine(";"); |
|
b.Append("}"); |
|
opCode.Members.Add(b.ToString()); |
|
|
|
b = new StringBuilder(); |
|
b.AppendLine("protected sealed override ILInstruction GetChild(int index)"); |
|
b.AppendLine("{"); |
|
b.AppendLine("\tswitch (index) {"); |
|
for (int i = 0; i < childCount; i++) { |
|
b.AppendLine("\t\tcase " + i + ":"); |
|
b.AppendLine("\t\t\treturn this." + children[i].Name + ";"); |
|
} |
|
b.AppendLine("\t\tdefault:"); |
|
if (collection == null) |
|
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); |
|
else |
|
b.AppendLine("\t\t\treturn this." + collection.PropertyName + "[index - " + childCount + "];"); |
|
b.AppendLine("\t}"); |
|
b.Append("}"); |
|
opCode.Members.Add(b.ToString()); |
|
|
|
b = new StringBuilder(); |
|
b.AppendLine("protected sealed override void SetChild(int index, ILInstruction value)"); |
|
b.AppendLine("{"); |
|
b.AppendLine("\tswitch (index) {"); |
|
for (int i = 0; i < childCount; i++) { |
|
b.AppendLine("\t\tcase " + i + ":"); |
|
b.AppendLine("\t\t\tthis." + children[i].PropertyName + " = value;"); |
|
b.AppendLine("\t\t\tbreak;"); |
|
} |
|
b.AppendLine("\t\tdefault:"); |
|
if (collection == null) |
|
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); |
|
else { |
|
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;"); |
|
b.AppendLine("\t\t\tbreak;"); |
|
} |
|
b.AppendLine("\t}"); |
|
b.Append("}"); |
|
opCode.Members.Add(b.ToString()); |
|
|
|
b = new StringBuilder(); |
|
b.AppendLine("protected sealed override SlotInfo GetChildSlot(int index)"); |
|
b.AppendLine("{"); |
|
b.AppendLine("\tswitch (index) {"); |
|
for (int i = 0; i < childCount; i++) { |
|
b.AppendLine("\t\tcase " + i + ":"); |
|
b.AppendLine("\t\t\treturn " + children[i].SlotName + ";"); |
|
} |
|
b.AppendLine("\t\tdefault:"); |
|
if (collection == null) |
|
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); |
|
else |
|
b.AppendLine("\t\t\treturn " + collection.SlotName + ";"); |
|
b.AppendLine("\t}"); |
|
b.Append("}"); |
|
opCode.Members.Add(b.ToString()); |
|
|
|
b = new StringBuilder(); |
|
b.AppendLine("public sealed override ILInstruction Clone()"); |
|
b.AppendLine("{"); |
|
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();"); |
|
for (int i = 0; i < children.Length; i++) { |
|
if (children[i].IsCollection) { |
|
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<ILInstruction>(clone, " + i + ");"); |
|
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));"); |
|
} else |
|
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();"); |
|
} |
|
b.AppendLine("\treturn clone;"); |
|
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"); |
|
opCode.GenerateMatch = false; |
|
}; |
|
|
|
// 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("IMethod method"); |
|
opCode.BaseConstructorArguments.Add("method"); |
|
opCode.GenerateMatch = false; |
|
}; |
|
|
|
// HasVariableOperand trait: the instruction refers to a local variable |
|
static Action<OpCode> HasVariableOperand = opCode => { |
|
opCode.ConstructorParameters.Add("ILVariable variable"); |
|
opCode.ConstructorBody.Add("Debug.Assert(variable != null);"); |
|
opCode.ConstructorBody.Add("this.variable = variable;"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILVariable", Name = "variable", FieldName = "Variable" }); |
|
opCode.GenerateWriteTo = true; |
|
opCode.WriteOperand.Add("output.Write(' ');"); |
|
opCode.WriteOperand.Add("variable.WriteTo(output);"); |
|
opCode.Interfaces.Add("IInstructionWithVariableOperand"); |
|
}; |
|
|
|
static Action<OpCode> HasFieldOperand = opCode => { |
|
opCode.ConstructorParameters.Add("IField field"); |
|
opCode.Members.Add("readonly IField field;"); |
|
opCode.ConstructorBody.Add("this.field = field;"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IField", Name = "field", FieldName = "Field" }); |
|
opCode.Members.Add("/// <summary>Returns the field operand.</summary>" + Environment.NewLine |
|
+ "public IField Field { get { return field; } }"); |
|
opCode.GenerateWriteTo = true; |
|
opCode.WriteOperand.Add("output.Write(' ');"); |
|
opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, field);"); |
|
opCode.Interfaces.Add("IInstructionWithFieldOperand"); |
|
}; |
|
|
|
static Action<OpCode> HasTypeOperand = opCode => { |
|
opCode.ConstructorParameters.Add("IType type"); |
|
opCode.Members.Add("readonly IType type;"); |
|
opCode.ConstructorBody.Add("this.type = type;"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IType", Name = "type", FieldName = "Type" }); |
|
opCode.Members.Add("/// <summary>Returns the type operand.</summary>" + 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<OpCode> HasMethodOperand = opCode => { |
|
opCode.ConstructorParameters.Add("IMethod method"); |
|
opCode.Members.Add("readonly IMethod method;"); |
|
opCode.ConstructorBody.Add("this.method = method;"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IMethod", Name = "method", FieldName = "Method" }); |
|
opCode.Members.Add("/// <summary>Returns the method operand.</summary>" + 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<OpCode> HasMemberOperand = opCode => { |
|
opCode.ConstructorParameters.Add("IMember member"); |
|
opCode.Members.Add("readonly IMember member;"); |
|
opCode.ConstructorBody.Add("this.member = member;"); |
|
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IMember", Name = "member", FieldName = "Member" }); |
|
opCode.Members.Add("/// <summary>Returns the token operand.</summary>" + Environment.NewLine |
|
+ "public IMember 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.MatchParameters.Add(new MatchParamInfo { TypeName = operandType, Name = "value", FieldName = "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.\");"); |
|
}; |
|
#> |