Browse Source

Update ICSharpCode.Decompiler to ILSpy 2.1

4.2
Daniel Grunwald 13 years ago
parent
commit
8a45f2e79e
  1. 16
      src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 6
      src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 1
      src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
  4. 8
      src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  5. 1
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  6. 15
      src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs
  7. 3
      src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  8. 653
      src/Libraries/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs
  9. 15
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  10. 6
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs
  11. 12
      src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  12. 310
      src/Libraries/ICSharpCode.Decompiler/ILAst/StateRange.cs
  13. 148
      src/Libraries/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs
  14. 21
      src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  15. 418
      src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  16. 4
      src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
  17. 144
      src/Libraries/ICSharpCode.Decompiler/Tests/Async.cs
  18. 4
      src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  19. 2
      src/Libraries/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs
  20. 6
      src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs

16
src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -81,6 +81,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -81,6 +81,8 @@ namespace ICSharpCode.Decompiler.Ast
return true;
if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type))
return true;
if (settings.AsyncAwait && AsyncDecompiler.IsCompilerGeneratedStateMachine(type))
return true;
} else if (type.IsCompilerGenerated()) {
if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
return true;
@ -770,6 +772,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -770,6 +772,10 @@ namespace ICSharpCode.Decompiler.Ast
SetNewModifier(astMethod);
}
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
if (context.CurrentMethodIsAsync) {
astMethod.Modifiers |= Modifiers.Async;
context.CurrentMethodIsAsync = false;
}
}
ConvertAttributes(astMethod, methodDef);
if (methodDef.HasCustomAttributes && astMethod.Parameters.Count > 0) {
@ -1342,6 +1348,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -1342,6 +1348,7 @@ namespace ICSharpCode.Decompiler.Ast
static void ConvertCustomAttributes(AstNode attributedNode, ICustomAttributeProvider customAttributeProvider, string attributeTarget = null)
{
EntityDeclaration entityDecl = attributedNode as EntityDeclaration;
if (customAttributeProvider.HasCustomAttributes) {
var attributes = new List<ICSharpCode.NRefactory.CSharp.Attribute>();
foreach (var customAttribute in customAttributeProvider.CustomAttributes.OrderBy(a => a.AttributeType.FullName)) {
@ -1353,6 +1360,15 @@ namespace ICSharpCode.Decompiler.Ast @@ -1353,6 +1360,15 @@ namespace ICSharpCode.Decompiler.Ast
// don't show the ParamArrayAttribute (it's converted to the 'params' modifier)
continue;
}
// if the method is async, remove [DebuggerStepThrough] and [Async
if (entityDecl != null && entityDecl.HasModifier(Modifiers.Async)) {
if (customAttribute.AttributeType.Name == "DebuggerStepThroughAttribute" && customAttribute.AttributeType.Namespace == "System.Diagnostics") {
continue;
}
if (customAttribute.AttributeType.Name == "AsyncStateMachineAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") {
continue;
}
}
var attribute = new ICSharpCode.NRefactory.CSharp.Attribute();
attribute.AddAnnotation(customAttribute);

6
src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.Ast
MethodDefinition oldCurrentMethod = context.CurrentMethod;
Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
context.CurrentMethod = methodDef;
context.CurrentMethodIsAsync = false;
try {
AstMethodBodyBuilder builder = new AstMethodBodyBuilder();
builder.methodDef = methodDef;
@ -847,8 +848,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -847,8 +848,11 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.ExpressionTreeParameterDeclarations:
args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode));
return args[args.Count - 1];
case ILCode.Await:
return new UnaryOperatorExpression(UnaryOperatorType.Await, arg1);
case ILCode.NullableOf:
case ILCode.ValueOf: return arg1;
case ILCode.ValueOf:
return arg1;
default:
throw new Exception("Unknown OpCode: " + byteCode.Code);
}

1
src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs

@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler @@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler
public TypeDefinition CurrentType;
public MethodDefinition CurrentMethod;
public DecompilerSettings Settings = new DecompilerSettings();
public bool CurrentMethodIsAsync;
// public ITypeResolveContext TypeResolveContext;
// public IProjectContent ProjectContent;

8
src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs

@ -114,6 +114,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -114,6 +114,9 @@ namespace ICSharpCode.Decompiler.Ast
object GetCurrentLocalDefinition()
{
AstNode node = nodeStack.Peek();
if (node is Identifier && node.Parent != null)
node = node.Parent;
var parameterDef = node.Annotation<ParameterDefinition>();
if (parameterDef != null)
return parameterDef;
@ -126,14 +129,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -126,14 +129,11 @@ namespace ICSharpCode.Decompiler.Ast
//if (variable.OriginalVariable != null)
// return variable.OriginalVariable;
return variable;
} else {
}
}
var label = node as LabelStatement;
if (label != null)
{
if (label != null) {
var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null);
if (method != null)
return method.ToString() + label.Label;

1
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -151,6 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -151,6 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
DecompilerContext subContext = context.Clone();
subContext.CurrentMethod = method;
subContext.CurrentMethodIsAsync = false;
subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext, ame.Parameters);
TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext);

15
src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs

@ -72,6 +72,21 @@ namespace ICSharpCode.Decompiler @@ -72,6 +72,21 @@ namespace ICSharpCode.Decompiler
}
}
bool asyncAwait = true;
/// <summary>
/// Decompile async methods.
/// </summary>
public bool AsyncAwait {
get { return asyncAwait; }
set {
if (asyncAwait != value) {
asyncAwait = value;
OnPropertyChanged("AsyncAwait");
}
}
}
bool automaticProperties = true;
/// <summary>

3
src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -101,6 +101,7 @@ @@ -101,6 +101,7 @@
<Compile Include="FlowAnalysis\SsaOptimization.cs" />
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="ILAst\AsyncDecompiler.cs" />
<Compile Include="ILAst\LiftedOperators.cs" />
<Compile Include="ILAst\InitializerPeepholeTransforms.cs" />
<Compile Include="ILAst\DefaultDictionary.cs" />
@ -114,6 +115,8 @@ @@ -114,6 +115,8 @@
<Compile Include="ILAst\PatternMatching.cs" />
<Compile Include="ILAst\PeepholeTransform.cs" />
<Compile Include="ILAst\SimpleControlFlow.cs" />
<Compile Include="ILAst\StateRange.cs" />
<Compile Include="ILAst\SymbolicExecution.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ILAst\YieldReturnDecompiler.cs" />
<Compile Include="ITextOutput.cs" />

653
src/Libraries/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs

