Browse Source

Fix stackalloc[] decompilation.

pull/728/merge
Daniel Grunwald 9 years ago
parent
commit
385048f32c
  1. 63
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  3. 7
      ICSharpCode.Decompiler/CSharp/TranslationContext.cs
  4. 2
      ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs
  5. 14
      ICSharpCode.Decompiler/Tests/TestCases/Correctness/UnsafeCode.cs
  6. 2
      NRefactory

63
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -108,10 +108,13 @@ namespace ICSharpCode.Decompiler.CSharp
return new ExpressionWithResolveResult(expr, exprRR); return new ExpressionWithResolveResult(expr, exprRR);
} }
public TranslatedExpression Translate(ILInstruction inst) public TranslatedExpression Translate(ILInstruction inst, IType typeHint = null)
{ {
Debug.Assert(inst != null); Debug.Assert(inst != null);
var cexpr = inst.AcceptVisitor(this, new TranslationContext()); TranslationContext context = new TranslationContext {
TypeHint = typeHint ?? SpecialType.UnknownType
};
var cexpr = inst.AcceptVisitor(this, context);
#if DEBUG #if DEBUG
if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown) { if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown) {
if (inst.ResultType.IsIntegerType()) { if (inst.ResultType.IsIntegerType()) {
@ -127,7 +130,7 @@ namespace ICSharpCode.Decompiler.CSharp
public TranslatedExpression TranslateCondition(ILInstruction condition) public TranslatedExpression TranslateCondition(ILInstruction condition)
{ {
var expr = Translate(condition); var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean));
return expr.ConvertToBoolean(this); return expr.ConvertToBoolean(this);
} }
@ -206,11 +209,51 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context) protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context)
{ {
var byteType = compilation.FindType(KnownTypeCode.Byte); IType elementType;
TranslatedExpression countExpression = TranslatePointerArgument(inst.Argument, context, out elementType);
countExpression = countExpression.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this);
return new StackAllocExpression { return new StackAllocExpression {
Type = ConvertType(byteType), Type = ConvertType(elementType ?? compilation.FindType(KnownTypeCode.Byte)),
CountExpression = Translate(inst.Argument) CountExpression = countExpression
}.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(byteType))); }.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType)));
}
/// <summary>
/// Translate the argument of an operation that deals with pointers:
/// * undoes the implicit multiplication with `sizeof(elementType)` and returns `elementType`
/// * on failure, translates the whole expression and returns `elementType = null`.
/// </summary>
TranslatedExpression TranslatePointerArgument(ILInstruction countExpr, TranslationContext context, out IType elementType)
{
ILInstruction left;
ILInstruction right;
if (countExpr.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out left, out right)
&& right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchSizeOf(out elementType))
{
return Translate(left);
}
var pointerTypeHint = context.TypeHint as PointerType;
if (pointerTypeHint == null) {
elementType = null;
return Translate(countExpr);
}
ResolveResult sizeofRR = resolver.ResolveSizeOf(pointerTypeHint.ElementType);
if (!(sizeofRR.IsCompileTimeConstant && sizeofRR.ConstantValue is int)) {
elementType = null;
return Translate(countExpr);
}
int typeSize = (int)sizeofRR.ConstantValue;
if (countExpr.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out left, out right)
&& right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchLdcI4(typeSize))
{
elementType = pointerTypeHint.ElementType;
return Translate(left);
}
elementType = null;
return Translate(countExpr);
} }
protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst, TranslationContext context) protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst, TranslationContext context)
@ -346,7 +389,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context) protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context)
{ {
var translatedValue = Translate(inst.Value); var translatedValue = Translate(inst.Value, typeHint: inst.Variable.Type);
if (inst.Variable.Kind == VariableKind.StackSlot && inst.Variable.IsSingleDefinition if (inst.Variable.Kind == VariableKind.StackSlot && inst.Variable.IsSingleDefinition
&& inst.Variable.StackType == translatedValue.Type.GetStackType() && inst.Variable.StackType == translatedValue.Type.GetStackType()
&& translatedValue.Type.Kind != TypeKind.Null && !loadedVariablesSet.Contains(inst.Variable)) { && translatedValue.Type.Kind != TypeKind.Null && !loadedVariablesSet.Contains(inst.Variable)) {
@ -1167,7 +1210,6 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitStObj(StObj inst, TranslationContext context) protected internal override TranslatedExpression VisitStObj(StObj inst, TranslationContext context)
{ {
var target = Translate(inst.Target); var target = Translate(inst.Target);
var value = Translate(inst.Value);
TranslatedExpression result; TranslatedExpression result;
if (target.Expression is DirectionExpression && TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { if (target.Expression is DirectionExpression && TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) {
// we can deference the managed reference by stripping away the 'ref' // we can deference the managed reference by stripping away the 'ref'
@ -1181,6 +1223,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithoutILInstruction() .WithoutILInstruction()
.WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType)); .WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType));
} }
var value = Translate(inst.Value, typeHint: result.Type);
return Assignment(result, value).WithILInstruction(inst); return Assignment(result, value).WithILInstruction(inst);
} }
@ -1261,7 +1304,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context) protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context)
{ {
var obj = compilation.FindType(KnownTypeCode.Object); var obj = compilation.FindType(KnownTypeCode.Object);
var arg = Translate(inst.Argument).ConvertTo(inst.Type, this); var arg = Translate(inst.Argument, typeHint: inst.Type).ConvertTo(inst.Type, this);
return new CastExpression(ConvertType(obj), arg.Expression) return new CastExpression(ConvertType(obj), arg.Expression)
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion)); .WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion));

