Browse Source

implemented SwitchInstruction, StElem, NewArr instructions

pull/728/head
Siegfried Pammer 10 years ago
parent
commit
6f60dbe04e
  1. 5
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  2. 77
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 27
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 52
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  5. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  6. 39
      ICSharpCode.Decompiler/IL/ILReader.cs
  7. 179
      ICSharpCode.Decompiler/IL/Instructions.cs
  8. 9
      ICSharpCode.Decompiler/IL/Instructions.tt
  9. 158
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  10. 22
      ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs
  11. 6
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  12. 101
      ICSharpCode.Decompiler/Tests/TestCases/Switch.cs
  13. 6
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  14. 18
      ICSharpCode.Decompiler/Util/Interval.cs

5
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -89,5 +89,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -89,5 +89,10 @@ namespace ICSharpCode.Decompiler.CSharp
var rr = node.Annotation<ResolveResult>();
return rr != null ? rr.GetSymbol() : null;
}
public static ResolveResult GetResolveResult(this AstNode node)
{
return node.Annotation<ResolveResult>() ?? ErrorResolveResult.UnknownError;
}
}
}

77
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -106,10 +106,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -106,10 +106,10 @@ namespace ICSharpCode.Decompiler.CSharp
return expr.WithRR(new ResolveResult(variable.Type));
}
ExpressionWithResolveResult ConvertField(IField field)
ExpressionWithResolveResult ConvertField(IField field, ILInstruction target = null)
{
Expression expr = new IdentifierExpression(field.Name);
return expr.WithRR(new ResolveResult(field.ReturnType));
return new MemberReferenceExpression(TranslateTarget(field, target, true), field.Name)
.WithRR(new ResolveResult(field.ReturnType));
}
TranslatedExpression IsType(IsInst inst)
@ -132,6 +132,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -132,6 +132,19 @@ namespace ICSharpCode.Decompiler.CSharp
{
return HandleCallInstruction(inst);
}
protected internal override TranslatedExpression VisitNewArr(NewArr inst)
{
var arg = Translate(inst.Size);
return new ArrayCreateExpression {
Type = ConvertType(inst.Type),
Arguments = {
arg
}
}
.WithILInstruction(inst)
.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, 1), new [] { arg.ResolveResult }, new ResolveResult[0]));
}
protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst)
{
@ -384,26 +397,52 @@ namespace ICSharpCode.Decompiler.CSharp @@ -384,26 +397,52 @@ namespace ICSharpCode.Decompiler.CSharp
{
return HandleCallInstruction(inst);
}
static bool IsDelegateConstruction(CallInstruction inst)
{
return inst.Arguments.Count == 2
&& (inst.Arguments[1].OpCode == OpCode.LdFtn
|| inst.Arguments[1].OpCode == OpCode.LdVirtFtn)
&& inst.Method.DeclaringType.Kind == TypeKind.Delegate;
}
TranslatedExpression HandleDelegateConstruction(CallInstruction inst)
{
var func = (LdFtn)inst.Arguments[1];
var target = TranslateTarget(func.Method, inst.Arguments[0], func.OpCode == OpCode.LdFtn);
return new MemberReferenceExpression(target, func.Method.Name)
.WithILInstruction(inst)
.WithRR(new MemberResolveResult(target.ResolveResult, func.Method));
}
TranslatedExpression TranslateTarget(IMember member, ILInstruction target, bool nonVirtualInvocation)
{
if (!member.IsStatic) {
if (nonVirtualInvocation && target.MatchLdThis() && member.DeclaringTypeDefinition != resolver.CurrentTypeDefinition) {
return new BaseReferenceExpression()
.WithILInstruction(target)
.WithRR(new ThisResolveResult(member.DeclaringType, nonVirtualInvocation));
} else {
return Translate(target).ConvertTo(member.DeclaringType, this);
}
} else {
return new TypeReferenceExpression(ConvertType(member.DeclaringType))
.WithoutILInstruction()
.WithRR(new TypeResolveResult(member.DeclaringType));
}
}
TranslatedExpression HandleCallInstruction(CallInstruction inst)
{
// Used for Call, CallVirt and NewObj
TranslatedExpression target;
if (inst.OpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target
} else if (!inst.Method.IsStatic) {
var argInstruction = inst.Arguments[0];
if (inst.OpCode == OpCode.Call && argInstruction.MatchLdThis()) {
target = new BaseReferenceExpression()
.WithILInstruction(argInstruction)
.WithRR(new ThisResolveResult(inst.Method.DeclaringType, causesNonVirtualInvocation: true));
} else {
target = Translate(argInstruction).ConvertTo(inst.Method.DeclaringType, this);
if (IsDelegateConstruction(inst)) {
return HandleDelegateConstruction(inst);
}
target = default(TranslatedExpression); // no target
} else {
target = new TypeReferenceExpression(ConvertType(inst.Method.DeclaringType))
.WithoutILInstruction()
.WithRR(new TypeResolveResult(inst.Method.DeclaringType));
target = TranslateTarget(inst.Method, inst.Arguments.FirstOrDefault(), inst.OpCode == OpCode.Call);
}
var arguments = inst.Arguments.SelectArray(Translate);
@ -414,6 +453,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -414,6 +453,10 @@ namespace ICSharpCode.Decompiler.CSharp
for (int i = firstParamIndex; i < arguments.Length; i++) {
var parameter = inst.Method.Parameters[i - firstParamIndex];
arguments[i] = arguments[i].ConvertTo(parameter.Type, this);
if (parameter.IsOut && arguments[i].Expression is DirectionExpression) {
((DirectionExpression)arguments[i].Expression).FieldDirection = FieldDirection.Out;
}
}
var argumentResolveResults = arguments.Skip(firstParamIndex).Select(arg => arg.ResolveResult).ToList();
@ -535,12 +578,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -535,12 +578,12 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdFld(LdFld inst)
{
return ConvertField(inst.Field).WithILInstruction(inst);
return ConvertField(inst.Field, inst.Target).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitStFld(StFld inst)
{
return Assignment(ConvertField(inst.Field).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst);
return Assignment(ConvertField(inst.Field, inst.Target).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitLdsFld(LdsFld inst)

27
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -17,14 +17,13 @@ @@ -17,14 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System.Diagnostics;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.CSharp
{
@ -73,6 +72,30 @@ namespace ICSharpCode.Decompiler.CSharp @@ -73,6 +72,30 @@ namespace ICSharpCode.Decompiler.CSharp
var falseStatement = inst.FalseInst.OpCode == OpCode.Nop ? null : Convert(inst.FalseInst);
return new IfElseStatement(condition, trueStatement, falseStatement);
}
CaseLabel CreateTypedCaseLabel(long i, IType type)
{
Expression value;
if (type.IsKnownType(KnownTypeCode.Boolean)) {
value = new PrimitiveExpression(i != 0);
} else {
value = new PrimitiveExpression(CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(type), i, true));
}
return new CaseLabel(value);
}
protected internal override Statement VisitSwitchInstruction(SwitchInstruction inst)
{
var value = exprBuilder.Translate(inst.Value);
var stmt = new SwitchStatement() { Expression = value };
foreach (var section in inst.Sections) {
var astSection = new ICSharpCode.NRefactory.CSharp.SwitchSection();
astSection.CaseLabels.AddRange(section.Labels.Range().Select(i => CreateTypedCaseLabel(i, value.Type)));
astSection.Statements.Add(Convert(section.Body));
stmt.SwitchSections.Add(astSection);
}
return stmt;
}
/// <summary>Target block that a 'continue;' statement would jump to</summary>
Block continueTarget;

52
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp;
@ -40,23 +41,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -40,23 +41,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
},
MemberName = "TypeHandle"
};
TransformContext context;
public override void VisitInvocationExpression(InvocationExpression invocationExpression)
{
base.VisitInvocationExpression(invocationExpression);
ProcessInvocationExpression(invocationExpression);
}
internal static void ProcessInvocationExpression(InvocationExpression invocationExpression)
void ProcessInvocationExpression(InvocationExpression invocationExpression)
{
MethodReference methodRef = invocationExpression.Annotation<MethodReference>();
if (methodRef == null)
var method = invocationExpression.GetSymbol() as IMethod;
if (method == null)
return;
var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b"
if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
{
if (method.Name == "Concat" && method.DeclaringType.FullName == "System.String" && arguments.Length >= 2
&& arguments.All(a => a.GetResolveResult().Type.IsKnownType(KnownTypeCode.String))) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0];
for (int i = 1; i < arguments.Length; i++) {
@ -66,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -66,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return;
}
switch (methodRef.FullName) {
switch (method.FullName) {
case "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)":
if (arguments.Length == 1) {
if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) {
@ -94,7 +97,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -94,7 +97,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
FieldReference field = oldArg.Annotation<FieldReference>();
if (field != null) {
AstType declaringType = ((TypeOfExpression)mre2.Target).Type.Detach();
oldArg.ReplaceWith(declaringType.Member(field.Name).WithAnnotation(field));
oldArg.ReplaceWith(declaringType.Member(field.Name).CopyAnnotationsFrom(oldArg));
invocationExpression.ReplaceWith(mre1.Target);
return;
}
@ -104,35 +107,35 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -104,35 +107,35 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
break;
}
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(methodRef.Name);
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name);
if (bop != null && arguments.Length == 2) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
invocationExpression.ReplaceWith(
new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef)
new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).CopyAnnotationsFrom(invocationExpression)
);
return;
}
UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name);
UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(method.Name);
if (uop != null && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef)
new UnaryOperatorExpression(uop.Value, arguments[0]).CopyAnnotationsFrom(invocationExpression)
);
return;
}
if (methodRef.Name == "op_Explicit" && arguments.Length == 1) {
if (method.Name == "op_Explicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
arguments[0].CastTo(ConvertType(methodRef.ReturnType, methodRef.MethodReturnType))
.WithAnnotation(methodRef)
arguments[0].CastTo(context.TypeSystemAstBuilder.ConvertType(method.ReturnType))
.CopyAnnotationsFrom(invocationExpression)
);
return;
}
if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
if (method.Name == "op_Implicit" && arguments.Length == 1) {
invocationExpression.ReplaceWith(arguments[0]);
return;
}
if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) {
if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]);
return;
}
@ -322,11 +325,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -322,11 +325,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
new TypePattern(typeof(MethodInfo)),
new TypePattern(typeof(ConstructorInfo))
});
static AstType ConvertType(TypeReference parameterType, object ctx)
{
return new SimpleType(parameterType.Name);
}
public override void VisitCastExpression(CastExpression castExpression)
{
@ -334,19 +332,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -334,19 +332,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// Handle methodof
Match m = getMethodOrConstructorFromHandlePattern.Match(castExpression);
if (m.Success) {
MethodReference method = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (m.Has("declaringType")) {
IMethod method = m.Get<AstNode>("method").Single().GetSymbol() as IMethod;
if (m.Has("declaringType") && method != null) {
Expression newNode = m.Get<AstType>("declaringType").Single().Detach().Member(method.Name);
newNode = newNode.Invoke(method.Parameters.Select(p => new TypeReferenceExpression(ConvertType(p.ParameterType, p))));
newNode.AddAnnotation(method);
newNode = newNode.Invoke(method.Parameters.Select(p => new TypeReferenceExpression(context.TypeSystemAstBuilder.ConvertType(p.Type))));
m.Get<AstNode>("method").Single().ReplaceWith(newNode);
}
castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single());
castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single().CopyAnnotationsFrom(castExpression));
}
}
void IAstTransform.Run(AstNode node, TransformContext context)
{
this.context = context;
node.AcceptVisitor(this);
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -100,6 +100,7 @@ @@ -100,6 +100,7 @@
<Compile Include="IL\Instructions\PatternMatching.cs" />
<Compile Include="IL\Instructions\Return.cs" />
<Compile Include="IL\Instructions\SimpleInstruction.cs" />
<Compile Include="IL\Instructions\SwitchInstruction.cs" />
<Compile Include="IL\Instructions\TryInstruction.cs" />
<Compile Include="IL\Instructions\UnaryInstruction.cs" />
<Compile Include="IL\IInlineContext.cs" />

39
ICSharpCode.Decompiler/IL/ILReader.cs

@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL @@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
void Warn(string message)
{
Debug.Fail(message);
//Debug.Fail(message);
}
void ReadInstructions(Dictionary<int, ImmutableArray<StackType>> outputStacks, CancellationToken cancellationToken)
@ -583,7 +583,7 @@ namespace ICSharpCode.Decompiler.IL @@ -583,7 +583,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Sub_Ovf_Un:
return BinaryNumeric(OpCode.Sub, true, Sign.Unsigned);
case ILOpCode.Switch:
throw new NotImplementedException();
return DecodeSwitch();
case ILOpCode.Xor:
return BinaryNumeric(OpCode.BitXor);
case ILOpCode.Box:
@ -656,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL @@ -656,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Mkrefany:
throw new NotImplementedException();
case ILOpCode.Newarr:
throw new NotImplementedException();
return new NewArr(ReadAndDecodeTypeReference(), Pop());
case ILOpCode.Refanytype:
throw new NotImplementedException();
case ILOpCode.Refanyval:
@ -666,15 +666,23 @@ namespace ICSharpCode.Decompiler.IL @@ -666,15 +666,23 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Sizeof:
return new SizeOf(ReadAndDecodeTypeReference());
case ILOpCode.Stelem:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: ReadAndDecodeTypeReference());
case ILOpCode.Stelem_I1:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.SByte));
case ILOpCode.Stelem_I2:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.Int16));
case ILOpCode.Stelem_I4:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.Int32));
case ILOpCode.Stelem_I8:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.Int64));
case ILOpCode.Stelem_R4:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.Single));
case ILOpCode.Stelem_R8:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.Double));
case ILOpCode.Stelem_I:
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.IntPtr));
case ILOpCode.Stelem_Ref:
throw new NotImplementedException();
return StElem(value: Pop(), index: Pop(), array: Pop(), type: compilation.FindType(KnownTypeCode.Object));
case ILOpCode.Stobj:
return new Void(new StObj(value: Pop(), target: Pop(), type: ReadAndDecodeTypeReference()));
case ILOpCode.Throw:
@ -747,6 +755,11 @@ namespace ICSharpCode.Decompiler.IL @@ -747,6 +755,11 @@ namespace ICSharpCode.Decompiler.IL
{
return new LdObj(new LdElema(array, index, type), type);
}
private ILInstruction StElem(ILInstruction array, ILInstruction index, ILInstruction value, IType type)
{
return new Void(new StObj(new LdElema(array, index, type), value, type));
}
private ILInstruction DecodeConstrainedCall()
{
@ -897,6 +910,24 @@ namespace ICSharpCode.Decompiler.IL @@ -897,6 +910,24 @@ namespace ICSharpCode.Decompiler.IL
branchStackDict[targetILOffset] = stack.ToImmutableArray();
}
}
ILInstruction DecodeSwitch()
{
uint length = reader.ReadUInt32();
int baseOffset = 4 * (int)length + reader.Position;
var instr = new SwitchInstruction(Pop());
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)
{

179
ICSharpCode.Decompiler/IL/Instructions.cs

@ -70,6 +70,10 @@ namespace ICSharpCode.Decompiler.IL @@ -70,6 +70,10 @@ namespace ICSharpCode.Decompiler.IL
Leave,
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
IfInstruction,
/// <summary>Switch statement</summary>
SwitchInstruction,
/// <summary>Switch section within a switch statement</summary>
SwitchSection,
/// <summary>Try-catch statement.</summary>
TryCatch,
/// <summary>Catch handler within a try-catch statement.</summary>
@ -158,6 +162,8 @@ namespace ICSharpCode.Decompiler.IL @@ -158,6 +162,8 @@ namespace ICSharpCode.Decompiler.IL
UnboxAny,
/// <summary>Creates an object instance and calls the constructor.</summary>
NewObj,
/// <summary>Creates an array instance.</summary>
NewArr,
/// <summary>Initializes the value at an address.</summary>
InitObj,
/// <summary>Returns the default value for a type.</summary>
@ -777,6 +783,71 @@ namespace ICSharpCode.Decompiler.IL @@ -777,6 +783,71 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>Switch statement</summary>
public sealed partial class SwitchInstruction : ILInstruction
{
public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitSwitchInstruction(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitSwitchInstruction(this);
}
}
/// <summary>Switch section within a switch statement</summary>
public sealed partial class SwitchSection : ILInstruction
{
ILInstruction body;
public ILInstruction Body {
get { return this.body; }
set {
ValidateChild(value);
SetChildInstruction(ref this.body, value, 0);
}
}
protected sealed override int GetChildCount()
{
return 1;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.body;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Body = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (SwitchSection)ShallowClone();
clone.Body = this.body.Clone();
return clone;
}
public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitSwitchSection(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitSwitchSection(this);
}
}
/// <summary>Try-catch statement.</summary>
public sealed partial class TryCatch : TryInstruction
{
@ -2320,6 +2391,87 @@ namespace ICSharpCode.Decompiler.IL @@ -2320,6 +2391,87 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>Creates an array instance.</summary>
public sealed partial class NewArr : ILInstruction
{
public NewArr(IType type, ILInstruction size) : base(OpCode.NewArr)
{
this.type = type;
this.Size = size;
}
readonly IType type;
/// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } }
ILInstruction size;
public ILInstruction Size {
get { return this.size; }
set {
ValidateArgument(value);
SetChildInstruction(ref this.size, value, 0);
}
}
protected sealed override int GetChildCount()
{
return 1;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.size;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Size = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (NewArr)ShallowClone();
clone.Size = this.size.Clone();
return clone;
}
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Size = this.size.Inline(flagsBefore, context);
return this;
}
internal sealed override void TransformStackIntoVariables(TransformStackIntoVariablesState state)
{
Size.TransformStackIntoVariables(state);
}
public override StackType ResultType { get { return StackType.O; } }
protected override InstructionFlags ComputeFlags()
{
return size.Flags | InstructionFlags.MayThrow;
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, type);
output.Write('(');
this.size.WriteTo(output);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitNewArr(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitNewArr(this);
}
}
/// <summary>Initializes the value at an address.</summary>
public sealed partial class InitObj : UnaryInstruction
{
@ -2718,6 +2870,14 @@ namespace ICSharpCode.Decompiler.IL @@ -2718,6 +2870,14 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitSwitchInstruction(SwitchInstruction inst)
{
Default(inst);
}
protected internal virtual void VisitSwitchSection(SwitchSection inst)
{
Default(inst);
}
protected internal virtual void VisitTryCatch(TryCatch inst)
{
Default(inst);
@ -2894,6 +3054,10 @@ namespace ICSharpCode.Decompiler.IL @@ -2894,6 +3054,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitNewArr(NewArr inst)
{
Default(inst);
}
protected internal virtual void VisitInitObj(InitObj inst)
{
Default(inst);
@ -3016,6 +3180,14 @@ namespace ICSharpCode.Decompiler.IL @@ -3016,6 +3180,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitSwitchInstruction(SwitchInstruction inst)
{
return Default(inst);
}
protected internal virtual T VisitSwitchSection(SwitchSection inst)
{
return Default(inst);
}
protected internal virtual T VisitTryCatch(TryCatch inst)
{
return Default(inst);
@ -3192,6 +3364,10 @@ namespace ICSharpCode.Decompiler.IL @@ -3192,6 +3364,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitNewArr(NewArr inst)
{
return Default(inst);
}
protected internal virtual T VisitInitObj(InitObj inst)
{
return Default(inst);
@ -3298,6 +3474,8 @@ namespace ICSharpCode.Decompiler.IL @@ -3298,6 +3474,8 @@ namespace ICSharpCode.Decompiler.IL
"br",
"leave",
"if",
"switch",
"switch.section",
"try.catch",
"try.catch.handler",
"try.finally",
@ -3342,6 +3520,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3342,6 +3520,7 @@ namespace ICSharpCode.Decompiler.IL
"unbox",
"unbox.any",
"newobj",
"newarr",
"initobj",
"default.value",
"throw",

9
ICSharpCode.Decompiler/IL/Instructions.tt

@ -70,6 +70,11 @@ @@ -70,6 +70,11 @@
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.",
@ -170,6 +175,8 @@ @@ -170,6 +175,8 @@
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("newarr", "Creates an array instance.",
CustomClassName("NewArr"), HasTypeOperand, CustomArguments("size"), MayThrow, 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.",
@ -185,7 +192,7 @@ @@ -185,7 +192,7 @@
CustomClassName("LdLen"), CustomArguments("array"), MayThrow, ResultType("I")),
new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), CustomArguments("array", "index"), HasTypeOperand,
MayThrow, ResultType("Ref"), SupportsReadonlyPrefix),
MayThrow, ResultType("Ref"), SupportsReadonlyPrefix)
};
#>
using System;

158
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -0,0 +1,158 @@ @@ -0,0 +1,158 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Linq;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Description of SwitchInstruction.
/// </summary>
partial class SwitchInstruction
{
public SwitchInstruction(ILInstruction value)
: base(OpCode.SwitchInstruction)
{
this.Value = value;
this.Sections = new InstructionCollection<SwitchSection>(this, 1);
}
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 0);
}
}
public readonly InstructionCollection<SwitchSection> Sections;
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
Value = value.Inline(flagsBefore, context);
return this;
}
protected override InstructionFlags ComputeFlags()
{
var sectionFlags = InstructionFlags.None;
foreach (var section in Sections) {
sectionFlags = IfInstruction.CombineFlags(sectionFlags, section.Flags);
}
return value.Flags | sectionFlags;
}
public override void WriteTo(ITextOutput output)
{
output.Write("switch (");
value.WriteTo(output);
output.WriteLine(") {");
output.Indent();
foreach (var section in this.Sections) {
section.WriteTo(output);
output.WriteLine();
}
output.Unindent();
output.Write('}');
}
protected override int GetChildCount()
{
return 1 + Sections.Count;
}
protected override ILInstruction GetChild(int index)
{
if (index == 0)
return value;
return Sections[index - 1];
}
protected override void SetChild(int index, ILInstruction value)
{
if (index == 0)
Value = value;
else
Sections[index - 1] = (SwitchSection)value;
}
internal override void TransformStackIntoVariables(TransformStackIntoVariablesState state)
{
Value.TransformStackIntoVariables(state);
var stateAfterExpression = state.Variables.Clone();
for (int i = 0; i < Sections.Count; i++) {
state.Variables = stateAfterExpression.Clone();
Sections[i] = (SwitchSection)Sections[i].Inline(InstructionFlags.None, state);
Sections[i].TransformStackIntoVariables(state);
if (!Sections[i].HasFlag(InstructionFlags.EndPointUnreachable)) {
state.MergeVariables(state.Variables, stateAfterExpression);
}
}
state.Variables = stateAfterExpression;
}
public override ILInstruction Clone()
{
var clone = new SwitchInstruction(value.Clone());
clone.ILRange = this.ILRange;
clone.Value = value.Clone();
clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone()));
return clone;
}
}
partial class SwitchSection
{
public SwitchSection()
: base(OpCode.SwitchSection)
{
}
public LongSet Labels { get; set; }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// Inlining into switch sections would be madness.
// To keep phase-1 execution semantics consistent with inlining, there's a
// phase-1-boundary around every switch section.
return this;
}
protected override InstructionFlags ComputeFlags()
{
return Block.Phase1Boundary(body.Flags);
}
public override void WriteTo(ITextOutput output)
{
output.Write("case ");
output.Write(Labels.ToString());
output.Write(": ");
body.WriteTo(output);
}
internal override void TransformStackIntoVariables(TransformStackIntoVariablesState state)
{
body.TransformStackIntoVariables(state);
}
}
}