@ -0,0 +1,653 @@ @@ -0,0 +1,653 @@
// Copyright (c) 2012 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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// Decompiler step for C# 5 async/await.
/// </summary>
class AsyncDecompiler
{
public static bool IsCompilerGeneratedStateMachine(TypeDefinition type)
{
if (!(type.DeclaringType != null && type.IsCompilerGenerated()))
return false;
foreach (TypeReference i in type.Interfaces) {
if (i.Namespace == "System.Runtime.CompilerServices" && i.Name == "IAsyncStateMachine")
return true;
}
return false;
}
enum AsyncMethodType
{
Void,
Task,
TaskOfT
}
DecompilerContext context;
// These fields are set by MatchTaskCreationPattern()
AsyncMethodType methodType;
int initialState;
TypeDefinition stateMachineStruct;
MethodDefinition moveNextMethod;
FieldDefinition builderField;
FieldDefinition stateField;
Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>();
// These fields are set by AnalyzeMoveNext()
int finalState = -2;
ILTryCatchBlock mainTryCatch;
ILLabel setResultAndExitLabel;
ILLabel exitLabel;
ILExpression resultExpr;
#region RunStep1() method
public static void RunStep1(DecompilerContext context, ILBlock method)
{
if (!context.Settings.AsyncAwait)
return; // abort if async decompilation is disabled
var yrd = new AsyncDecompiler();
yrd.context = context;
if (!yrd.MatchTaskCreationPattern(method))
return;
#if DEBUG
if (Debugger.IsAttached) {
yrd.Run();
} else {
#endif
try {
yrd.Run();
} catch (SymbolicAnalysisFailedException) {
return;
}
#if DEBUG
}
#endif
context.CurrentMethodIsAsync = true;
method.Body.Clear();
method.EntryGoto = null;
method.Body.AddRange(yrd.newTopLevelBody);
ILAstOptimizer.RemoveRedundantCode(method);
}
void Run()
{
AnalyzeMoveNext();
ValidateCatchBlock(mainTryCatch.CatchBlocks[0]);
AnalyzeStateMachine(mainTryCatch.TryBlock);
// AnalyzeStateMachine invokes ConvertBody
MarkGeneratedVariables();
YieldReturnDecompiler.TranslateFieldsToLocalAccess(newTopLevelBody, fieldToParameterMap);
}
#endregion
#region MatchTaskCreationPattern
bool MatchTaskCreationPattern(ILBlock method)
{
if (method.Body.Count < 5)
return false;
// Check the second-to-last instruction (the start call) first, as we can get the most information from that
MethodReference startMethod;
ILExpression loadStartTarget, loadStartArgument;
// call(AsyncTaskMethodBuilder::Start, ldloca(builder), ldloca(stateMachine))
if (!method.Body[method.Body.Count - 2].Match(ILCode.Call, out startMethod, out loadStartTarget, out loadStartArgument))
return false;
if (startMethod.Name != "Start" || startMethod.DeclaringType == null || startMethod.DeclaringType.Namespace != "System.Runtime.CompilerServices")
return false;
switch (startMethod.DeclaringType.Name) {
case "AsyncTaskMethodBuilder`1":
methodType = AsyncMethodType.TaskOfT;
break;
case "AsyncTaskMethodBuilder":
methodType = AsyncMethodType.Task;
break;
case "AsyncVoidMethodBuilder":
methodType = AsyncMethodType.Void;
break;
default:
return false;
}
ILVariable stateMachineVar, builderVar;
if (!loadStartTarget.Match(ILCode.Ldloca, out builderVar))
return false;
if (!loadStartArgument.Match(ILCode.Ldloca, out stateMachineVar))
return false;
stateMachineStruct = stateMachineVar.Type.ResolveWithinSameModule();
if (stateMachineStruct == null || !stateMachineStruct.IsValueType)
return false;
moveNextMethod = stateMachineStruct.Methods.FirstOrDefault(f => f.Name == "MoveNext");
if (moveNextMethod == null)
return false;
// Check third-to-last instruction (copy of builder):
// stloc(builder, ldfld(StateMachine::<>t__builder, ldloca(stateMachine)))
ILExpression loadBuilderExpr;
if (!method.Body[method.Body.Count - 3].MatchStloc(builderVar, out loadBuilderExpr))
return false;
FieldReference builderFieldRef;
ILExpression loadStateMachineForBuilderExpr;
if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr))
return false;
if (!loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar))
return false;
builderField = builderFieldRef.ResolveWithinSameModule();
if (builderField == null)
return false;
// Check the last instruction (ret)
if (methodType == AsyncMethodType.Void) {
if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret))
return false;
} else {
// ret(call(AsyncTaskMethodBuilder::get_Task, ldflda(StateMachine::<>t__builder, ldloca(stateMachine))))
ILExpression returnValue;
if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret, out returnValue))
return false;
MethodReference getTaskMethod;
ILExpression builderExpr;
if (!returnValue.Match(ILCode.Call, out getTaskMethod, out builderExpr))
return false;
ILExpression loadStateMachineForBuilderExpr2;
FieldReference builderField2;
if (!builderExpr.Match(ILCode.Ldflda, out builderField2, out loadStateMachineForBuilderExpr2))
return false;
if (builderField2.ResolveWithinSameModule() != builderField || !loadStateMachineForBuilderExpr2.MatchLdloca(stateMachineVar))
return false;
}
// Check the last field assignment - this should be the state field
ILExpression initialStateExpr;
if (!MatchStFld(method.Body[method.Body.Count - 4], stateMachineVar, out stateField, out initialStateExpr))
return false;
if (!initialStateExpr.Match(ILCode.Ldc_I4, out initialState))
return false;
if (initialState != -1)
return false;
// Check the second-to-last field assignment - this should be the builder field
FieldDefinition builderField3;
ILExpression builderInitialization;
if (!MatchStFld(method.Body[method.Body.Count - 5], stateMachineVar, out builderField3, out builderInitialization))
return false;
MethodReference createMethodRef;
if (builderField3 != builderField || !builderInitialization.Match(ILCode.Call, out createMethodRef))
return false;
if (createMethodRef.Name != "Create")
return false;
for (int i = 0; i < method.Body.Count - 5; i++) {
FieldDefinition field;
ILExpression fieldInit;
if (!MatchStFld(method.Body[i], stateMachineVar, out field, out fieldInit))
return false;
ILVariable v;
if (!fieldInit.Match(ILCode.Ldloc, out v))
return false;
if (!v.IsParameter)
return false;
fieldToParameterMap[field] = v;
}
return true;
}
static bool MatchStFld(ILNode stfld, ILVariable stateMachineVar, out FieldDefinition field, out ILExpression expr)
{
field = null;
FieldReference fieldRef;
ILExpression ldloca;
if (!stfld.Match(ILCode.Stfld, out fieldRef, out ldloca, out expr))
return false;
field = fieldRef.ResolveWithinSameModule();
return field != null && ldloca.MatchLdloca(stateMachineVar);
}
#endregion
#region Analyze MoveNext
void AnalyzeMoveNext()
{
ILBlock ilMethod = CreateILAst(moveNextMethod);
if (ilMethod.Body.Count != 6)
throw new SymbolicAnalysisFailedException();
mainTryCatch = ilMethod.Body[0] as ILTryCatchBlock;
if (mainTryCatch == null || mainTryCatch.CatchBlocks.Count != 1)
throw new SymbolicAnalysisFailedException();
if (mainTryCatch.FaultBlock != null || mainTryCatch.FinallyBlock != null)
throw new SymbolicAnalysisFailedException();
setResultAndExitLabel = ilMethod.Body[1] as ILLabel;
if (setResultAndExitLabel == null)
throw new SymbolicAnalysisFailedException();
if (!MatchStateAssignment(ilMethod.Body[2], out finalState))
throw new SymbolicAnalysisFailedException();
// call(AsyncTaskMethodBuilder`1::SetResult, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloc(<>t__result))
MethodReference setResultMethod;
ILExpression builderExpr;
if (methodType == AsyncMethodType.TaskOfT) {
if (!ilMethod.Body[3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr))
throw new SymbolicAnalysisFailedException();
} else {
if (!ilMethod.Body[3].Match(ILCode.Call, out setResultMethod, out builderExpr))
throw new SymbolicAnalysisFailedException();
}
if (!(setResultMethod.Name == "SetResult" && IsBuilderFieldOnThis(builderExpr)))
throw new SymbolicAnalysisFailedException();
exitLabel = ilMethod.Body[4] as ILLabel;
if (exitLabel == null)
throw new SymbolicAnalysisFailedException();
}
/// <summary>
/// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step.
/// </summary>
ILBlock CreateILAst(MethodDefinition method)
{
if (method == null || !method.HasBody)
throw new SymbolicAnalysisFailedException();
ILBlock ilMethod = new ILBlock();
ILAstBuilder astBuilder = new ILAstBuilder();
ilMethod.Body = astBuilder.Build(method, true, context);
ILAstOptimizer optimizer = new ILAstOptimizer();
optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn);
return ilMethod;
}
void ValidateCatchBlock(ILTryCatchBlock.CatchBlock catchBlock)
{
if (catchBlock.ExceptionType == null || catchBlock.ExceptionType.Name != "Exception")
throw new SymbolicAnalysisFailedException();
if (catchBlock.Body.Count != 3)
throw new SymbolicAnalysisFailedException();
int stateID;
if (!(MatchStateAssignment(catchBlock.Body[0], out stateID) && stateID == finalState))
throw new SymbolicAnalysisFailedException();
MethodReference setExceptionMethod;
ILExpression builderExpr, exceptionExpr;
if (!catchBlock.Body[1].Match(ILCode.Call, out setExceptionMethod, out builderExpr, out exceptionExpr))
throw new SymbolicAnalysisFailedException();
if (!(setExceptionMethod.Name == "SetException" && IsBuilderFieldOnThis(builderExpr) && exceptionExpr.MatchLdloc(catchBlock.ExceptionVariable)))
throw new SymbolicAnalysisFailedException();
ILLabel label;
if (!(catchBlock.Body[2].Match(ILCode.Leave, out label) && label == exitLabel))
throw new SymbolicAnalysisFailedException();
}
bool IsBuilderFieldOnThis(ILExpression builderExpr)
{
// ldflda(StateMachine::<>t__builder, ldloc(this))
FieldReference fieldRef;
ILExpression target;
return builderExpr.Match(ILCode.Ldflda, out fieldRef, out target)
&& fieldRef.ResolveWithinSameModule() == builderField
&& target.MatchThis();
}
bool MatchStateAssignment(ILNode stfld, out int stateID)
{
// stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(-2))
stateID = 0;
FieldReference fieldRef;
ILExpression target, val;
if (stfld.Match(ILCode.Stfld, out fieldRef, out target, out val)) {
return fieldRef.ResolveWithinSameModule() == stateField
&& target.MatchThis()
&& val.Match(ILCode.Ldc_I4, out stateID);
}
return false;
}
#endregion
#region AnalyzeStateMachine
ILVariable doFinallyBodies;
List<ILNode> newTopLevelBody;
void AnalyzeStateMachine(ILBlock block)
{
var body = block.Body;
if (body.Count == 0)
throw new SymbolicAnalysisFailedException();
if (DetectDoFinallyBodies(body)) {
body.RemoveAt(0);
if (body.Count == 0)
throw new SymbolicAnalysisFailedException();
}
StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField);
int bodyLength = block.Body.Count;
int pos = rangeAnalysis.AssignStateRanges(body, bodyLength);
rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);
var labelStateRangeMapping = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);
newTopLevelBody = ConvertBody(body, pos, bodyLength, labelStateRangeMapping);
newTopLevelBody.Insert(0, MakeGoTo(labelStateRangeMapping, initialState));
newTopLevelBody.Add(setResultAndExitLabel);
if (methodType == AsyncMethodType.TaskOfT) {
newTopLevelBody.Add(new ILExpression(ILCode.Ret, null, resultExpr));
} else {
newTopLevelBody.Add(new ILExpression(ILCode.Ret, null));
}
}
bool DetectDoFinallyBodies(List<ILNode> body)
{
ILVariable v;
ILExpression initExpr;
if (!body[0].Match(ILCode.Stloc, out v, out initExpr))
return false;
int initialValue;
if (!(initExpr.Match(ILCode.Ldc_I4, out initialValue) && initialValue == 1))
return false;
doFinallyBodies = v;
return true;
}
#endregion
#region ConvertBody
ILExpression MakeGoTo(LabelRangeMapping mapping, int state)
{
foreach (var pair in mapping) {
if (pair.Value.Contains(state))
return new ILExpression(ILCode.Br, pair.Key);
}
throw new SymbolicAnalysisFailedException();
}
List<ILNode> ConvertBody(List<ILNode> body, int startPos, int bodyLength, LabelRangeMapping mapping)
{
List<ILNode> newBody = new List<ILNode>();
// Copy all instructions from the old body to newBody.
for (int pos = startPos; pos < bodyLength; pos++) {
ILTryCatchBlock tryCatchBlock = body[pos] as ILTryCatchBlock;
ILExpression expr = body[pos] as ILExpression;
if (expr != null && expr.Code == ILCode.Leave && expr.Operand == exitLabel) {
ILVariable awaiterVar;
FieldDefinition awaiterField;
int targetStateID;
HandleAwait(newBody, out awaiterVar, out awaiterField, out targetStateID);
MarkAsGeneratedVariable(awaiterVar);
newBody.Add(new ILExpression(ILCode.Await, null, new ILExpression(ILCode.Ldloca, awaiterVar)));
newBody.Add(MakeGoTo(mapping, targetStateID));
} else if (tryCatchBlock != null) {
ILTryCatchBlock newTryCatchBlock = new ILTryCatchBlock();
var tryBody = tryCatchBlock.TryBlock.Body;
if (tryBody.Count == 0)
throw new SymbolicAnalysisFailedException();
StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField);
int tryBodyLength = tryBody.Count;
int posInTryBody = rangeAnalysis.AssignStateRanges(tryBody, tryBodyLength);
rangeAnalysis.EnsureLabelAtPos(tryBody, ref posInTryBody, ref tryBodyLength);
var mappingInTryBlock = rangeAnalysis.CreateLabelRangeMapping(tryBody, posInTryBody, tryBodyLength);
var newTryBody = ConvertBody(tryBody, posInTryBody, tryBodyLength, mappingInTryBlock);
newTryBody.Insert(0, MakeGoTo(mappingInTryBlock, initialState));
// If there's a label at the beginning of the state dispatcher, copy that
if (posInTryBody > 0 && tryBody.FirstOrDefault() is ILLabel)
newTryBody.Insert(0, tryBody.First());
newTryCatchBlock.TryBlock = new ILBlock(newTryBody);
newTryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(tryCatchBlock.CatchBlocks);
newTryCatchBlock.FaultBlock = tryCatchBlock.FaultBlock;
if (tryCatchBlock.FinallyBlock != null)
newTryCatchBlock.FinallyBlock = new ILBlock(ConvertFinally(tryCatchBlock.FinallyBlock.Body));
newBody.Add(newTryCatchBlock);
} else {
newBody.Add(body[pos]);
}
}
return newBody;
}
List<ILNode> ConvertFinally(List<ILNode> body)
{
List<ILNode> newBody = new List<ILNode>(body);
ILLabel endFinallyLabel;
ILExpression ceqExpr;
if (newBody.Count > 0 && newBody[0].Match(ILCode.Brtrue, out endFinallyLabel, out ceqExpr)) {
ILExpression loadDoFinallyBodies, loadZero;
object unused;
if (ceqExpr.Match(ILCode.Ceq, out unused, out loadDoFinallyBodies, out loadZero)) {
int num;
if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies) && loadZero.Match(ILCode.Ldc_I4, out num) && num == 0) {
newBody.RemoveAt(0);
}
} else if (ceqExpr.Match(ILCode.LogicNot, out loadDoFinallyBodies)) {
if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies)) {
newBody.RemoveAt(0);
}
}
}
return newBody;
}
void HandleAwait(List<ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID)
{
// Handle the instructions prior to the exit out of the method to detect what is being awaited.
// (analyses the last instructions in newBody and removes the analyzed instructions from newBody)
if (doFinallyBodies != null) {
// stloc(<>t__doFinallyBodies, ldc.i4(0))
ILExpression dfbInitExpr;
if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr))
throw new SymbolicAnalysisFailedException();
int val;
if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0))
throw new SymbolicAnalysisFailedException();
newBody.RemoveAt(newBody.Count - 1); // remove doFinallyBodies assignment
}
// call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this))
ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression;
newBody.RemoveAt(newBody.Count - 1); // remove AwaitUnsafeOnCompleted call
if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call)
throw new SymbolicAnalysisFailedException();
if (((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name != "AwaitUnsafeOnCompleted")
throw new SymbolicAnalysisFailedException();
if (callAwaitUnsafeOnCompleted.Arguments.Count != 3)
throw new SymbolicAnalysisFailedException();
if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar))
throw new SymbolicAnalysisFailedException();
// stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001))
FieldReference awaiterFieldRef;
ILExpression loadThis, loadAwaiterVar;
if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar))
throw new SymbolicAnalysisFailedException();
newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment
awaiterField = awaiterFieldRef.ResolveWithinSameModule();
if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar)))
throw new SymbolicAnalysisFailedException();
// stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0))
if (!MatchStateAssignment(newBody.LastOrDefault(), out targetStateID))
throw new SymbolicAnalysisFailedException();
newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment
}
#endregion
#region MarkGeneratedVariables
int smallestGeneratedVariableIndex = int.MaxValue;
void MarkAsGeneratedVariable(ILVariable v)
{
if (v.OriginalVariable != null && v.OriginalVariable.Index >= 0) {
smallestGeneratedVariableIndex = Math.Min(smallestGeneratedVariableIndex, v.OriginalVariable.Index);
}
}
void MarkGeneratedVariables()
{
var expressions = new ILBlock(newTopLevelBody).GetSelfAndChildrenRecursive<ILExpression>();
foreach (var v in expressions.Select(e => e.Operand).OfType<ILVariable>()) {
if (v.OriginalVariable != null && v.OriginalVariable.Index >= smallestGeneratedVariableIndex)
v.IsGenerated = true;
}
}
#endregion
#region RunStep2() method
public static void RunStep2(DecompilerContext context, ILBlock method)
{
if (context.CurrentMethodIsAsync) {
Step2(method.Body);
ILAstOptimizer.RemoveRedundantCode(method);
// Repeat the inlining/copy propagation optimization because the conversion of field access
// to local variables can open up additional inlining possibilities.
ILInlining inlining = new ILInlining(method);
inlining.InlineAllVariables();
inlining.CopyPropagation();
}
}
static void Step2(List<ILNode> body)
{
for (int pos = 0; pos < body.Count; pos++) {
ILTryCatchBlock tc = body[pos] as ILTryCatchBlock;
if (tc != null) {
Step2(tc.TryBlock.Body);
} else {
Step2(body, ref pos);
}
}
}
static bool Step2(List<ILNode> body, ref int pos)
{
// stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr)
// brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001)))
// await(ldloca(CS$0$0001))
// ...
// IL_7C:
// arg_8B_0 = call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult, ldloca(CS$0$0001))
// initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0001))
ILExpression loadAwaiter;
ILVariable awaiterVar;
if (!body[pos].Match(ILCode.Await, out loadAwaiter))
return false;
if (!loadAwaiter.Match(ILCode.Ldloca, out awaiterVar))
return false;
ILVariable stackVar;
ILExpression stackExpr;
while (pos >= 1 && body[pos - 1].Match(ILCode.Stloc, out stackVar, out stackExpr))
pos--;
// stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr)
ILExpression getAwaiterCall;
if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall)))
return false;
MethodReference getAwaiterMethod;
ILExpression awaitedExpr;
if (!(getAwaiterCall.Match(ILCode.Call, out getAwaiterMethod, out awaitedExpr) || getAwaiterCall.Match(ILCode.Callvirt, out getAwaiterMethod, out awaitedExpr)))
return false;
if (awaitedExpr.Code == ILCode.AddressOf) {
// remove 'AddressOf()' when calling GetAwaiter() on a value type
awaitedExpr = awaitedExpr.Arguments[0];
}
// brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001)))
ILLabel label;
ILExpression getIsCompletedCall;
if (!(pos >= 1 && body[pos - 1].Match(ILCode.Brtrue, out label, out getIsCompletedCall)))
return false;
int labelPos = body.IndexOf(label);
if (labelPos < pos)
return false;
for (int i = pos + 1; i < labelPos; i++) {
// validate that we aren't deleting any unexpected instructions -
// between the await and the label, there should only be the stack, awaiter and state logic
ILExpression expr = body[i] as ILExpression;
if (expr == null)
return false;
switch (expr.Code) {
case ILCode.Stloc:
case ILCode.Initobj:
case ILCode.Stfld:
case ILCode.Await:
// e.g.
// stloc(CS$0$0001, ldfld(StateMachine::<>u__$awaitere, ldloc(this)))
// initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0002_66))
// stfld('<AwaitInLoopCondition>d__d'::<>u__$awaitere, ldloc(this), ldloc(CS$0$0002_66))
// stfld('<AwaitInLoopCondition>d__d'::<>1__state, ldloc(this), ldc.i4(-1))
break;
default:
return false;
}
}
if (labelPos + 1 >= body.Count)
return false;
ILExpression resultAssignment = body[labelPos + 1] as ILExpression;
ILVariable resultVar;
ILExpression getResultCall;
bool isResultAssignment = resultAssignment.Match(ILCode.Stloc, out resultVar, out getResultCall);
if (!isResultAssignment)
getResultCall = resultAssignment;
if (!(getResultCall.Operand is MethodReference && ((MethodReference)getResultCall.Operand).Name == "GetResult"))
return false;
pos -= 2; // also delete 'stloc', 'brtrue' and 'await'
body.RemoveRange(pos, labelPos - pos);
Debug.Assert(body[pos] == label);
pos++;
if (isResultAssignment) {
Debug.Assert(body[pos] == resultAssignment);
resultAssignment.Arguments[0] = new ILExpression(ILCode.Await, null, awaitedExpr);
} else {
body[pos] = new ILExpression(ILCode.Await, null, awaitedExpr);
}
// if the awaiter variable is cleared out in the next instruction, remove that instruction
if (IsVariableReset(body.ElementAtOrDefault(pos + 1), awaiterVar)) {
body.RemoveAt(pos + 1);
}
return true;
}
static bool IsVariableReset(ILNode expr, ILVariable variable)
{
object unused;
ILExpression ldloca;
return expr.Match(ILCode.Initobj, out unused, out ldloca) && ldloca.MatchLdloca(variable);
}
#endregion
}
}