3
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -99,6 +99,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
pre.CopyAnnotationsFrom(memberReferenceExpression); pre.CopyAnnotationsFrom(memberReferenceExpression);
memberReferenceExpression.ReplaceWith(pre); memberReferenceExpression.ReplaceWith(pre);
} }
var rr = memberReferenceExpression.GetResolveResult();
if (rr != null && rr.Type is PointerType)
return true;
return result; return result;
} }

7
ICSharpCode.Decompiler/CSharp/TranslationContext.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp namespace ICSharpCode.Decompiler.CSharp
{ {
@ -25,6 +26,10 @@ namespace ICSharpCode.Decompiler.CSharp
/// </summary> /// </summary>
public struct TranslationContext public struct TranslationContext
{ {
/// <summary>
/// The expected type during ILAst->C# translation; or <c>SpecialType.Unknown</c>
/// if no specific type is expected.
/// </summary>
public IType TypeHint;
} }
} }

2
ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs

@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.Tests
TestAssembleDecompileCompileOutput("BitNot.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit); TestAssembleDecompileCompileOutput("BitNot.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit);
} }
[Test, Ignore("Fixed statements are broken")] [Test, Ignore("Pointer reference expression is not supported")]
public void UnsafeCode() public void UnsafeCode()
{ {
TestCompileDecompileCompileOutputAll("UnsafeCode.cs"); TestCompileDecompileCompileOutputAll("UnsafeCode.cs");

14
ICSharpCode.Decompiler/Tests/TestCases/Correctness/UnsafeCode.cs

@ -20,6 +20,12 @@ using System;
public class UnsafeCode public class UnsafeCode
{ {
struct SimpleStruct
{
public int X;
public double Y;
}
static void Main() static void Main()
{ {
// TODO: test behavior, or convert this into a pretty-test // TODO: test behavior, or convert this into a pretty-test
@ -145,6 +151,7 @@ public class UnsafeCode
public unsafe string StackAlloc(int count) public unsafe string StackAlloc(int count)
{ {
char* ptr = stackalloc char[count]; char* ptr = stackalloc char[count];
char* ptr2 = stackalloc char[100];
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ptr[i] = (char)i; ptr[i] = (char)i;
@ -152,6 +159,13 @@ public class UnsafeCode
return this.PointerReferenceExpression((double*)ptr); return this.PointerReferenceExpression((double*)ptr);
} }
public unsafe string StackAllocStruct(int count)
{
SimpleStruct* s = stackalloc SimpleStruct[checked(count * 2)];
SimpleStruct* p = stackalloc SimpleStruct[10];
return this.PointerReferenceExpression(&s->Y);
}
public unsafe int* PointerArithmetic(int* p) public unsafe int* PointerArithmetic(int* p)
{ {
return p + 2; return p + 2;

2
NRefactory

@ -1 +1 @@
Subproject commit 19497cfae13c3dcebed7dfeb1cba549e4c2deded Subproject commit 5ca6d4172c8540d01b65d120f46db416d29740e3
Loading…
Cancel
Save