22
ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs

@ -129,17 +129,17 @@ namespace ICSharpCode.Decompiler.IL @@ -129,17 +129,17 @@ namespace ICSharpCode.Decompiler.IL
} else if (inst.ResultType == StackType.Void && stack.Count > 0) {
// For void instructions on non-empty stack, we can create a new inline block (or add to an existing one)
// This works even when inst involves Peek.
ILInstruction headInst = stack.Pop();
Block inlineBlock = headInst as Block;
if (inlineBlock == null || inlineBlock.FinalInstruction.OpCode != OpCode.Pop) {
inlineBlock = new Block {
Instructions = { headInst },
ILRange = new Interval(headInst.ILRange.Start, headInst.ILRange.Start),
FinalInstruction = new Pop(headInst.ResultType)
};
}
inlineBlock.Instructions.Add(inst);
inst = inlineBlock;
// ILInstruction headInst = stack.Pop();
// Block inlineBlock = headInst as Block;
// if (inlineBlock == null || inlineBlock.FinalInstruction.OpCode != OpCode.Pop) {
// inlineBlock = new Block {
// Instructions = { headInst },
// ILRange = new Interval(headInst.ILRange.Start, headInst.ILRange.Start),
// FinalInstruction = new Pop(headInst.ResultType)
// };
// }
// inlineBlock.Instructions.Add(inst);
// inst = inlineBlock;
}
if (inst.HasFlag(InstructionFlags.MayPeek)) {
// Prevent instruction from being inlined if it was peeked at.

6
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -107,15 +107,13 @@ @@ -107,15 +107,13 @@
<Compile Include="TestCases\ControlFlow.cs" />
<Compile Include="TestCases\HelloWorld.cs" />
<Compile Include="TestCases\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Switch.cs" />
<Compile Include="TestRunner.cs" />
<Compile Include="Util\LongSetTests.cs" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="ILTransforms" />
<Folder Include="Util" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

101
ICSharpCode.Decompiler/Tests/TestCases/Switch.cs

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// 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;
public static class Switch
{
public static void Main()
{
string[] args = { "First case", "Else" };
TestCase(ShortSwitchOverString, args);
}
static void TestCase(Func<string, string> target, params string[] args)
{
foreach (var arg in args) {
Console.WriteLine(target(arg));
}
}
public static string ShortSwitchOverString(string text)
{
switch (text) {
case "First case":
return "Text";
default:
return "Default";
}
}
public static string SwitchOverString1(string text)
{
switch (text) {
case "First case":
return "Text1";
case "Second case":
case "2nd case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
case null:
return null;
default:
return "Default";
}
}
public static string SwitchOverString2()
{
switch (Environment.UserName) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
default:
return "Default";
}
}
public static string SwitchOverBool(bool b)
{
switch (b) {
case true:
return bool.TrueString;
case false:
return bool.FalseString;
default:
return null;
}
}
}

6
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -52,6 +52,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -52,6 +52,12 @@ namespace ICSharpCode.Decompiler.Tests
TestCompileDecompileCompileOutputAll("PropertiesAndEvents.cs");
}
[Test]
public void Switch()
{
TestCompileDecompileCompileOutputAll("Switch.cs");
}
void TestCompileDecompileCompileOutputAll(string testFileName)
{
TestCompileDecompileCompileOutput(testFileName, CompilerOptions.None);

18
ICSharpCode.Decompiler/Util/Interval.cs

@ -140,12 +140,18 @@ namespace ICSharpCode.Decompiler @@ -140,12 +140,18 @@ namespace ICSharpCode.Decompiler
return !(lhs == rhs);
}
#endregion
public IEnumerable<long> Range()
{
for (long i = Start; i < End; i++)
yield return i;
}
}
/// <summary>
/// An immutable set of longs, that is implemented as a list of intervals.
/// </summary>
struct LongSet
public struct LongSet
{
public readonly ImmutableArray<LongInterval> Intervals;
@ -154,6 +160,11 @@ namespace ICSharpCode.Decompiler @@ -154,6 +160,11 @@ namespace ICSharpCode.Decompiler
this.Intervals = intervals;
}
public LongSet(long value)
: this(ImmutableArray.Create(new LongInterval(value, unchecked(value + 1))))
{
}
public bool IsEmpty
{
get { return Intervals.IsDefaultOrEmpty; }
@ -184,6 +195,11 @@ namespace ICSharpCode.Decompiler @@ -184,6 +195,11 @@ namespace ICSharpCode.Decompiler
return min;
}
public IEnumerable<long> Range()
{
return Intervals.SelectMany(i => i.Range());
}
public override string ToString()
{
return string.Join(",", Intervals);

Loading…
Cancel
Save