15
src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.ILAst
InlineVariables,
CopyPropagation,
YieldReturn,
AsyncAwait,
PropertyAccessInstructions,
SplitToMovableBlocks,
TypeInference,
@ -107,6 +108,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -107,6 +108,10 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
YieldReturnDecompiler.Run(context, method);
AsyncDecompiler.RunStep1(context, method);
if (abortBeforeStep == ILAstOptimizationStep.AsyncAwait) return;
AsyncDecompiler.RunStep2(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return;
IntroducePropertyAccessInstructions(method);
@ -256,7 +261,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -256,7 +261,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Ignore arguments of 'leave'
/// </summary>
/// <param name="method"></param>
void RemoveRedundantCode(ILBlock method)
internal static void RemoveRedundantCode(ILBlock method)
{
Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>();
foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
@ -286,7 +291,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -286,7 +291,13 @@ namespace ICSharpCode.Decompiler.ILAst
prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
// Ignore pop
} else {
newBody.Add(body[i]);
ILLabel label = body[i] as ILLabel;
if (label != null) {
if (labelRefCount.GetOrDefault(label) > 0)
newBody.Add(label);
} else {
newBody.Add(body[i]);
}
}
}
block.Body = newBody;

6
src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -333,7 +333,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -333,7 +333,11 @@ namespace ICSharpCode.Decompiler.ILAst
/// The last child of this node is the call constructing the expression tree, all other children are the
/// assignments to the ParameterExpression variables.
/// </summary>
ExpressionTreeParameterDeclarations
ExpressionTreeParameterDeclarations,
/// <summary>
/// C# 5 await
/// </summary>
Await
}
public static class ILCodeUtil

