135 changed files with 6849 additions and 2281 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
#region Using directives
|
||||
|
||||
using System; |
||||
using System.Reflection; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
#endregion
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("${ProjectName}")] |
||||
[assembly: AssemblyDescription("")] |
||||
[assembly: AssemblyConfiguration("")] |
||||
[assembly: AssemblyCompany("")] |
||||
[assembly: AssemblyProduct("${ProjectName}")] |
||||
[assembly: AssemblyCopyright("Copyright ${DATE:yyyy}")] |
||||
[assembly: AssemblyTrademark("")] |
||||
[assembly: AssemblyCulture("")] |
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all the values or you can use the default the Revision and
|
||||
// Build Numbers by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.*")] |
@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0"?> |
||||
<Template originator = "Daniel Grunwald" created = "05/17/2012"> |
||||
|
||||
<!-- Template Header --> |
||||
<TemplateConfiguration> |
||||
<Name>${res:Templates.Project.PortableLibrary.Name}</Name> |
||||
<Category>C#</Category> |
||||
<Icon>C#.Project.Library</Icon> |
||||
<Description>${res:Templates.Project.PortableLibrary.Description}</Description> |
||||
</TemplateConfiguration> |
||||
|
||||
<!-- Actions --> |
||||
<Actions> |
||||
<Open filename = "MyClass.cs"/> |
||||
</Actions> |
||||
|
||||
<!-- Template Content --> |
||||
<Project language="C#"> |
||||
<PreCreateActions> |
||||
<RunCommand path="/SharpDevelop/BackendBindings/TemplateCommands/CheckPortableLibraryInstalled"/> |
||||
</PreCreateActions> |
||||
|
||||
<PropertyGroup> |
||||
<OutputType>Library</OutputType> |
||||
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> |
||||
<TargetFrameworkProfile>Profile1</TargetFrameworkProfile> |
||||
</PropertyGroup> |
||||
|
||||
<PropertyGroup escapeValue="False"> |
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
</PropertyGroup> |
||||
|
||||
<ProjectItems /> |
||||
|
||||
<Imports clear="True"> |
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
||||
</Imports> |
||||
|
||||
<Files> |
||||
<File name="MyClass.cs"><![CDATA[${StandardHeader.C#} |
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace ${StandardNamespace} |
||||
{ |
||||
/// <summary> |
||||
/// Description of MyClass. |
||||
/// </summary> |
||||
public class MyClass |
||||
{ |
||||
|
||||
} |
||||
}]]></File> |
||||
<File name="Properties\AssemblyInfo.cs" src="PortableAssemblyInfo.cs"/> |
||||
</Files> |
||||
</Project> |
||||
</Template> |
@ -1,51 +1,67 @@
@@ -1,51 +1,67 @@
|
||||
#region Usings
|
||||
|
||||
using System; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using ICSharpCode.SharpDevelop.Project; |
||||
using System.Data.Entity.Design; |
||||
using System.CodeDom; |
||||
using System.Data.Metadata.Edm; |
||||
using System.Xml.Linq; |
||||
using System.IO; |
||||
using System.Xml.Linq; |
||||
|
||||
#endregion
|
||||
using ICSharpCode.SharpDevelop.Project; |
||||
|
||||
namespace ICSharpCode.Data.EDMDesigner.Core.IO |
||||
{ |
||||
public class EntityModelCodeGenerator : IO, ICustomTool |
||||
{ |
||||
#region ICustomTool Member
|
||||
|
||||
public void GenerateCode(FileProjectItem item, CustomToolContext context) |
||||
{ |
||||
LanguageOption languageToGenerateCode = LanguageOption.GenerateCSharpCode; |
||||
|
||||
if (item.Project.Language != "C#") |
||||
languageToGenerateCode = LanguageOption.GenerateVBCode; |
||||
|
||||
XDocument edmxDocument = XDocument.Load(item.FileName); |
||||
XElement conceptualModelsElement = EDMXIO.ReadSection(edmxDocument, EDMXIO.EDMXSection.CSDL); |
||||
|
||||
if (conceptualModelsElement == null) |
||||
throw new ArgumentException("Input file is not a valid EDMX file."); |
||||
|
||||
XDocument csdlDocument = new XDocument(new XDeclaration("1.0", "utf-8", null), conceptualModelsElement.Element(XName.Get("Schema", csdlNamespace.NamespaceName))); |
||||
|
||||
string tempFileName = IO.GetTempFilenameWithExtension("csdl"); |
||||
csdlDocument.Save(tempFileName); |
||||
public class EntityModelCodeGenerator : IO, ICustomTool |
||||
{ |
||||
public void GenerateCode(FileProjectItem item, CustomToolContext context) |
||||
{ |
||||
XElement schema = GetModelSchema(item); |
||||
XDocument csdlDocument = CreateCsdlDocument(schema); |
||||
|
||||
string tempFileName = IO.GetTempFilenameWithExtension("csdl"); |
||||
csdlDocument.Save(tempFileName); |
||||
|
||||
string outputFileName = context.GetOutputFileName(item, "Designer"); |
||||
LanguageOption languageToGenerateCode = GetLanguageOption(item); |
||||
string outputFileName = context.GetOutputFileName(item, "Designer"); |
||||
|
||||
EntityCodeGenerator entityCodeGenerator = new EntityCodeGenerator(languageToGenerateCode); |
||||
IList<EdmSchemaError> edmSchemaErrors = entityCodeGenerator.GenerateCode(tempFileName, outputFileName); |
||||
File.Delete(tempFileName); |
||||
|
||||
context.EnsureOutputFileIsInProject(item, outputFileName); |
||||
} |
||||
EntityCodeGenerator entityCodeGenerator = new EntityCodeGenerator(languageToGenerateCode); |
||||
AddNamespaceMapping(entityCodeGenerator, schema, context.OutputNamespace); |
||||
IList<EdmSchemaError> edmSchemaErrors = entityCodeGenerator.GenerateCode(tempFileName, outputFileName); |
||||
File.Delete(tempFileName); |
||||
|
||||
context.EnsureOutputFileIsInProject(item, outputFileName); |
||||
} |
||||
|
||||
XElement GetModelSchema(FileProjectItem item) |
||||
{ |
||||
XDocument edmxDocument = XDocument.Load(item.FileName); |
||||
XElement conceptualModelsElement = EDMXIO.ReadSection(edmxDocument, EDMXIO.EDMXSection.CSDL); |
||||
if (conceptualModelsElement == null) |
||||
throw new ArgumentException("Input file is not a valid EDMX file."); |
||||
|
||||
#endregion
|
||||
} |
||||
return conceptualModelsElement.Element(XName.Get("Schema", csdlNamespace.NamespaceName)); |
||||
} |
||||
|
||||
XDocument CreateCsdlDocument(XElement schema) |
||||
{ |
||||
return new XDocument(new XDeclaration("1.0", "utf-8", null), schema); |
||||
} |
||||
|
||||
LanguageOption GetLanguageOption(FileProjectItem item) |
||||
{ |
||||
if (item.Project.Language != "C#") |
||||
return LanguageOption.GenerateVBCode; |
||||
return LanguageOption.GenerateCSharpCode; |
||||
} |
||||
|
||||
void AddNamespaceMapping(EntityCodeGenerator entityCodeGenerator, XElement schema, string outputNamespace) |
||||
{ |
||||
if (!String.IsNullOrEmpty(outputNamespace)) |
||||
AddNamespaceMapping(entityCodeGenerator, schema.Attribute(XName.Get("Namespace")), outputNamespace); |
||||
} |
||||
|
||||
void AddNamespaceMapping(EntityCodeGenerator entityCodeGenerator, XAttribute edmNamespace, string outputNamespace) |
||||
{ |
||||
if (edmNamespace != null) |
||||
entityCodeGenerator.EdmToObjectNamespaceMap.Add(edmNamespace.Value, outputNamespace); |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Windows; |
||||
using System.Windows.Media; |
||||
|
||||
using ICSharpCode.AvalonEdit.Rendering; |
||||
using ICSharpCode.AvalonEdit.Utils; |
||||
|
||||
namespace ICSharpCode.AvalonEdit |
||||
{ |
||||
/// <summary>
|
||||
/// Renders a ruler at a certain column.
|
||||
/// </summary>
|
||||
public class ColumnRulerRenderer : IBackgroundRenderer |
||||
{ |
||||
Pen pen; |
||||
int column; |
||||
TextView textView; |
||||
|
||||
public const string Name = "Column ruler"; |
||||
public static readonly Color DefaultForeground = Colors.LightGray; |
||||
|
||||
public ColumnRulerRenderer(TextView textView) |
||||
{ |
||||
if (textView == null) |
||||
throw new ArgumentNullException("textView"); |
||||
|
||||
this.pen = new Pen(new SolidColorBrush(DefaultForeground), 1); |
||||
this.pen.Freeze(); |
||||
this.textView = textView; |
||||
this.textView.BackgroundRenderers.Add(this); |
||||
} |
||||
|
||||
public KnownLayer Layer { |
||||
get { return KnownLayer.Background; } |
||||
} |
||||
|
||||
public void SetRuler(int column, Brush brush) |
||||
{ |
||||
if (this.column != column) { |
||||
this.column = column; |
||||
textView.InvalidateLayer(this.Layer); |
||||
} |
||||
if (pen.Brush != brush) { |
||||
this.pen = new Pen(brush, 1); |
||||
this.pen.Freeze(); |
||||
textView.InvalidateLayer(this.Layer); |
||||
} |
||||
} |
||||
|
||||
public void Draw(TextView textView, System.Windows.Media.DrawingContext drawingContext) |
||||
{ |
||||
if (column < 1) return; |
||||
double offset = textView.WideSpaceWidth * column; |
||||
Size pixelSize = PixelSnapHelpers.GetPixelSize(textView); |
||||
double markerXPos = PixelSnapHelpers.PixelAlign(offset, pixelSize.Width); |
||||
Point start = new Point(markerXPos, 0); |
||||
Point end = new Point(markerXPos, Math.Max(textView.DocumentHeight, textView.ActualHeight)); |
||||
|
||||
drawingContext.DrawLine(pen, start, end); |
||||
} |
||||
} |
||||
} |
@ -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
|
||||
} |
||||
} |
@ -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>> {} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue