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.
1221 lines
44 KiB
1221 lines
44 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. |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Threading.Tasks; |
|
using System.Collections.Immutable; |
|
using System.Diagnostics; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using Mono.Cecil; |
|
using Cil = Mono.Cecil.Cil; |
|
using System.Collections; |
|
using System.Threading; |
|
|
|
namespace ICSharpCode.Decompiler.IL |
|
{ |
|
public class ILReader |
|
{ |
|
internal static ILOpCode ReadOpCode(ref BlobReader reader) |
|
{ |
|
byte b = reader.ReadByte(); |
|
if (b == 0xfe) |
|
return (ILOpCode)(0x100 | reader.ReadByte()); |
|
else |
|
return (ILOpCode)b; |
|
} |
|
|
|
internal static MetadataToken ReadMetadataToken(ref BlobReader reader) |
|
{ |
|
return new MetadataToken(reader.ReadUInt32()); |
|
} |
|
|
|
readonly ICompilation compilation; |
|
readonly IDecompilerTypeSystem typeSystem; |
|
|
|
public ILReader(IDecompilerTypeSystem typeSystem) |
|
{ |
|
if (typeSystem == null) |
|
throw new ArgumentNullException("typeSystem"); |
|
this.typeSystem = typeSystem; |
|
this.compilation = typeSystem.Compilation; |
|
} |
|
|
|
Cil.MethodBody body; |
|
StackType methodReturnStackType; |
|
BlobReader reader; |
|
ImmutableStack<ILVariable> currentStack; |
|
ILVariable[] parameterVariables; |
|
ILVariable[] localVariables; |
|
BitArray isBranchTarget; |
|
List<ILInstruction> instructionBuilder; |
|
|
|
// Dictionary that stores stacks for each IL instruction |
|
Dictionary<int, ImmutableStack<ILVariable>> stackByOffset; |
|
Dictionary<Cil.ExceptionHandler, ILVariable> variableByExceptionHandler; |
|
UnionFind<ILVariable> unionFind; |
|
IEnumerable<ILVariable> stackVariables; |
|
|
|
void Init(Cil.MethodBody body) |
|
{ |
|
if (body == null) |
|
throw new ArgumentNullException("body"); |
|
this.body = body; |
|
this.reader = body.GetILReader(); |
|
this.currentStack = ImmutableStack<ILVariable>.Empty; |
|
this.unionFind = new UnionFind<ILVariable>(); |
|
this.methodReturnStackType = typeSystem.Resolve(body.Method.ReturnType).GetStackType(); |
|
InitParameterVariables(); |
|
this.localVariables = body.Variables.SelectArray(CreateILVariable); |
|
if (body.InitLocals) { |
|
foreach (var v in localVariables) { |
|
v.HasInitialValue = true; |
|
} |
|
} |
|
this.instructionBuilder = new List<ILInstruction>(); |
|
this.isBranchTarget = new BitArray(body.CodeSize); |
|
this.stackByOffset = new Dictionary<int, ImmutableStack<ILVariable>>(); |
|
this.variableByExceptionHandler = new Dictionary<Cil.ExceptionHandler, ILVariable>(); |
|
} |
|
|
|
IMetadataTokenProvider ReadAndDecodeMetadataToken() |
|
{ |
|
var token = ReadMetadataToken(ref reader); |
|
return body.LookupToken(token); |
|
} |
|
|
|
IType ReadAndDecodeTypeReference() |
|
{ |
|
var token = ReadMetadataToken(ref reader); |
|
var typeReference = body.LookupToken(token) as TypeReference; |
|
return typeSystem.Resolve(typeReference); |
|
} |
|
|
|
IMethod ReadAndDecodeMethodReference() |
|
{ |
|
var token = ReadMetadataToken(ref reader); |
|
var methodReference = body.LookupToken(token) as MethodReference; |
|
return typeSystem.Resolve(methodReference); |
|
} |
|
|
|
IField ReadAndDecodeFieldReference() |
|
{ |
|
var token = ReadMetadataToken(ref reader); |
|
var fieldReference = body.LookupToken(token) as FieldReference; |
|
return typeSystem.Resolve(fieldReference); |
|
} |
|
|
|
void InitParameterVariables() |
|
{ |
|
parameterVariables = new ILVariable[GetPopCount(OpCode.Call, body.Method)]; |
|
int paramIndex = 0; |
|
if (body.Method.HasThis) |
|
parameterVariables[paramIndex++] = CreateILVariable(body.ThisParameter); |
|
foreach (var p in body.Method.Parameters) |
|
parameterVariables[paramIndex++] = CreateILVariable(p); |
|
Debug.Assert(paramIndex == parameterVariables.Length); |
|
} |
|
|
|
ILVariable CreateILVariable(Cil.VariableDefinition v) |
|
{ |
|
VariableKind kind = v.IsPinned ? VariableKind.PinnedLocal : VariableKind.Local; |
|
ILVariable ilVar = new ILVariable(kind, typeSystem.Resolve(v.VariableType), v.Index); |
|
if (string.IsNullOrEmpty(v.Name)) |
|
ilVar.Name = "V_" + v.Index; |
|
else |
|
ilVar.Name = v.Name; |
|
return ilVar; |
|
} |
|
|
|
ILVariable CreateILVariable(ParameterDefinition p) |
|
{ |
|
IType parameterType; |
|
if (p.Index == -1) { |
|
// Manually construct ctor parameter type due to Cecil bug: |
|
// https://github.com/jbevain/cecil/issues/275 |
|
ITypeDefinition def = typeSystem.Resolve(body.Method.DeclaringType).GetDefinition(); |
|
if (def != null && def.TypeParameterCount > 0) { |
|
parameterType = new ParameterizedType(def, def.TypeArguments); |
|
if (def.IsReferenceType == false) { |
|
parameterType = new NRefactory.TypeSystem.ByReferenceType(parameterType); |
|
} |
|
} else { |
|
parameterType = typeSystem.Resolve(p.ParameterType); |
|
} |
|
} else { |
|
parameterType = typeSystem.Resolve(p.ParameterType); |
|
} |
|
Debug.Assert(!parameterType.IsUnbound()); |
|
if (parameterType.IsUnbound()) { |
|
// parameter types should not be unbound, the only known cause for these is a Cecil bug: |
|
Debug.Assert(p.Index < 0); // cecil bug occurs only for "this" |
|
parameterType = new ParameterizedType(parameterType.GetDefinition(), parameterType.TypeArguments); |
|
} |
|
var ilVar = new ILVariable(VariableKind.Parameter, parameterType, p.Index); |
|
Debug.Assert(ilVar.StoreCount == 1); // count the initial store when the method is called with an argument |
|
if (p.Index < 0) |
|
ilVar.Name = "this"; |
|
else if (string.IsNullOrEmpty(p.Name)) |
|
ilVar.Name = "P_" + p.Index; |
|
else |
|
ilVar.Name = p.Name; |
|
return ilVar; |
|
} |
|
|
|
/// <summary> |
|
/// Warn when invalid IL is detected. |
|
/// ILSpy should be able to handle invalid IL; but this method can be helpful for debugging the ILReader, |
|
/// as this method should not get called when processing valid IL. |
|
/// </summary> |
|
void Warn(string message) |
|
{ |
|
Debug.Fail(message); |
|
} |
|
|
|
void MergeStacks(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> b) |
|
{ |
|
var enum1 = a.GetEnumerator(); |
|
var enum2 = b.GetEnumerator(); |
|
bool ok1 = enum1.MoveNext(); |
|
bool ok2 = enum2.MoveNext(); |
|
while (ok1 && ok2) { |
|
if (enum1.Current.StackType != enum2.Current.StackType) { |
|
Warn("Incompatible stack types: " + enum1.Current.StackType + " vs " + enum2.Current.StackType); |
|
} |
|
unionFind.Merge(enum1.Current, enum2.Current); |
|
ok1 = enum1.MoveNext(); |
|
ok2 = enum2.MoveNext(); |
|
} |
|
if (ok1 || ok2) { |
|
Warn("Incompatible stack heights: " + a.Count() + " vs " + b.Count()); |
|
} |
|
} |
|
|
|
void StoreStackForOffset(int offset, ImmutableStack<ILVariable> stack) |
|
{ |
|
ImmutableStack<ILVariable> existing; |
|
if (stackByOffset.TryGetValue(offset, out existing)) { |
|
MergeStacks(existing, stack); |
|
} else { |
|
stackByOffset.Add(offset, stack); |
|
} |
|
} |
|
|
|
void ReadInstructions(CancellationToken cancellationToken) |
|
{ |
|
// Fill isBranchTarget and branchStackDict based on exception handlers |
|
foreach (var eh in body.ExceptionHandlers) { |
|
ImmutableStack<ILVariable> ehStack = null; |
|
if (eh.HandlerType == Cil.ExceptionHandlerType.Catch || eh.HandlerType == Cil.ExceptionHandlerType.Filter) { |
|
var v = new ILVariable(VariableKind.Exception, typeSystem.Resolve(eh.CatchType), eh.HandlerStart.Offset) { |
|
Name = "E_" + eh.HandlerStart.Offset |
|
}; |
|
variableByExceptionHandler.Add(eh, v); |
|
ehStack = ImmutableStack.Create(v); |
|
} else { |
|
ehStack = ImmutableStack<ILVariable>.Empty; |
|
} |
|
if (eh.FilterStart != null) { |
|
isBranchTarget[eh.FilterStart.Offset] = true; |
|
StoreStackForOffset(eh.FilterStart.Offset, ehStack); |
|
} |
|
if (eh.HandlerStart != null) { |
|
isBranchTarget[eh.HandlerStart.Offset] = true; |
|
StoreStackForOffset(eh.HandlerStart.Offset, ehStack); |
|
} |
|
} |
|
|
|
while (reader.Position < reader.Length) { |
|
cancellationToken.ThrowIfCancellationRequested(); |
|
int start = reader.Position; |
|
StoreStackForOffset(start, currentStack); |
|
ILInstruction decodedInstruction = DecodeInstruction(); |
|
if (decodedInstruction.ResultType == StackType.Unknown) |
|
Warn("Unknown result type (might be due to invalid IL)"); |
|
decodedInstruction.CheckInvariant(ILPhase.InILReader); |
|
decodedInstruction.ILRange = new Interval(start, reader.Position); |
|
UnpackPush(decodedInstruction).ILRange = decodedInstruction.ILRange; |
|
instructionBuilder.Add(decodedInstruction); |
|
if ((decodedInstruction.DirectFlags & InstructionFlags.EndPointUnreachable) != 0) { |
|
if (!stackByOffset.TryGetValue(reader.Position, out currentStack)) { |
|
currentStack = ImmutableStack<ILVariable>.Empty; |
|
} |
|
} |
|
} |
|
|
|
var visitor = new CollectStackVariablesVisitor(unionFind); |
|
for (int i = 0; i < instructionBuilder.Count; i++) { |
|
instructionBuilder[i] = instructionBuilder[i].AcceptVisitor(visitor); |
|
} |
|
stackVariables = visitor.variables; |
|
} |
|
|
|
/// <summary> |
|
/// Debugging helper: writes the decoded instruction stream interleaved with the inferred evaluation stack layout. |
|
/// </summary> |
|
public void WriteTypedIL(Cil.MethodBody body, ITextOutput output, CancellationToken cancellationToken = default(CancellationToken)) |
|
{ |
|
Init(body); |
|
ReadInstructions(cancellationToken); |
|
foreach (var inst in instructionBuilder) { |
|
output.Write(" ["); |
|
bool isFirstElement = true; |
|
foreach (var element in stackByOffset[inst.ILRange.Start]) { |
|
if (isFirstElement) |
|
isFirstElement = false; |
|
else |
|
output.Write(", "); |
|
output.WriteReference(element.Name, element, isLocal: true); |
|
output.Write(":"); |
|
output.Write(element.StackType); |
|
} |
|
output.Write(']'); |
|
output.WriteLine(); |
|
if (isBranchTarget[inst.ILRange.Start]) |
|
output.Write('*'); |
|
else |
|
output.Write(' '); |
|
output.WriteDefinition("IL_" + inst.ILRange.Start.ToString("x4"), inst.ILRange.Start); |
|
output.Write(": "); |
|
inst.WriteTo(output); |
|
output.WriteLine(); |
|
} |
|
new Disassembler.MethodBodyDisassembler(output, false, cancellationToken).WriteExceptionHandlers(body); |
|
} |
|
|
|
/// <summary> |
|
/// Decodes the specified method body and returns an ILFunction. |
|
/// </summary> |
|
public ILFunction ReadIL(Cil.MethodBody body, CancellationToken cancellationToken = default(CancellationToken)) |
|
{ |
|
Init(body); |
|
ReadInstructions(cancellationToken); |
|
var blockBuilder = new BlockBuilder(body, typeSystem, variableByExceptionHandler); |
|
var container = blockBuilder.CreateBlocks(instructionBuilder, isBranchTarget); |
|
var function = new ILFunction(body.Method, container); |
|
function.Variables.AddRange(parameterVariables); |
|
function.Variables.AddRange(localVariables); |
|
function.Variables.AddRange(stackVariables); |
|
function.Variables.AddRange(variableByExceptionHandler.Values); |
|
function.AddRef(); // mark the root node |
|
foreach (var c in function.Descendants.OfType<BlockContainer>()) { |
|
c.SortBlocks(); |
|
} |
|
return function; |
|
} |
|
|
|
static ILInstruction UnpackPush(ILInstruction inst) |
|
{ |
|
ILVariable v; |
|
ILInstruction inner; |
|
if (inst.MatchStLoc(out v, out inner) && v.Kind == VariableKind.StackSlot) |
|
return inner; |
|
else |
|
return inst; |
|
} |
|
|
|
ILInstruction Neg() |
|
{ |
|
switch (PeekStackType()) { |
|
case StackType.I4: |
|
return Push(new Sub(new LdcI4(0), Pop(), checkForOverflow: false, sign: Sign.None)); |
|
case StackType.I: |
|
return Push(new Sub(new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None), Pop(), checkForOverflow: false, sign: Sign.None)); |
|
case StackType.I8: |
|
return Push(new Sub(new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None)); |
|
case StackType.F: |
|
return Push(new Sub(new LdcF(0), Pop(), checkForOverflow: false, sign: Sign.None)); |
|
default: |
|
Warn("Unsupported input type for neg."); |
|
goto case StackType.I4; |
|
} |
|
} |
|
|
|
ILInstruction DecodeInstruction() |
|
{ |
|
var ilOpCode = ReadOpCode(ref reader); |
|
switch (ilOpCode) { |
|
case ILOpCode.Constrained: |
|
return DecodeConstrainedCall(); |
|
case ILOpCode.Readonly: |
|
return DecodeReadonly(); |
|
case ILOpCode.Tailcall: |
|
return DecodeTailCall(); |
|
case ILOpCode.Unaligned: |
|
return DecodeUnaligned(); |
|
case ILOpCode.Volatile: |
|
return DecodeVolatile(); |
|
case ILOpCode.Add: |
|
return BinaryNumeric(OpCode.Add); |
|
case ILOpCode.Add_Ovf: |
|
return BinaryNumeric(OpCode.Add, true, Sign.Signed); |
|
case ILOpCode.Add_Ovf_Un: |
|
return BinaryNumeric(OpCode.Add, true, Sign.Unsigned); |
|
case ILOpCode.And: |
|
return BinaryNumeric(OpCode.BitAnd); |
|
case ILOpCode.Arglist: |
|
return Push(new Arglist()); |
|
case ILOpCode.Beq: |
|
return DecodeComparisonBranch(false, ComparisonKind.Equality); |
|
case ILOpCode.Beq_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.Equality); |
|
case ILOpCode.Bge: |
|
return DecodeComparisonBranch(false, ComparisonKind.GreaterThanOrEqual); |
|
case ILOpCode.Bge_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.GreaterThanOrEqual); |
|
case ILOpCode.Bge_Un: |
|
return DecodeComparisonBranch(false, ComparisonKind.GreaterThanOrEqual, un: true); |
|
case ILOpCode.Bge_Un_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.GreaterThanOrEqual, un: true); |
|
case ILOpCode.Bgt: |
|
return DecodeComparisonBranch(false, ComparisonKind.GreaterThan); |
|
case ILOpCode.Bgt_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.GreaterThan); |
|
case ILOpCode.Bgt_Un: |
|
return DecodeComparisonBranch(false, ComparisonKind.GreaterThan, un: true); |
|
case ILOpCode.Bgt_Un_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.GreaterThan, un: true); |
|
case ILOpCode.Ble: |
|
return DecodeComparisonBranch(false, ComparisonKind.LessThanOrEqual); |
|
case ILOpCode.Ble_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.LessThanOrEqual); |
|
case ILOpCode.Ble_Un: |
|
return DecodeComparisonBranch(false, ComparisonKind.LessThanOrEqual, un: true); |
|
case ILOpCode.Ble_Un_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.LessThanOrEqual, un: true); |
|
case ILOpCode.Blt: |
|
return DecodeComparisonBranch(false, ComparisonKind.LessThan); |
|
case ILOpCode.Blt_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.LessThan); |
|
case ILOpCode.Blt_Un: |
|
return DecodeComparisonBranch(false, ComparisonKind.LessThan, un: true); |
|
case ILOpCode.Blt_Un_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.LessThan, un: true); |
|
case ILOpCode.Bne_Un: |
|
return DecodeComparisonBranch(false, ComparisonKind.Inequality, un: true); |
|
case ILOpCode.Bne_Un_S: |
|
return DecodeComparisonBranch(true, ComparisonKind.Inequality, un: true); |
|
case ILOpCode.Br: |
|
return DecodeUnconditionalBranch(false); |
|
case ILOpCode.Br_S: |
|
return DecodeUnconditionalBranch(true); |
|
case ILOpCode.Break: |
|
return new DebugBreak(); |
|
case ILOpCode.Brfalse: |
|
return DecodeConditionalBranch(false, true); |
|
case ILOpCode.Brfalse_S: |
|
return DecodeConditionalBranch(true, true); |
|
case ILOpCode.Brtrue: |
|
return DecodeConditionalBranch(false, false); |
|
case ILOpCode.Brtrue_S: |
|
return DecodeConditionalBranch(true, false); |
|
case ILOpCode.Call: |
|
return DecodeCall(OpCode.Call); |
|
case ILOpCode.Callvirt: |
|
return DecodeCall(OpCode.CallVirt); |
|
case ILOpCode.Calli: |
|
throw new NotImplementedException(); |
|
case ILOpCode.Ceq: |
|
return Push(Comparison(ComparisonKind.Equality)); |
|
case ILOpCode.Cgt: |
|
return Push(Comparison(ComparisonKind.GreaterThan)); |
|
case ILOpCode.Cgt_Un: |
|
return Push(Comparison(ComparisonKind.GreaterThan, un: true)); |
|
case ILOpCode.Clt: |
|
return Push(Comparison(ComparisonKind.LessThan)); |
|
case ILOpCode.Clt_Un: |
|
return Push(Comparison(ComparisonKind.LessThan, un: true)); |
|
case ILOpCode.Ckfinite: |
|
return new Ckfinite(Peek()); |
|
case ILOpCode.Conv_I1: |
|
return Push(new Conv(Pop(), PrimitiveType.I1, false, Sign.None)); |
|
case ILOpCode.Conv_I2: |
|
return Push(new Conv(Pop(), PrimitiveType.I2, false, Sign.None)); |
|
case ILOpCode.Conv_I4: |
|
return Push(new Conv(Pop(), PrimitiveType.I4, false, Sign.None)); |
|
case ILOpCode.Conv_I8: |
|
return Push(new Conv(Pop(), PrimitiveType.I8, false, Sign.None)); |
|
case ILOpCode.Conv_R4: |
|
return Push(new Conv(Pop(), PrimitiveType.R4, false, Sign.Signed)); |
|
case ILOpCode.Conv_R8: |
|
return Push(new Conv(Pop(), PrimitiveType.R8, false, Sign.Signed)); |
|
case ILOpCode.Conv_U1: |
|
return Push(new Conv(Pop(), PrimitiveType.U1, false, Sign.None)); |
|
case ILOpCode.Conv_U2: |
|
return Push(new Conv(Pop(), PrimitiveType.U2, false, Sign.None)); |
|
case ILOpCode.Conv_U4: |
|
return Push(new Conv(Pop(), PrimitiveType.U4, false, Sign.None)); |
|
case ILOpCode.Conv_U8: |
|
return Push(new Conv(Pop(), PrimitiveType.U8, false, Sign.None)); |
|
case ILOpCode.Conv_I: |
|
return Push(new Conv(Pop(), PrimitiveType.I, false, Sign.None)); |
|
case ILOpCode.Conv_U: |
|
return Push(new Conv(Pop(), PrimitiveType.U, false, Sign.None)); |
|
case ILOpCode.Conv_R_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.R8, false, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_I1: |
|
return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_I2: |
|
return Push(new Conv(Pop(), PrimitiveType.I2, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_I4: |
|
return Push(new Conv(Pop(), PrimitiveType.I4, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_I8: |
|
return Push(new Conv(Pop(), PrimitiveType.I8, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_U1: |
|
return Push(new Conv(Pop(), PrimitiveType.U1, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_U2: |
|
return Push(new Conv(Pop(), PrimitiveType.U2, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_U4: |
|
return Push(new Conv(Pop(), PrimitiveType.U4, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_U8: |
|
return Push(new Conv(Pop(), PrimitiveType.U8, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_I: |
|
return Push(new Conv(Pop(), PrimitiveType.I, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_U: |
|
return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Signed)); |
|
case ILOpCode.Conv_Ovf_I1_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_I2_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.I2, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_I4_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.I4, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_I8_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.I8, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_U1_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.U1, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_U2_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.U2, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_U4_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.U4, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_U8_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.U8, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_I_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.I, true, Sign.Unsigned)); |
|
case ILOpCode.Conv_Ovf_U_Un: |
|
return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Unsigned)); |
|
case ILOpCode.Cpblk: |
|
throw new NotImplementedException(); |
|
case ILOpCode.Div: |
|
return BinaryNumeric(OpCode.Div, false, Sign.Signed); |
|
case ILOpCode.Div_Un: |
|
return BinaryNumeric(OpCode.Div, false, Sign.Unsigned); |
|
case ILOpCode.Dup: |
|
return Push(Peek()); |
|
case ILOpCode.Endfilter: |
|
case ILOpCode.Endfinally: |
|
return new Leave(null); |
|
case ILOpCode.Initblk: |
|
throw new NotImplementedException(); |
|
case ILOpCode.Jmp: |
|
throw new NotImplementedException(); |
|
case ILOpCode.Ldarg: |
|
return Push(Ldarg(reader.ReadUInt16())); |
|
case ILOpCode.Ldarg_S: |
|
return Push(Ldarg(reader.ReadByte())); |
|
case ILOpCode.Ldarg_0: |
|
case ILOpCode.Ldarg_1: |
|
case ILOpCode.Ldarg_2: |
|
case ILOpCode.Ldarg_3: |
|
return Push(Ldarg(ilOpCode - ILOpCode.Ldarg_0)); |
|
case ILOpCode.Ldarga: |
|
return Push(Ldarga(reader.ReadUInt16())); |
|
case ILOpCode.Ldarga_S: |
|
return Push(Ldarga(reader.ReadByte())); |
|
case ILOpCode.Ldc_I4: |
|
return Push(new LdcI4(reader.ReadInt32())); |
|
case ILOpCode.Ldc_I8: |
|
return Push(new LdcI8(reader.ReadInt64())); |
|
case ILOpCode.Ldc_R4: |
|
return Push(new LdcF(reader.ReadSingle())); |
|
case ILOpCode.Ldc_R8: |
|
return Push(new LdcF(reader.ReadDouble())); |
|
case ILOpCode.Ldc_I4_M1: |
|
case ILOpCode.Ldc_I4_0: |
|
case ILOpCode.Ldc_I4_1: |
|
case ILOpCode.Ldc_I4_2: |
|
case ILOpCode.Ldc_I4_3: |
|
case ILOpCode.Ldc_I4_4: |
|
case ILOpCode.Ldc_I4_5: |
|
case ILOpCode.Ldc_I4_6: |
|
case ILOpCode.Ldc_I4_7: |
|
case ILOpCode.Ldc_I4_8: |
|
return Push(new LdcI4((int)ilOpCode - (int)ILOpCode.Ldc_I4_0)); |
|
case ILOpCode.Ldc_I4_S: |
|
return Push(new LdcI4(reader.ReadSByte())); |
|
case ILOpCode.Ldnull: |
|
return Push(new LdNull()); |
|
case ILOpCode.Ldstr: |
|
return Push(DecodeLdstr()); |
|
case ILOpCode.Ldftn: |
|
return Push(new LdFtn(ReadAndDecodeMethodReference())); |
|
case ILOpCode.Ldind_I1: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.SByte))); |
|
case ILOpCode.Ldind_I2: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int16))); |
|
case ILOpCode.Ldind_I4: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int32))); |
|
case ILOpCode.Ldind_I8: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int64))); |
|
case ILOpCode.Ldind_U1: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Byte))); |
|
case ILOpCode.Ldind_U2: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt16))); |
|
case ILOpCode.Ldind_U4: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt32))); |
|
case ILOpCode.Ldind_R4: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Single))); |
|
case ILOpCode.Ldind_R8: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Double))); |
|
case ILOpCode.Ldind_I: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.IntPtr))); |
|
case ILOpCode.Ldind_Ref: |
|
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Object))); |
|
case ILOpCode.Ldloc: |
|
return Push(Ldloc(reader.ReadUInt16())); |
|
case ILOpCode.Ldloc_S: |
|
return Push(Ldloc(reader.ReadByte())); |
|
case ILOpCode.Ldloc_0: |
|
case ILOpCode.Ldloc_1: |
|
case ILOpCode.Ldloc_2: |
|
case ILOpCode.Ldloc_3: |
|
return Push(Ldloc(ilOpCode - ILOpCode.Ldloc_0)); |
|
case ILOpCode.Ldloca: |
|
return Push(Ldloca(reader.ReadUInt16())); |
|
case ILOpCode.Ldloca_S: |
|
return Push(Ldloca(reader.ReadByte())); |
|
case ILOpCode.Leave: |
|
return DecodeUnconditionalBranch(false, isLeave: true); |
|
case ILOpCode.Leave_S: |
|
return DecodeUnconditionalBranch(true, isLeave: true); |
|
case ILOpCode.Localloc: |
|
return Push(new LocAlloc(Pop())); |
|
case ILOpCode.Mul: |
|
return BinaryNumeric(OpCode.Mul, false, Sign.None); |
|
case ILOpCode.Mul_Ovf: |
|
return BinaryNumeric(OpCode.Mul, true, Sign.Signed); |
|
case ILOpCode.Mul_Ovf_Un: |
|
return BinaryNumeric(OpCode.Mul, true, Sign.Unsigned); |
|
case ILOpCode.Neg: |
|
return Neg(); |
|
case ILOpCode.Newobj: |
|
return DecodeCall(OpCode.NewObj); |
|
case ILOpCode.Nop: |
|
return new Nop(); |
|
case ILOpCode.Not: |
|
return Push(new BitNot(Pop())); |
|
case ILOpCode.Or: |
|
return BinaryNumeric(OpCode.BitOr); |
|
case ILOpCode.Pop: |
|
Pop(); |
|
return new Nop(); |
|
case ILOpCode.Rem: |
|
return BinaryNumeric(OpCode.Rem, false, Sign.Signed); |
|
case ILOpCode.Rem_Un: |
|
return BinaryNumeric(OpCode.Rem, false, Sign.Unsigned); |
|
case ILOpCode.Ret: |
|
return Return(); |
|
case ILOpCode.Shl: |
|
return BinaryNumeric(OpCode.Shl, false, Sign.None); |
|
case ILOpCode.Shr: |
|
return BinaryNumeric(OpCode.Shr, false, Sign.Signed); |
|
case ILOpCode.Shr_Un: |
|
return BinaryNumeric(OpCode.Shr, false, Sign.Unsigned); |
|
case ILOpCode.Starg: |
|
return Starg(reader.ReadUInt16()); |
|
case ILOpCode.Starg_S: |
|
return Starg(reader.ReadByte()); |
|
case ILOpCode.Stind_I1: |
|
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.SByte)); |
|
case ILOpCode.Stind_I2: |
|
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int16)); |
|
case ILOpCode.Stind_I4: |
|
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int32)); |
|
case ILOpCode.Stind_I8: |
|
return new StObj(value: Pop(StackType.I8), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int64)); |
|
case ILOpCode.Stind_R4: |
|
return new StObj(value: Pop(StackType.F), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Single)); |
|
case ILOpCode.Stind_R8: |
|
return new StObj(value: Pop(StackType.F), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Double)); |
|
case ILOpCode.Stind_I: |
|
return new StObj(value: Pop(StackType.I), target: PopPointer(), type: compilation.FindType(KnownTypeCode.IntPtr)); |
|
case ILOpCode.Stind_Ref: |
|
return new StObj(value: Pop(StackType.O), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Object)); |
|
case ILOpCode.Stloc: |
|
return Stloc(reader.ReadUInt16()); |
|
case ILOpCode.Stloc_S: |
|
return Stloc(reader.ReadByte()); |
|
case ILOpCode.Stloc_0: |
|
case ILOpCode.Stloc_1: |
|
case ILOpCode.Stloc_2: |
|
case ILOpCode.Stloc_3: |
|
return Stloc(ilOpCode - ILOpCode.Stloc_0); |
|
case ILOpCode.Sub: |
|
return BinaryNumeric(OpCode.Sub, false, Sign.None); |
|
case ILOpCode.Sub_Ovf: |
|
return BinaryNumeric(OpCode.Sub, true, Sign.Signed); |
|
case ILOpCode.Sub_Ovf_Un: |
|
return BinaryNumeric(OpCode.Sub, true, Sign.Unsigned); |
|
case ILOpCode.Switch: |
|
return DecodeSwitch(); |
|
case ILOpCode.Xor: |
|
return BinaryNumeric(OpCode.BitXor); |
|
case ILOpCode.Box: |
|
{ |
|
var type = ReadAndDecodeTypeReference(); |
|
return Push(new Box(Pop(type.GetStackType()), type)); |
|
} |
|
case ILOpCode.Castclass: |
|
return Push(new CastClass(Pop(StackType.O), ReadAndDecodeTypeReference())); |
|
case ILOpCode.Cpobj: |
|
{ |
|
var type = ReadAndDecodeTypeReference(); |
|
var ld = new LdObj(PopPointer(), type); |
|
return new StObj(PopPointer(), ld, type); |
|
} |
|
case ILOpCode.Initobj: |
|
return InitObj(PopPointer(), ReadAndDecodeTypeReference()); |
|
case ILOpCode.Isinst: |
|
return Push(new IsInst(Pop(StackType.O), ReadAndDecodeTypeReference())); |
|
case ILOpCode.Ldelem: |
|
return LdElem(ReadAndDecodeTypeReference()); |
|
case ILOpCode.Ldelem_I1: |
|
return LdElem(compilation.FindType(KnownTypeCode.SByte)); |
|
case ILOpCode.Ldelem_I2: |
|
return LdElem(compilation.FindType(KnownTypeCode.Int16)); |
|
case ILOpCode.Ldelem_I4: |
|
return LdElem(compilation.FindType(KnownTypeCode.Int32)); |
|
case ILOpCode.Ldelem_I8: |
|
return LdElem(compilation.FindType(KnownTypeCode.Int64)); |
|
case ILOpCode.Ldelem_U1: |
|
return LdElem(compilation.FindType(KnownTypeCode.Byte)); |
|
case ILOpCode.Ldelem_U2: |
|
return LdElem(compilation.FindType(KnownTypeCode.UInt16)); |
|
case ILOpCode.Ldelem_U4: |
|
return LdElem(compilation.FindType(KnownTypeCode.UInt32)); |
|
case ILOpCode.Ldelem_R4: |
|
return LdElem(compilation.FindType(KnownTypeCode.Single)); |
|
case ILOpCode.Ldelem_R8: |
|
return LdElem(compilation.FindType(KnownTypeCode.Double)); |
|
case ILOpCode.Ldelem_I: |
|
return LdElem(compilation.FindType(KnownTypeCode.IntPtr)); |
|
case ILOpCode.Ldelem_Ref: |
|
return LdElem(compilation.FindType(KnownTypeCode.Object)); |
|
case ILOpCode.Ldelema: |
|
return Push(new LdElema(indices: Pop(), array: Pop(), type: ReadAndDecodeTypeReference())); |
|
case ILOpCode.Ldfld: |
|
return Push(new LdFld(Pop(), ReadAndDecodeFieldReference())); |
|
case ILOpCode.Ldflda: |
|
return Push(new LdFlda(Pop(), ReadAndDecodeFieldReference())); |
|
case ILOpCode.Stfld: |
|
{ |
|
var field = ReadAndDecodeFieldReference(); |
|
return new StFld(value: Pop(field.Type.GetStackType()), target: Pop(), field: field); |
|
} |
|
case ILOpCode.Ldlen: |
|
return Push(new LdLen(StackType.I, Pop())); |
|
case ILOpCode.Ldobj: |
|
return Push(new LdObj(PopPointer(), ReadAndDecodeTypeReference())); |
|
case ILOpCode.Ldsfld: |
|
return Push(new LdsFld(ReadAndDecodeFieldReference())); |
|
case ILOpCode.Ldsflda: |
|
return Push(new LdsFlda(ReadAndDecodeFieldReference())); |
|
case ILOpCode.Stsfld: |
|
{ |
|
var field = ReadAndDecodeFieldReference(); |
|
return new StsFld(Pop(field.Type.GetStackType()), field); |
|
} |
|
case ILOpCode.Ldtoken: |
|
return Push(LdToken(ReadAndDecodeMetadataToken())); |
|
case ILOpCode.Ldvirtftn: |
|
return Push(new LdVirtFtn(Pop(), ReadAndDecodeMethodReference())); |
|
case ILOpCode.Mkrefany: |
|
return Push(new MakeRefAny(PopPointer(), ReadAndDecodeTypeReference())); |
|
case ILOpCode.Newarr: |
|
return Push(new NewArr(ReadAndDecodeTypeReference(), Pop())); |
|
case ILOpCode.Refanytype: |
|
return Push(new RefAnyType(Pop())); |
|
case ILOpCode.Refanyval: |
|
return Push(new RefAnyValue(Pop(), ReadAndDecodeTypeReference())); |
|
case ILOpCode.Rethrow: |
|
return new Rethrow(); |
|
case ILOpCode.Sizeof: |
|
return Push(new SizeOf(ReadAndDecodeTypeReference())); |
|
case ILOpCode.Stelem: |
|
return StElem(ReadAndDecodeTypeReference()); |
|
case ILOpCode.Stelem_I1: |
|
return StElem(compilation.FindType(KnownTypeCode.SByte)); |
|
case ILOpCode.Stelem_I2: |
|
return StElem(compilation.FindType(KnownTypeCode.Int16)); |
|
case ILOpCode.Stelem_I4: |
|
return StElem(compilation.FindType(KnownTypeCode.Int32)); |
|
case ILOpCode.Stelem_I8: |
|
return StElem(compilation.FindType(KnownTypeCode.Int64)); |
|
case ILOpCode.Stelem_R4: |
|
return StElem(compilation.FindType(KnownTypeCode.Single)); |
|
case ILOpCode.Stelem_R8: |
|
return StElem(compilation.FindType(KnownTypeCode.Double)); |
|
case ILOpCode.Stelem_I: |
|
return StElem(compilation.FindType(KnownTypeCode.IntPtr)); |
|
case ILOpCode.Stelem_Ref: |
|
return StElem(compilation.FindType(KnownTypeCode.Object)); |
|
case ILOpCode.Stobj: |
|
{ |
|
var type = ReadAndDecodeTypeReference(); |
|
return new StObj(value: Pop(type.GetStackType()), target: PopPointer(), type: type); |
|
} |
|
case ILOpCode.Throw: |
|
return new Throw(Pop()); |
|
case ILOpCode.Unbox: |
|
return Push(new Unbox(Pop(), ReadAndDecodeTypeReference())); |
|
case ILOpCode.Unbox_Any: |
|
return Push(new UnboxAny(Pop(), ReadAndDecodeTypeReference())); |
|
default: |
|
return new InvalidInstruction("Unknown opcode: " + ilOpCode.ToString()); |
|
} |
|
} |
|
|
|
|
|
StackType PeekStackType() |
|
{ |
|
if (currentStack.IsEmpty) |
|
return StackType.Unknown; |
|
else |
|
return currentStack.Peek().StackType; |
|
} |
|
|
|
class CollectStackVariablesVisitor : ILVisitor<ILInstruction> |
|
{ |
|
readonly UnionFind<ILVariable> unionFind; |
|
internal readonly HashSet<ILVariable> variables = new HashSet<ILVariable>(); |
|
|
|
public CollectStackVariablesVisitor(UnionFind<ILVariable> unionFind) |
|
{ |
|
Debug.Assert(unionFind != null); |
|
this.unionFind = unionFind; |
|
} |
|
|
|
protected override ILInstruction Default(ILInstruction inst) |
|
{ |
|
foreach (var child in inst.Children) { |
|
var newChild = child.AcceptVisitor(this); |
|
if (newChild != child) |
|
child.ReplaceWith(newChild); |
|
} |
|
return inst; |
|
} |
|
|
|
protected internal override ILInstruction VisitLdLoc(LdLoc inst) |
|
{ |
|
base.VisitLdLoc(inst); |
|
if (inst.Variable.Kind == VariableKind.StackSlot) { |
|
var variable = unionFind.Find(inst.Variable); |
|
if (variables.Add(variable)) |
|
variable.Name = "S_" + (variables.Count - 1); |
|
return new LdLoc(variable) { ILRange = inst.ILRange }; |
|
} |
|
return inst; |
|
} |
|
|
|
protected internal override ILInstruction VisitStLoc(StLoc inst) |
|
{ |
|
base.VisitStLoc(inst); |
|
if (inst.Variable.Kind == VariableKind.StackSlot) { |
|
var variable = unionFind.Find(inst.Variable); |
|
if (variables.Add(variable)) |
|
variable.Name = "S_" + (variables.Count - 1); |
|
return new StLoc(variable, inst.Value) { ILRange = inst.ILRange }; |
|
} |
|
return inst; |
|
} |
|
} |
|
|
|
ILInstruction Push(ILInstruction inst) |
|
{ |
|
Debug.Assert(inst.ResultType != StackType.Void); |
|
IType type = compilation.FindType(inst.ResultType.ToKnownTypeCode()); |
|
var v = new ILVariable(VariableKind.StackSlot, type, inst.ResultType, inst.ILRange.Start); |
|
v.Name = "S_" + inst.ILRange.Start.ToString("x4"); |
|
currentStack = currentStack.Push(v); |
|
return new StLoc(v, inst); |
|
} |
|
|
|
ILInstruction Peek() |
|
{ |
|
if (currentStack.IsEmpty) { |
|
return new InvalidInstruction("Stack underflow") { ILRange = new Interval(reader.Position, reader.Position) }; |
|
} |
|
return new LdLoc(currentStack.Peek()); |
|
} |
|
|
|
ILInstruction Pop() |
|
{ |
|
if (currentStack.IsEmpty) { |
|
return new InvalidInstruction("Stack underflow") { ILRange = new Interval(reader.Position, reader.Position) }; |
|
} |
|
ILVariable v; |
|
currentStack = currentStack.Pop(out v); |
|
return new LdLoc(v); |
|
} |
|
|
|
ILInstruction Pop(StackType expectedType) |
|
{ |
|
ILInstruction inst = Pop(); |
|
if (expectedType != inst.ResultType) { |
|
if (expectedType == StackType.I && inst.ResultType == StackType.I4) { |
|
inst = new Conv(inst, PrimitiveType.I, false, Sign.None); |
|
} else if (inst is InvalidInstruction) { |
|
((InvalidInstruction)inst).ExpectedResultType = expectedType; |
|
} else { |
|
Warn($"Expected {expectedType}, but got {inst.ResultType}"); |
|
} |
|
} |
|
return inst; |
|
} |
|
|
|
ILInstruction PopPointer() |
|
{ |
|
ILInstruction inst = Pop(); |
|
switch (inst.ResultType) { |
|
case StackType.I4: |
|
return new Conv(inst, PrimitiveType.I, false, Sign.None); |
|
case StackType.I: |
|
case StackType.Ref: |
|
case StackType.Unknown: |
|
return inst; |
|
default: |
|
Warn("Expected native int or pointer, but got " + inst.ResultType); |
|
return inst; |
|
} |
|
} |
|
|
|
private ILInstruction Return() |
|
{ |
|
if (methodReturnStackType == StackType.Void) |
|
return new IL.Return(); |
|
else |
|
return new IL.Return(Pop(methodReturnStackType)); |
|
} |
|
|
|
private ILInstruction DecodeLdstr() |
|
{ |
|
var metadataToken = ReadMetadataToken(ref reader); |
|
return new LdStr(body.LookupStringToken(metadataToken)); |
|
} |
|
|
|
private ILInstruction Ldarg(ushort v) |
|
{ |
|
return new LdLoc(parameterVariables[v]); |
|
} |
|
|
|
private ILInstruction Ldarga(ushort v) |
|
{ |
|
return new LdLoca(parameterVariables[v]); |
|
} |
|
|
|
private ILInstruction Starg(ushort v) |
|
{ |
|
return new StLoc(parameterVariables[v], Pop(parameterVariables[v].StackType)); |
|
} |
|
|
|
private ILInstruction Ldloc(ushort v) |
|
{ |
|
return new LdLoc(localVariables[v]); |
|
} |
|
|
|
private ILInstruction Ldloca(ushort v) |
|
{ |
|
return new LdLoca(localVariables[v]); |
|
} |
|
|
|
private ILInstruction Stloc(ushort v) |
|
{ |
|
return new StLoc(localVariables[v], Pop(localVariables[v].StackType)); |
|
} |
|
|
|
private ILInstruction LdElem(IType type) |
|
{ |
|
return Push(new LdObj(new LdElema(indices: Pop(), array: Pop(), type: type), type)); |
|
} |
|
|
|
private ILInstruction StElem(IType type) |
|
{ |
|
var value = Pop(type.GetStackType()); |
|
var index = Pop(); |
|
var array = Pop(); |
|
return new StObj(new LdElema(type, array, index), value, type); |
|
} |
|
|
|
ILInstruction InitObj(ILInstruction target, IType type) |
|
{ |
|
return new StObj(target, new DefaultValue(type), type); |
|
} |
|
|
|
private ILInstruction DecodeConstrainedCall() |
|
{ |
|
var typeRef = ReadAndDecodeTypeReference(); |
|
var inst = DecodeInstruction(); |
|
var call = UnpackPush(inst) as CallInstruction; |
|
if (call != null) |
|
call.ConstrainedTo = typeRef; |
|
else |
|
Warn("Ignored invalid 'constrained' prefix"); |
|
return inst; |
|
} |
|
|
|
private ILInstruction DecodeTailCall() |
|
{ |
|
var inst = DecodeInstruction(); |
|
var call = UnpackPush(inst) as CallInstruction; |
|
if (call != null) |
|
call.IsTail = true; |
|
else |
|
Warn("Ignored invalid 'tail' prefix"); |
|
return inst; |
|
} |
|
|
|
private ILInstruction DecodeUnaligned() |
|
{ |
|
byte alignment = reader.ReadByte(); |
|
var inst = DecodeInstruction(); |
|
var sup = UnpackPush(inst) as ISupportsUnalignedPrefix; |
|
if (sup != null) |
|
sup.UnalignedPrefix = alignment; |
|
else |
|
Warn("Ignored invalid 'unaligned' prefix"); |
|
return inst; |
|
} |
|
|
|
private ILInstruction DecodeVolatile() |
|
{ |
|
var inst = DecodeInstruction(); |
|
var svp = UnpackPush(inst) as ISupportsVolatilePrefix; |
|
if (svp != null) |
|
svp.IsVolatile = true; |
|
else |
|
Warn("Ignored invalid 'volatile' prefix"); |
|
return inst; |
|
} |
|
|
|
private ILInstruction DecodeReadonly() |
|
{ |
|
var inst = DecodeInstruction(); |
|
var ldelema = UnpackPush(inst) as LdElema; |
|
if (ldelema != null) |
|
ldelema.IsReadOnly = true; |
|
else |
|
Warn("Ignored invalid 'readonly' prefix"); |
|
return inst; |
|
} |
|
|
|
ILInstruction DecodeCall(OpCode opCode) |
|
{ |
|
var method = ReadAndDecodeMethodReference(); |
|
int firstArgument = (opCode != OpCode.NewObj && !method.IsStatic) ? 1 : 0; |
|
var arguments = new ILInstruction[firstArgument + method.Parameters.Count]; |
|
for (int i = method.Parameters.Count - 1; i >= 0; i--) { |
|
arguments[firstArgument + i] = Pop(method.Parameters[i].Type.GetStackType()); |
|
} |
|
if (firstArgument == 1) { |
|
arguments[0] = Pop(); |
|
} |
|
switch (method.DeclaringType.Kind) { |
|
case TypeKind.Array: |
|
var elementType = ((ICSharpCode.NRefactory.TypeSystem.ArrayType)method.DeclaringType).ElementType; |
|
if (opCode == OpCode.NewObj) |
|
return Push(new NewArr(elementType, arguments)); |
|
if (method.Name == "Set") { |
|
var target = arguments[0]; |
|
var value = arguments.Last(); |
|
var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray(); |
|
return new StObj(new LdElema(elementType, target, indices), value, elementType); |
|
} |
|
if (method.Name == "Get") { |
|
var target = arguments[0]; |
|
var indices = arguments.Skip(1).ToArray(); |
|
return Push(new LdObj(new LdElema(elementType, target, indices), elementType)); |
|
} |
|
if (method.Name == "Address") { |
|
var target = arguments[0]; |
|
var indices = arguments.Skip(1).ToArray(); |
|
return Push(new LdElema(elementType, target, indices)); |
|
} |
|
Warn("Unknown method called on array type: " + method.Name); |
|
goto default; |
|
default: |
|
var call = CallInstruction.Create(opCode, method); |
|
call.Arguments.AddRange(arguments); |
|
if (call.ResultType != StackType.Void) |
|
return Push(call); |
|
return call; |
|
} |
|
} |
|
|
|
static int GetPopCount(OpCode callCode, MethodReference methodReference) |
|
{ |
|
int popCount = methodReference.Parameters.Count; |
|
if (callCode != OpCode.NewObj && methodReference.HasThis) |
|
popCount++; |
|
return popCount; |
|
} |
|
|
|
static int GetPopCount(OpCode callCode, IMethod method) |
|
{ |
|
int popCount = method.Parameters.Count; |
|
if (callCode != OpCode.NewObj && !method.IsStatic) |
|
popCount++; |
|
return popCount; |
|
} |
|
|
|
ILInstruction Comparison(ComparisonKind kind, bool un = false) |
|
{ |
|
var right = Pop(); |
|
var left = Pop(); |
|
// make the implicit I4->I conversion explicit: |
|
if (left.ResultType == StackType.I4 && right.ResultType == StackType.I) { |
|
left = new Conv(left, PrimitiveType.I, false, Sign.None); |
|
} else if (left.ResultType == StackType.I && right.ResultType == StackType.I4) { |
|
right = new Conv(right, PrimitiveType.I, false, Sign.None); |
|
} |
|
|
|
// Based on Table 4: Binary Comparison or Branch Operation |
|
if (left.ResultType == StackType.F && right.ResultType == StackType.F) { |
|
if (un) { |
|
// for floats, 'un' means 'unordered' |
|
return new LogicNot(new Comp(kind.Negate(), Sign.None, left, right)); |
|
} else { |
|
return new Comp(kind, Sign.None, left, right); |
|
} |
|
} else if (left.ResultType.IsIntegerType() && !kind.IsEqualityOrInequality()) { |
|
// integer comparison where the sign matters |
|
Debug.Assert(right.ResultType.IsIntegerType()); |
|
return new Comp(kind, un ? Sign.Unsigned : Sign.Signed, left, right); |
|
} else { |
|
// integer equality, object reference or managed reference comparison |
|
return new Comp(kind, Sign.None, left, right); |
|
} |
|
} |
|
|
|
ILInstruction DecodeComparisonBranch(bool shortForm, ComparisonKind kind, bool un = false) |
|
{ |
|
int start = reader.Position - 1; |
|
var condition = Comparison(kind, un); |
|
int target = shortForm ? reader.ReadSByte() : reader.ReadInt32(); |
|
target += reader.Position; |
|
condition.ILRange = new Interval(start, reader.Position); |
|
MarkBranchTarget(target); |
|
return new IfInstruction(condition, new Branch(target)); |
|
} |
|
|
|
ILInstruction DecodeConditionalBranch(bool shortForm, bool negate) |
|
{ |
|
int target = shortForm ? reader.ReadSByte() : reader.ReadInt32(); |
|
target += reader.Position; |
|
ILInstruction condition = Pop(); |
|
switch (condition.ResultType) { |
|
case StackType.O: |
|
// introduce explicit comparison with null |
|
condition = new Comp( |
|
negate ? ComparisonKind.Equality : ComparisonKind.Inequality, |
|
Sign.None, condition, new LdNull()); |
|
break; |
|
case StackType.I: |
|
// introduce explicit comparison with 0 |
|
condition = new Comp( |
|
negate ? ComparisonKind.Equality : ComparisonKind.Inequality, |
|
Sign.None, condition, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None)); |
|
break; |
|
case StackType.I8: |
|
// introduce explicit comparison with 0 |
|
condition = new Comp( |
|
negate ? ComparisonKind.Equality : ComparisonKind.Inequality, |
|
Sign.None, condition, new LdcI8(0)); |
|
break; |
|
default: |
|
if (negate) { |
|
condition = new LogicNot(condition); |
|
} |
|
break; |
|
} |
|
MarkBranchTarget(target); |
|
return new IfInstruction(condition, new Branch(target)); |
|
} |
|
|
|
ILInstruction DecodeUnconditionalBranch(bool shortForm, bool isLeave = false) |
|
{ |
|
int target = shortForm ? reader.ReadSByte() : reader.ReadInt32(); |
|
target += reader.Position; |
|
if (isLeave) { |
|
currentStack = currentStack.Clear(); |
|
} |
|
MarkBranchTarget(target); |
|
return new Branch(target); |
|
} |
|
|
|
void MarkBranchTarget(int targetILOffset) |
|
{ |
|
isBranchTarget[targetILOffset] = true; |
|
StoreStackForOffset(targetILOffset, currentStack); |
|
} |
|
|
|
ILInstruction DecodeSwitch() |
|
{ |
|
uint length = reader.ReadUInt32(); |
|
int baseOffset = 4 * (int)length + reader.Position; |
|
var instr = new SwitchInstruction(Pop(StackType.I4)); |
|
|
|
for (uint i = 0; i < length; i++) { |
|
var section = new SwitchSection(); |
|
section.Labels = new LongSet(i); |
|
int target = baseOffset + reader.ReadInt32(); |
|
MarkBranchTarget(target); |
|
section.Body = new Branch(target); |
|
instr.Sections.Add(section); |
|
} |
|
|
|
return instr; |
|
} |
|
|
|
ILInstruction BinaryNumeric(OpCode opCode, bool checkForOverflow = false, Sign sign = Sign.None) |
|
{ |
|
var right = Pop(); |
|
var left = Pop(); |
|
if (opCode != OpCode.Shl && opCode != OpCode.Shr) { |
|
// make the implicit I4->I conversion explicit: |
|
if (left.ResultType == StackType.I4 && right.ResultType == StackType.I) { |
|
left = new Conv(left, PrimitiveType.I, false, Sign.None); |
|
} else if (left.ResultType == StackType.I && right.ResultType == StackType.I4) { |
|
right = new Conv(right, PrimitiveType.I, false, Sign.None); |
|
} |
|
} |
|
return Push(BinaryNumericInstruction.Create(opCode, left, right, checkForOverflow, sign)); |
|
} |
|
|
|
ILInstruction LdToken(IMetadataTokenProvider token) |
|
{ |
|
if (token is TypeReference) |
|
return new LdTypeToken(typeSystem.Resolve((TypeReference)token)); |
|
if (token is FieldReference) |
|
return new LdMemberToken(typeSystem.Resolve((FieldReference)token)); |
|
if (token is MethodReference) |
|
return new LdMemberToken(typeSystem.Resolve((MethodReference)token)); |
|
throw new NotImplementedException(); |
|
} |
|
} |
|
}
|
|
|