12
src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -155,5 +155,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -155,5 +155,17 @@ namespace ICSharpCode.Decompiler.ILAst
ILVariable v;
return node.Match(ILCode.Ldloc, out v) && v == expectedVar;
}
public static bool MatchLdloca(this ILNode node, ILVariable expectedVar)
{
ILVariable v;
return node.Match(ILCode.Ldloca, out v) && v == expectedVar;
}
public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr)
{
ILVariable v;
return node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar;
}
}
}

310
src/Libraries/ICSharpCode.Decompiler/ILAst/StateRange.cs

@ -0,0 +1,310 @@ @@ -0,0 +1,310 @@
// Copyright (c) 2012 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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
struct Interval
{
public readonly int Start, End;
public Interval(int start, int end)
{
Debug.Assert(start <= end || (start == 0 && end == -1));
this.Start = start;
this.End = end;
}
public override string ToString()
{
return string.Format("({0} to {1})", Start, End);
}
}
class StateRange
{
readonly List<Interval> data = new List<Interval>();
public StateRange()
{
}
public StateRange(int start, int end)
{
this.data.Add(new Interval(start, end));
}
public bool IsEmpty {
get { return data.Count == 0; }
}
public bool Contains(int val)
{
foreach (Interval v in data) {
if (v.Start <= val && val <= v.End)
return true;
}
return false;
}
public void UnionWith(StateRange other)
{
data.AddRange(other.data);
}
/// <summary>
/// Unions this state range with (other intersect (minVal to maxVal))
/// </summary>
public void UnionWith(StateRange other, int minVal, int maxVal)
{
foreach (Interval v in other.data) {
int start = Math.Max(v.Start, minVal);
int end = Math.Min(v.End, maxVal);
if (start <= end)
data.Add(new Interval(start, end));
}
}
/// <summary>
/// Merges overlapping interval ranges.
/// </summary>
public void Simplify()
{
if (data.Count < 2)
return;
data.Sort((a, b) => a.Start.CompareTo(b.Start));
Interval prev = data[0];
int prevIndex = 0;
for (int i = 1; i < data.Count; i++) {
Interval next = data[i];
Debug.Assert(prev.Start <= next.Start);
if (next.Start <= prev.End + 1) { // intervals overlapping or touching
prev = new Interval(prev.Start, Math.Max(prev.End, next.End));
data[prevIndex] = prev;
data[i] = new Interval(0, -1); // mark as deleted
} else {
prev = next;
prevIndex = i;
}
}
data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted
}
public override string ToString()
{
return string.Join(",", data);
}
public Interval ToEnclosingInterval()
{
if (data.Count == 0)
throw new SymbolicAnalysisFailedException();
return new Interval(data[0].Start, data[data.Count - 1].End);
}
}
enum StateRangeAnalysisMode
{
IteratorMoveNext,
IteratorDispose,
AsyncMoveNext
}
class StateRangeAnalysis
{
readonly StateRangeAnalysisMode mode;
readonly FieldDefinition stateField;
internal DefaultDictionary<ILNode, StateRange> ranges;
SymbolicEvaluationContext evalContext;
internal Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; // used only for IteratorDispose
/// <summary>
/// Initializes the state range logic:
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
/// </summary>
public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField)
{
this.mode = mode;
this.stateField = stateField;
if (mode == StateRangeAnalysisMode.IteratorDispose) {
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
}
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue);
evalContext = new SymbolicEvaluationContext(stateField);
}
public int AssignStateRanges(List<ILNode> body, int bodyLength)
{
if (bodyLength == 0)
return 0;
for (int i = 0; i < bodyLength; i++) {
StateRange nodeRange = ranges[body[i]];
nodeRange.Simplify();
ILLabel label = body[i] as ILLabel;
if (label != null) {
ranges[body[i + 1]].UnionWith(nodeRange);
continue;
}
ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
if (tryFinally != null) {
if (mode == StateRangeAnalysisMode.IteratorDispose) {
if (tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
throw new SymbolicAnalysisFailedException();
ranges[tryFinally.TryBlock].UnionWith(nodeRange);
if (tryFinally.TryBlock.Body.Count != 0) {
ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange);
AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count);
}
continue;
} else if (mode == StateRangeAnalysisMode.AsyncMoveNext) {
return i;
} else {
throw new SymbolicAnalysisFailedException();
}
}
ILExpression expr = body[i] as ILExpression;
if (expr == null)
throw new SymbolicAnalysisFailedException();
switch (expr.Code) {
case ILCode.Switch:
{
SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
if (val.Type != SymbolicValueType.State)
goto default;
ILLabel[] targetLabels = (ILLabel[])expr.Operand;
for (int j = 0; j < targetLabels.Length; j++) {
int state = j - val.Constant;
ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
}
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
break;
}
case ILCode.Br:
case ILCode.Leave:
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
break;
case ILCode.Brtrue:
{
SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.StateEquals) {
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
break;
} else if (val.Type == SymbolicValueType.StateInEquals) {
ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange targetRange = ranges[(ILLabel)expr.Operand];
targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
break;
} else {
goto default;
}
}
case ILCode.Nop:
ranges[body[i + 1]].UnionWith(nodeRange);
break;
case ILCode.Ret:
break;
case ILCode.Stloc:
{
SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.State && val.Constant == 0) {
evalContext.AddStateVariable((ILVariable)expr.Operand);
goto case ILCode.Nop;
} else {
goto default;
}
}
case ILCode.Call:
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
if (mode == StateRangeAnalysisMode.IteratorDispose) {
MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule();
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new SymbolicAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
break;
} else {
goto default;
}
default:
if (mode == StateRangeAnalysisMode.IteratorDispose) {
throw new SymbolicAnalysisFailedException();
} else {
return i;
}
}
}
return bodyLength;
}
public void EnsureLabelAtPos(List<ILNode> body, ref int pos, ref int bodyLength)
{
if (pos > 0 && body[pos - 1] is ILLabel) {
pos--;
} else {
// ensure that the first element at body[pos] is a label:
ILLabel newLabel = new ILLabel();
newLabel.Name = "YieldReturnEntryPoint";
ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos]
body.Insert(pos, newLabel);
bodyLength++;
}
}
public LabelRangeMapping CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength)
{
LabelRangeMapping result = new LabelRangeMapping();
CreateLabelRangeMapping(body, pos, bodyLength, result, false);
return result;
}
void CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength, LabelRangeMapping result, bool onlyInitialLabels)
{
for (int i = pos; i < bodyLength; i++) {
ILLabel label = body[i] as ILLabel;
if (label != null) {
result.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label]));
} else {
ILTryCatchBlock tryCatchBlock = body[i] as ILTryCatchBlock;
if (tryCatchBlock != null) {
CreateLabelRangeMapping(tryCatchBlock.TryBlock.Body, 0, tryCatchBlock.TryBlock.Body.Count, result, true);
} else if (onlyInitialLabels) {
break;
}
}
}
}
}
class LabelRangeMapping : List<KeyValuePair<ILLabel, StateRange>> {}
}

