Browse Source

Support fixed statement with multiple initializers.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
3ed0de81f7
  1. 20
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 8
      ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs
  3. 13
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  4. 85
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  5. 9
      ICSharpCode.Decompiler/Tests/UnsafeCode.cs

20
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -178,17 +178,17 @@ namespace ICSharpCode.Decompiler.Ast
yield return tryCatchStmt; yield return tryCatchStmt;
} else if (node is ILFixedStatement) { } else if (node is ILFixedStatement) {
ILFixedStatement fixedNode = (ILFixedStatement)node; ILFixedStatement fixedNode = (ILFixedStatement)node;
ILVariable v;
ILExpression init;
if (!fixedNode.Initializer.Match(ILCode.Stloc, out v, out init))
throw new InvalidOperationException("Fixed initializer must be an assignment to a local variable");
FixedStatement fixedStatement = new FixedStatement(); FixedStatement fixedStatement = new FixedStatement();
fixedStatement.Type = AstBuilder.ConvertType(v.Type); foreach (ILExpression initializer in fixedNode.Initializers) {
fixedStatement.Variables.Add( Debug.Assert(initializer.Code == ILCode.Stloc);
new VariableInitializer { ILVariable v = (ILVariable)initializer.Operand;
Name = v.Name, fixedStatement.Variables.Add(
Initializer = (Expression)TransformExpression(init) new VariableInitializer {
}.WithAnnotation(v)); Name = v.Name,
Initializer = (Expression)TransformExpression(initializer.Arguments[0])
}.WithAnnotation(v));
}
fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type);
fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock);
yield return fixedStatement; yield return fixedStatement;
} else if (node is ILBlock) { } else if (node is ILBlock) {

8
ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs

@ -55,8 +55,12 @@ namespace ICSharpCode.Decompiler.Ast
return node; return node;
FixedStatement fixedStatement = node as FixedStatement; FixedStatement fixedStatement = node as FixedStatement;
if (fixedStatement != null && fixedStatement.Variables.Single().Name == name) if (fixedStatement != null) {
return null; // no need to introduce the variable here foreach (VariableInitializer v in fixedStatement.Variables) {
if (v.Name == name)
return null; // no need to introduce the variable here
}
}
AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops); AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops);
if (withinCurrent != null) { if (withinCurrent != null) {

13
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -519,13 +519,13 @@ namespace ICSharpCode.Decompiler.ILAst
public class ILFixedStatement : ILNode public class ILFixedStatement : ILNode
{ {
public ILExpression Initializer; public List<ILExpression> Initializers = new List<ILExpression>();
public ILBlock BodyBlock; public ILBlock BodyBlock;
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
if (this.Initializer != null) foreach (ILExpression initializer in this.Initializers)
yield return this.Initializer; yield return initializer;
if (this.BodyBlock != null) if (this.BodyBlock != null)
yield return this.BodyBlock; yield return this.BodyBlock;
} }
@ -533,8 +533,11 @@ namespace ICSharpCode.Decompiler.ILAst
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write("fixed ("); output.Write("fixed (");
if (this.Initializer != null) for (int i = 0; i < this.Initializers.Count; i++) {
this.Initializer.WriteTo(output); if (i > 0)
output.Write(", ");
this.Initializers[i].WriteTo(output);
}
output.WriteLine(") {"); output.WriteLine(") {");
output.Indent(); output.Indent();
this.BodyBlock.WriteTo(output); this.BodyBlock.WriteTo(output);

85
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -2,8 +2,10 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.Utils; using ICSharpCode.NRefactory.Utils;
using Mono.Cecil; using Mono.Cecil;
@ -297,28 +299,42 @@ namespace ICSharpCode.Decompiler.ILAst
#region IntroduceFixedStatements #region IntroduceFixedStatements
void IntroduceFixedStatements(ILBlock block, ref int i) void IntroduceFixedStatements(ILBlock block, ref int i)
{ {
// stloc(pinned_Var, conv.u(ldc.i4(0)))
ILExpression initValue; ILExpression initValue;
ILVariable pinnedVar; ILVariable pinnedVar;
if (!MatchFixedInitializer(block, i, out pinnedVar, out initValue)) int initEndPos;
if (!MatchFixedInitializer(block, i, out pinnedVar, out initValue, out initEndPos))
return; return;
// find initialization of v:
int j; ILFixedStatement fixedStmt = block.Body.ElementAtOrDefault(initEndPos) as ILFixedStatement;
for (j = i + 1; j < block.Body.Count; j++) { if (fixedStmt != null) {
ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression;
if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) {
// we found a second initializer for the existing fixed statement
fixedStmt.Initializers.Insert(0, initValue);
block.Body.RemoveRange(i, initEndPos - i);
fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1);
if (pinnedVar.Type.IsByReference)
pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType);
return;
}
}
// find where pinnedVar is reset to 0:
for (int j = initEndPos; j < block.Body.Count; j++) {
ILVariable v2; ILVariable v2;
ILExpression storedVal; ILExpression storedVal;
// stloc(pinned_Var, conv.u(ldc.i4(0)))
if (block.Body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { if (block.Body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) {
if (IsNullOrZero(storedVal)) { if (IsNullOrZero(storedVal)) {
// Create fixed statement from i to j // Create fixed statement from i to j
ILFixedStatement stmt = new ILFixedStatement(); fixedStmt = new ILFixedStatement();
stmt.Initializer = initValue; fixedStmt.Initializers.Add(initValue);
stmt.BodyBlock = new ILBlock(block.Body.GetRange(i + 1, j - i - 1)); // from i+1 to j-1 (inclusive) fixedStmt.BodyBlock = new ILBlock(block.Body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive)
block.Body.RemoveRange(i + 1, j - i); // from j+1 to i (inclusive) block.Body.RemoveRange(i + 1, j - i); // from i+1 to j (inclusive)
block.Body[i] = stmt; block.Body[i] = fixedStmt;
if (pinnedVar.Type.IsByReference) if (pinnedVar.Type.IsByReference)
pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType);
HandleStringFixing(stmt);
break; break;
} }
} }
@ -332,11 +348,13 @@ namespace ICSharpCode.Decompiler.ILAst
return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull; return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull;
} }
bool MatchFixedInitializer(ILBlock block, int i, out ILVariable pinnedVar, out ILExpression initValue) bool MatchFixedInitializer(ILBlock block, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos)
{ {
if (block.Body[i].Match(ILCode.Stloc, out pinnedVar, out initValue)) { if (block.Body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned) {
initValue = (ILExpression)block.Body[i]; initValue = (ILExpression)block.Body[i];
return pinnedVar.IsPinned; nextPos = i + 1;
HandleStringFixing(pinnedVar, block.Body, ref nextPos, ref initValue);
return true;
} }
ILCondition ifStmt = block.Body[i] as ILCondition; ILCondition ifStmt = block.Body[i] as ILCondition;
ILExpression arrayLoadingExpr; ILExpression arrayLoadingExpr;
@ -358,12 +376,14 @@ namespace ICSharpCode.Decompiler.ILAst
&& IsNullOrZero(falseValue.Arguments[1])) && IsNullOrZero(falseValue.Arguments[1]))
{ {
initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr); initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr);
nextPos = i + 1;
return true; return true;
} }
} }
} }
} }
initValue = null; initValue = null;
nextPos = -1;
return false; return false;
} }
@ -395,7 +415,7 @@ namespace ICSharpCode.Decompiler.ILAst
return expr; return expr;
} }
void HandleStringFixing(ILFixedStatement fixedStatement) bool HandleStringFixing(ILVariable pinnedVar, List<ILNode> body, ref int pos, ref ILExpression fixedStmtInitializer)
{ {
// fixed (stloc(pinnedVar, ldloc(text))) { // fixed (stloc(pinnedVar, ldloc(text))) {
// var1 = var2 = conv.i(ldloc(pinnedVar)) // var1 = var2 = conv.i(ldloc(pinnedVar))
@ -405,43 +425,42 @@ namespace ICSharpCode.Decompiler.ILAst
// stloc(ptrVar, var2) // stloc(ptrVar, var2)
// ... // ...
ILVariable pinnedVar = (ILVariable)fixedStatement.Initializer.Operand; if (pos >= body.Count)
Debug.Assert(pinnedVar.IsPinned); return false;
var body = fixedStatement.BodyBlock.Body;
if (body.Count < 3)
return;
ILVariable var1, var2; ILVariable var1, var2;
ILExpression varAssignment, ptrInitialization; ILExpression varAssignment, ptrInitialization;
if (!(body[0].Match(ILCode.Stloc, out var1, out varAssignment) && varAssignment.Match(ILCode.Stloc, out var2, out ptrInitialization))) if (!(body[pos].Match(ILCode.Stloc, out var1, out varAssignment) && varAssignment.Match(ILCode.Stloc, out var2, out ptrInitialization)))
return; return false;
if (!(var1.IsGenerated && var2.IsGenerated)) if (!(var1.IsGenerated && var2.IsGenerated))
return; return false;
if (ptrInitialization.Code == ILCode.Conv_I || ptrInitialization.Code == ILCode.Conv_U) if (ptrInitialization.Code == ILCode.Conv_I || ptrInitialization.Code == ILCode.Conv_U)
ptrInitialization = ptrInitialization.Arguments[0]; ptrInitialization = ptrInitialization.Arguments[0];
if (!ptrInitialization.MatchLdloc(pinnedVar)) if (!ptrInitialization.MatchLdloc(pinnedVar))
return; return false;
ILCondition ifStmt = body[1] as ILCondition; ILCondition ifStmt = body[pos + 1] as ILCondition;
if (!(ifStmt != null && ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 && (ifStmt.FalseBlock == null || ifStmt.FalseBlock.Body.Count == 0))) if (!(ifStmt != null && ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 && (ifStmt.FalseBlock == null || ifStmt.FalseBlock.Body.Count == 0)))
return; return false;
if (!UnpackDoubleNegation(ifStmt.Condition).MatchLdloc(var1)) if (!UnpackDoubleNegation(ifStmt.Condition).MatchLdloc(var1))
return; return false;
ILVariable assignedVar; ILVariable assignedVar;
ILExpression assignedExpr; ILExpression assignedExpr;
if (!(ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out assignedVar, out assignedExpr) && assignedVar == var2 && assignedExpr.Code == ILCode.Add)) if (!(ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out assignedVar, out assignedExpr) && assignedVar == var2 && assignedExpr.Code == ILCode.Add))
return; return false;
MethodReference calledMethod; MethodReference calledMethod;
if (!(assignedExpr.Arguments[0].MatchLdloc(var1) && assignedExpr.Arguments[1].Match(ILCode.Call, out calledMethod))) if (!(assignedExpr.Arguments[0].MatchLdloc(var1) && assignedExpr.Arguments[1].Match(ILCode.Call, out calledMethod)))
return; return false;
if (!(calledMethod.Name == "get_OffsetToStringData" && calledMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers")) if (!(calledMethod.Name == "get_OffsetToStringData" && calledMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers"))
return; return false;
ILVariable pointerVar; ILVariable pointerVar;
if (body[2].Match(ILCode.Stloc, out pointerVar, out assignedExpr) && assignedExpr.MatchLdloc(var2)) { if (body[pos + 2].Match(ILCode.Stloc, out pointerVar, out assignedExpr) && assignedExpr.MatchLdloc(var2)) {
body.RemoveRange(0, 3); pos += 3;
fixedStatement.Initializer.Operand = pointerVar; fixedStmtInitializer.Operand = pointerVar;
return true;
} }
return false;
} }
#endregion #endregion
} }

9
ICSharpCode.Decompiler/Tests/UnsafeCode.cs

@ -56,4 +56,13 @@ public class UnsafeCode
{ {
return d->ToString(); return d->ToString();
} }
public unsafe void FixMultipleStrings(string text)
{
fixed (char* c = text, d = Environment.UserName, e = text) {
*c = 'c';
*d = 'd';
*e = 'e';
}
}
} }

Loading…
Cancel
Save