148
src/Libraries/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs

@ -0,0 +1,148 @@ @@ -0,0 +1,148 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// This exception is thrown when we find something else than we expect from the C# compiler.
/// This aborts the analysis and makes the whole transform fail.
/// </summary>
class SymbolicAnalysisFailedException : Exception {}
enum SymbolicValueType
{
/// <summary>
/// Unknown value
/// </summary>
Unknown,
/// <summary>
/// int: Constant (result of ldc.i4)
/// </summary>
IntegerConstant,
/// <summary>
/// int: State + Constant
/// </summary>
State,
/// <summary>
/// This pointer (result of ldarg.0)
/// </summary>
This,
/// <summary>
/// bool: State == Constant
/// </summary>
StateEquals,
/// <summary>
/// bool: State != Constant
/// </summary>
StateInEquals
}
struct SymbolicValue
{
public readonly int Constant;
public readonly SymbolicValueType Type;
public SymbolicValue(SymbolicValueType type, int constant = 0)
{
this.Type = type;
this.Constant = constant;
}
public override string ToString()
{
return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant);
}
}
class SymbolicEvaluationContext
{
readonly FieldDefinition stateField;
readonly List<ILVariable> stateVariables = new List<ILVariable>();
public SymbolicEvaluationContext(FieldDefinition stateField)
{
this.stateField = stateField;
}
public void AddStateVariable(ILVariable v)
{
if (!stateVariables.Contains(v))
stateVariables.Add(v);
}
SymbolicValue Failed()
{
return new SymbolicValue(SymbolicValueType.Unknown);
}
public SymbolicValue Eval(ILExpression expr)
{
SymbolicValue left, right;
switch (expr.Code) {
case ILCode.Sub:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant)
return Failed();
if (right.Type != SymbolicValueType.IntegerConstant)
return Failed();
return new SymbolicValue(left.Type, unchecked ( left.Constant - right.Constant ));
case ILCode.Ldfld:
if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This)
return Failed();
if (CecilExtensions.ResolveWithinSameModule(expr.Operand as FieldReference) != stateField)
return Failed();
return new SymbolicValue(SymbolicValueType.State);
case ILCode.Ldloc:
ILVariable loadedVariable = (ILVariable)expr.Operand;
if (stateVariables.Contains(loadedVariable))
return new SymbolicValue(SymbolicValueType.State);
else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0)
return new SymbolicValue(SymbolicValueType.This);
else
return Failed();
case ILCode.Ldc_I4:
return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand);
case ILCode.Ceq:
case ILCode.Cne:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
return Failed();
// bool: (state + left.Constant == right.Constant)
// bool: (state == right.Constant - left.Constant)
return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant));
case ILCode.LogicNot:
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.StateEquals)
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant);
else if (val.Type == SymbolicValueType.StateInEquals)
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant);
else
return Failed();
default:
return Failed();
}
}
}
}

21
src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -790,8 +790,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -790,8 +790,17 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.YieldBreak:
return null;
case ILCode.Ret:
if (forceInferChildren && expr.Arguments.Count == 1)
InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType);
if (forceInferChildren && expr.Arguments.Count == 1) {
TypeReference returnType = context.CurrentMethod.ReturnType;
if (context.CurrentMethodIsAsync && returnType != null && returnType.Namespace == "System.Threading.Tasks") {
if (returnType.Name == "Task") {
returnType = typeSystem.Void;
} else if (returnType.Name == "Task`1" && returnType.IsGenericInstance) {
returnType = ((GenericInstanceType)returnType).GenericArguments[0];
}
}
InferTypeForExpression(expr.Arguments[0], returnType);
}
return null;
case ILCode.YieldReturn:
if (forceInferChildren) {
@ -803,6 +812,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -803,6 +812,14 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
return null;
case ILCode.Await:
{
TypeReference taskType = InferTypeForExpression(expr.Arguments[0], null);
if (taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") {
return ((GenericInstanceType)taskType).GenericArguments[0];
}
return null;
}
#endregion
case ILCode.Pop:
return null;

418
src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -24,7 +24,7 @@ using Mono.Cecil; @@ -24,7 +24,7 @@ using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public class YieldReturnDecompiler
class YieldReturnDecompiler
{
// For a description on the code generated by the C# compiler for yield return:
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx
@ -34,11 +34,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -34,11 +34,8 @@ namespace ICSharpCode.Decompiler.ILAst
// - Figure out which of the fields is the state field
// - Construct an exception table based on states. This allows us to determine, for each state, what the parent try block is.
/// <summary>
/// This exception is thrown when we find something else than we expect from the C# compiler.
/// This aborts the analysis and makes the whole transform fail.
/// </summary>
class YieldAnalysisFailedException : Exception {}
// See http://community.sharpdevelop.net/blogs/danielgrunwald/archive/2011/03/06/ilspy-yield-return.aspx
// for a description of this step.
DecompilerContext context;
TypeDefinition enumeratorType;
@ -66,7 +63,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -66,7 +63,7 @@ namespace ICSharpCode.Decompiler.ILAst
#endif
try {
yrd.Run();
} catch (YieldAnalysisFailedException) {
} catch (SymbolicAnalysisFailedException) {
return;
}
#if DEBUG
@ -211,7 +208,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -211,7 +208,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
if (stateField == null)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
}
/// <summary>
@ -220,7 +217,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -220,7 +217,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILBlock CreateILAst(MethodDefinition method)
{
if (method == null || !method.HasBody)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
ILBlock ilMethod = new ILBlock();
ILAstBuilder astBuilder = new ILAstBuilder();
@ -269,7 +266,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -269,7 +266,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
if (currentField == null)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
}
#endregion
@ -322,337 +319,30 @@ namespace ICSharpCode.Decompiler.ILAst @@ -322,337 +319,30 @@ namespace ICSharpCode.Decompiler.ILAst
disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose");
ILBlock ilMethod = CreateILAst(disposeMethod);
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
InitStateRanges(ilMethod.Body[0]);
AssignStateRanges(ilMethod.Body, ilMethod.Body.Count, forDispose: true);
var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count);
finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval;
// Now look at the finally blocks:
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
Interval interval = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
var finallyBody = tryFinally.FinallyBlock.Body;
if (finallyBody.Count != 2)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
ILExpression call = finallyBody[0] as ILExpression;
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
if (!call.Arguments[0].MatchThis())
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
if (!finallyBody[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, interval);
}
ranges = null;
}
#endregion
#region Assign StateRanges / Symbolic Execution (used for analysis of Dispose() and MoveNext())
#region struct Interval / class StateRange
struct Interval
{
public readonly int Start, End;
public Interval(int start, int end)
{
Debug.Assert(start <= end || (start == 0 && end == -1));
this.Start = start;
this.End = end;
}
public override string ToString()
{
return string.Format("({0} to {1})", Start, End);
}
}
class StateRange
{
readonly List<Interval> data = new List<Interval>();
public StateRange()
{
}
public StateRange(int start, int end)
{
this.data.Add(new Interval(start, end));
}
public bool Contains(int val)
{
foreach (Interval v in data) {
if (v.Start <= val && val <= v.End)
return true;
}
return false;
}
public void UnionWith(StateRange other)
{
data.AddRange(other.data);
}
/// <summary>
/// Unions this state range with (other intersect (minVal to maxVal))
/// </summary>
public void UnionWith(StateRange other, int minVal, int maxVal)
{
foreach (Interval v in other.data) {
int start = Math.Max(v.Start, minVal);
int end = Math.Min(v.End, maxVal);
if (start <= end)
data.Add(new Interval(start, end));
}
}
/// <summary>
/// Merges overlapping interval ranges.
/// </summary>
public void Simplify()
{
if (data.Count < 2)
return;
data.Sort((a, b) => a.Start.CompareTo(b.Start));
Interval prev = data[0];
int prevIndex = 0;
for (int i = 1; i < data.Count; i++) {
Interval next = data[i];
Debug.Assert(prev.Start <= next.Start);
if (next.Start <= prev.End + 1) { // intervals overlapping or touching
prev = new Interval(prev.Start, Math.Max(prev.End, next.End));
data[prevIndex] = prev;
data[i] = new Interval(0, -1); // mark as deleted
} else {
prev = next;
prevIndex = i;
}
}
data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted
}
public override string ToString()
{
return string.Join(",", data);
}
public Interval ToEnclosingInterval()
{
if (data.Count == 0)
throw new YieldAnalysisFailedException();
return new Interval(data[0].Start, data[data.Count - 1].End);
}
}
#endregion
DefaultDictionary<ILNode, StateRange> ranges;
ILVariable rangeAnalysisStateVariable;
/// <summary>
/// Initializes the state range logic:
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
/// </summary>
void InitStateRanges(ILNode entryPoint)
{
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue);
rangeAnalysisStateVariable = null;
}
int AssignStateRanges(List<ILNode> body, int bodyLength, bool forDispose)
{
if (bodyLength == 0)
return 0;
for (int i = 0; i < bodyLength; i++) {
StateRange nodeRange = ranges[body[i]];
nodeRange.Simplify();
ILLabel label = body[i] as ILLabel;
if (label != null) {
ranges[body[i + 1]].UnionWith(nodeRange);
continue;
}
ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
if (tryFinally != null) {
if (!forDispose || tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
throw new YieldAnalysisFailedException();
ranges[tryFinally.TryBlock].UnionWith(nodeRange);
if (tryFinally.TryBlock.Body.Count != 0) {
ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange);
AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count, forDispose);
}
continue;
}
ILExpression expr = body[i] as ILExpression;
if (expr == null)
throw new YieldAnalysisFailedException();
switch (expr.Code) {
case ILCode.Switch:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type != SymbolicValueType.State)
throw new YieldAnalysisFailedException();
ILLabel[] targetLabels = (ILLabel[])expr.Operand;
for (int j = 0; j < targetLabels.Length; j++) {
int state = j - val.Constant;
ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
}
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
break;
}
case ILCode.Br:
case ILCode.Leave:
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
break;
case ILCode.Brtrue:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.StateEquals) {
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
} else if (val.Type == SymbolicValueType.StateInEquals) {
ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange targetRange = ranges[(ILLabel)expr.Operand];
targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
} else {
throw new YieldAnalysisFailedException();
}
break;
}
case ILCode.Nop:
ranges[body[i + 1]].UnionWith(nodeRange);
break;
case ILCode.Ret:
break;
case ILCode.Stloc:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.State && val.Constant == 0 && rangeAnalysisStateVariable == null)
rangeAnalysisStateVariable = (ILVariable)expr.Operand;
else
throw new YieldAnalysisFailedException();
goto case ILCode.Nop;
}
case ILCode.Call:
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
if (forDispose) {
MethodDefinition mdef = GetMethodDefinition(expr.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
} else {
throw new YieldAnalysisFailedException();
}
break;
default:
if (forDispose)
throw new YieldAnalysisFailedException();
else
return i;
}
}
return bodyLength;
}
enum SymbolicValueType
{
/// <summary>
/// int: Constant (result of ldc.i4)
/// </summary>
IntegerConstant,
/// <summary>
/// int: State + Constant
/// </summary>
State,
/// <summary>
/// This pointer (result of ldarg.0)
/// </summary>
This,
/// <summary>
/// bool: State == Constant
/// </summary>
StateEquals,
/// <summary>
/// bool: State != Constant
/// </summary>
StateInEquals
}
struct SymbolicValue
{
public readonly int Constant;
public readonly SymbolicValueType Type;
public SymbolicValue(SymbolicValueType type, int constant = 0)
{
this.Type = type;
this.Constant = constant;
}
public override string ToString()
{
return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant);
}
}
SymbolicValue Eval(ILExpression expr)
{
SymbolicValue left, right;
switch (expr.Code) {
case ILCode.Sub:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
if (right.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
return new SymbolicValue(left.Type, unchecked ( left.Constant - right.Constant ));
case ILCode.Ldfld:
if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This)
throw new YieldAnalysisFailedException();
if (GetFieldDefinition(expr.Operand as FieldReference) != stateField)
throw new YieldAnalysisFailedException();
return new SymbolicValue(SymbolicValueType.State);
case ILCode.Ldloc:
ILVariable loadedVariable = (ILVariable)expr.Operand;
if (loadedVariable == rangeAnalysisStateVariable)
return new SymbolicValue(SymbolicValueType.State);
else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0)
return new SymbolicValue(SymbolicValueType.This);
else
throw new YieldAnalysisFailedException();
case ILCode.Ldc_I4:
return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand);
case ILCode.Ceq:
case ILCode.Cne:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
// bool: (state + left.Constant == right.Constant)
// bool: (state == right.Constant - left.Constant)
return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant));
case ILCode.LogicNot:
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.StateEquals)
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant);
else if (val.Type == SymbolicValueType.StateInEquals)
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant);
else
throw new YieldAnalysisFailedException();
default:
throw new YieldAnalysisFailedException();
}
rangeAnalysis = null;
}
#endregion
@ -667,10 +357,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -667,10 +357,10 @@ namespace ICSharpCode.Decompiler.ILAst
ILBlock ilMethod = CreateILAst(moveNextMethod);
if (ilMethod.Body.Count == 0)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
ILExpression lastReturnArg;
if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
// There are two possibilities:
if (lastReturnArg.Code == ILCode.Ldloc) {
@ -678,14 +368,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -678,14 +368,14 @@ namespace ICSharpCode.Decompiler.ILAst
returnVariable = (ILVariable)lastReturnArg.Operand;
returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
if (returnLabel == null)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
} else {
// b) the compiler directly returns constants
returnVariable = null;
returnLabel = null;
// In this case, the last return must return false.
if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
}
ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
@ -694,23 +384,23 @@ namespace ICSharpCode.Decompiler.ILAst @@ -694,23 +384,23 @@ namespace ICSharpCode.Decompiler.ILAst
if (tryFaultBlock != null) {
// there are try-finally blocks
if (returnVariable == null) // in this case, we must use a return variable
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
// must be a try-fault block:
if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
ILBlock faultBlock = tryFaultBlock.FaultBlock;
// Ensure the fault block contains the call to Dispose().
if (faultBlock.Body.Count != 2)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
MethodReference disposeMethodRef;
ILExpression disposeArg;
if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
if (!faultBlock.Body[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
body = tryFaultBlock.TryBlock.Body;
bodyLength = body.Count;
@ -734,39 +424,22 @@ namespace ICSharpCode.Decompiler.ILAst @@ -734,39 +424,22 @@ namespace ICSharpCode.Decompiler.ILAst
bodyLength--;
ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
bodyLength--; // don't conside the stloc instruction to be part of the body
}
// verify that the last element in the body is a label pointing to the 'ret(false)'
returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
if (returnFalseLabel == null)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
InitStateRanges(body[0]);
int pos = AssignStateRanges(body, bodyLength, forDispose: false);
if (pos > 0 && body[pos - 1] is ILLabel) {
pos--;
} else {
// ensure that the first element at body[pos] is a label:
ILLabel newLabel = new ILLabel();
newLabel.Name = "YieldReturnEntryPoint";
ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos]
body.Insert(pos, newLabel);
bodyLength++;
}
List<KeyValuePair<ILLabel, StateRange>> labels = new List<KeyValuePair<ILLabel, StateRange>>();
for (int i = pos; i < bodyLength; i++) {
ILLabel label = body[i] as ILLabel;
if (label != null) {
labels.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label]));
}
}
var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField);
int pos = rangeAnalysis.AssignStateRanges(body, bodyLength);
rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);
var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);
ConvertBody(body, pos, bodyLength, labels);
}
#endregion
@ -797,7 +470,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -797,7 +470,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Handle stores to 'state' or 'current'
if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) {
if (expr.Arguments[1].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
currentState = (int)expr.Arguments[1].Operand;
stateChanges.Add(new SetState(newBody.Count, currentState));
} else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) {
@ -809,18 +482,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -809,18 +482,18 @@ namespace ICSharpCode.Decompiler.ILAst
// handle store+branch to the returnVariable
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
int val = (int)expr.Arguments[0].Operand;
if (val == 0) {
newBody.Add(MakeGoTo(returnFalseLabel));
} else if (val == 1) {
newBody.Add(MakeGoTo(labels, currentState));
} else {
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
}
} else if (expr != null && expr.Code == ILCode.Ret) {
if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
// handle direct return (e.g. in release builds)
int val = (int)expr.Arguments[0].Operand;
if (val == 0) {
@ -828,24 +501,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -828,24 +501,24 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (val == 1) {
newBody.Add(MakeGoTo(labels, currentState));
} else {
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
}
} else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis()) {
MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference);
if (method == null)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
Interval interval;
if (method == disposeMethod) {
// Explicit call to dispose is used for "yield break;" within the method.
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
newBody.Add(MakeGoTo(returnFalseLabel));
} else if (finallyMethodToStateInterval.TryGetValue(method, out interval)) {
// Call to Finally-method
int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End);
if (index < 0)
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
ILLabel label = new ILLabel();
label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End;
@ -883,7 +556,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -883,7 +556,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (pair.Value.Contains(state))
return MakeGoTo(pair.Key);
}
throw new YieldAnalysisFailedException();
throw new SymbolicAnalysisFailedException();
}
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod)
@ -907,6 +580,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -907,6 +580,11 @@ namespace ICSharpCode.Decompiler.ILAst
#region TranslateFieldsToLocalAccess
void TranslateFieldsToLocalAccess()
{
TranslateFieldsToLocalAccess(newBody, fieldToParameterMap);
}
internal static void TranslateFieldsToLocalAccess(List<ILNode> newBody, Dictionary<FieldDefinition, ILVariable> fieldToParameterMap)
{
var fieldToLocalMap = new DefaultDictionary<FieldDefinition, ILVariable>(f => new ILVariable { Name = f.Name, Type = f.FieldType });
foreach (ILNode node in newBody) {

4
src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs

@ -19,8 +19,8 @@ using System.Runtime.InteropServices; @@ -19,8 +19,8 @@ using System.Runtime.InteropServices;
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("2.0.0.1595")]
[assembly: AssemblyInformationalVersion("2.0.0.1595-5773d3d2")]
[assembly: AssemblyVersion("2.1.0.1603")]
[assembly: AssemblyInformationalVersion("2.1.0.1603-1170e2f8")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",

144
src/Libraries/ICSharpCode.Decompiler/Tests/Async.cs

@ -0,0 +1,144 @@ @@ -0,0 +1,144 @@
// 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.
#pragma warning disable 1998
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
public class Async
{
public async void SimpleVoidMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
}
public async void VoidMethodWithoutAwait()
{
Console.WriteLine("No Await");
}
public async Task SimpleVoidTaskMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
}
public async Task TaskMethodWithoutAwait()
{
Console.WriteLine("No Await");
}
public async Task<bool> SimpleBoolTaskMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
return true;
}
public async void TwoAwaitsWithDifferentAwaiterTypes()
{
Console.WriteLine("Before");
if (await this.SimpleBoolTaskMethod())
{
await Task.Delay(TimeSpan.FromSeconds(1.0));
}
Console.WriteLine("After");
}
public async void StreamCopyTo(Stream destination, int bufferSize)
{
byte[] array = new byte[bufferSize];
int count;
while ((count = await destination.ReadAsync(array, 0, array.Length)) != 0)
{
await destination.WriteAsync(array, 0, count);
}
}
public async void StreamCopyToWithConfigureAwait(Stream destination, int bufferSize)
{
byte[] array = new byte[bufferSize];
int count;
while ((count = await destination.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(array, 0, count).ConfigureAwait(false);
}
}
public async void AwaitInLoopCondition()
{
while (await this.SimpleBoolTaskMethod())
{
Console.WriteLine("Body");
}
}
public async Task<int> AwaitInForEach(IEnumerable<Task<int>> elements)
{
int num = 0;
foreach (Task<int> current in elements)
{
num += await current;
}
return num;
}
public async Task TaskMethodWithoutAwaitButWithExceptionHandling()
{
try
{
using (new StringWriter())
{
Console.WriteLine("No Await");
}
}
catch (Exception)
{
Console.WriteLine("Crash");
}
}
public async Task<int> NestedAwait(Task<Task<int>> task)
{
return await(await task);
}
public async Task AwaitWithStack(Task<int> task)
{
Console.WriteLine("A", 1, await task);
}
public async Task AwaitWithStack2(Task<int> task)
{
if (await this.SimpleBoolTaskMethod())
{
Console.WriteLine("A", 1, await task);
}
else
{
int num = 1;
Console.WriteLine("A", 1, num);
}
}
}

4
src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler.Tests</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler.Tests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<NoWarn>67,169,1058,728,1720,649</NoWarn>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -62,6 +63,7 @@ @@ -62,6 +63,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Async.cs" />
<Compile Include="CallOverloadedMethod.cs" />
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="ControlFlow.cs" />

2
src/Libraries/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs

@ -24,7 +24,7 @@ public class PropertiesAndEvents @@ -24,7 +24,7 @@ public class PropertiesAndEvents
public event EventHandler AutomaticEvent;
[field: NonSerialized]
public event EventHandler AutomaticEventWithInitializer = delegate
public event EventHandler AutomaticEventWithInitializer = delegate(object sender, EventArgs e)
{
};

6
src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -34,6 +34,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -34,6 +34,12 @@ namespace ICSharpCode.Decompiler.Tests
[TestFixture]
public class TestRunner
{
[Test]
public void Async()
{
TestFile(@"..\..\Tests\Async.cs");
}
[Test, Ignore("disambiguating overloads is not yet implemented")]
public void CallOverloadedMethod()
{

Loading…
Cancel
Save