From c42bf520b8f2f9923ec46d9282cbd5adbd77771a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Sun, 5 Jun 2011 17:28:03 +0200 Subject: [PATCH 01/18] Highlighting references to clicked variable or parameter. --- .../Ast/AstMethodBodyBuilder.cs | 3 +- .../Ast/TextOutputFormatter.cs | 61 +++++++++++++++++-- .../Ast/Transforms/DeclareVariables.cs | 40 +++++++----- .../Ast/Transforms/DelegateConstruction.cs | 17 ++++-- .../Transforms/PatternStatementTransform.cs | 13 ++-- ICSharpCode.Decompiler/ITextOutput.cs | 2 +- ICSharpCode.Decompiler/PlainTextOutput.cs | 2 +- ILSpy/AvalonEdit/TextMarkerService.cs | 4 +- ILSpy/TextView/AvalonEditTextOutput.cs | 9 ++- ILSpy/TextView/DecompilerTextView.cs | 28 ++++++++- ILSpy/TextView/ReferenceElementGenerator.cs | 5 +- 11 files changed, 144 insertions(+), 40 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 1958b083a..6e5f5ff99 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -128,6 +128,7 @@ namespace ICSharpCode.Decompiler.Ast else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(type, v.Name); + newVarDecl.Variables.Single().AddAnnotation(v); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } @@ -210,7 +211,7 @@ namespace ICSharpCode.Decompiler.Ast Type = AstBuilder.ConvertType(catchClause.ExceptionType), VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, Body = TransformBlock(catchClause) - }); + }.WithAnnotation(catchClause.ExceptionVariable)); } } if (tryCatchNode.FinallyBlock != null) diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 7c8ca1230..a866cf8e3 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -43,12 +43,26 @@ namespace ICSharpCode.Decompiler.Ast public void WriteIdentifier(string identifier) { - MemberReference memberRef = GetCurrentMemberReference(); - - if (memberRef != null) + object memberRef = GetCurrentMemberReference(); + + if (memberRef != null) { output.WriteReference(identifier, memberRef); - else - output.Write(identifier); + return; + } + + var definition = GetCurrentLocalDefinition(); + if (definition != null) { + output.WriteDefinition(identifier, definition); + return; + } + + memberRef = GetCurrentLocalReference(); + if (memberRef != null) { + output.WriteReference(identifier, memberRef, true); + return; + } + + output.Write(identifier); } MemberReference GetCurrentMemberReference() @@ -60,6 +74,43 @@ namespace ICSharpCode.Decompiler.Ast } return memberRef; } + + object GetCurrentLocalReference() + { + AstNode node = nodeStack.Peek(); + ILVariable variable = node.Annotation(); + if (variable != null) { + if (variable.OriginalParameter != null) + return variable.OriginalParameter; + //if (variable.OriginalVariable != null) + // return variable.OriginalVariable; + return variable; + } + return null; + } + + object GetCurrentLocalDefinition() + { + AstNode node = nodeStack.Peek(); + var parameterDef = node.Annotation(); + if (parameterDef != null) + return parameterDef; + + if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) { + var variable = node.Annotation(); + if (variable != null) { + if (variable.OriginalParameter != null) + return variable.OriginalParameter; + //if (variable.OriginalVariable != null) + // return variable.OriginalVariable; + return variable; + } else { + + } + } + + return null; + } public void WriteKeyword(string keyword) { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs index ed2144085..db11f488f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; @@ -35,6 +36,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms { public AstType Type; public string Name; + public ILVariable Variable; public AssignmentExpression ReplacedAssignment; public Statement InsertionPoint; @@ -57,9 +59,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (var v in variablesToDeclare) { if (v.ReplacedAssignment == null) { BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; + var vds = new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name); + if(v.Variable != null) + vds.Variables.First().AddAnnotation(v.Variable); block.Statements.InsertBefore( v.InsertionPoint, - new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name)); + vds); } } // First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST. @@ -67,9 +72,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (v.ReplacedAssignment != null) { // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, // which might be still in use by the definite assignment graph. + var variableInit = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()) + .CopyAnnotationsFrom(v.ReplacedAssignment) + .CopyAnnotationsFrom(v.ReplacedAssignment.Left) + .WithAnnotation(v.Variable); VariableDeclarationStatement varDecl = new VariableDeclarationStatement { Type = (AstType)v.Type.Clone(), - Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) } + Variables = { variableInit } }; ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; if (es != null) { @@ -102,7 +111,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (VariableDeclarationStatement varDecl in variables) { string variableName = varDecl.Variables.Single().Name; bool allowPassIntoLoops = varDecl.Variables.Single().Annotation() == null; - DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops); + ILVariable ilVariable = varDecl.Variables.Single().Annotation(); + DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops, ilVariable); } } } @@ -111,7 +121,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } - void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops) + void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops, ILVariable ilVariable) { // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block Statement declarationPoint = null; @@ -127,52 +137,52 @@ namespace ICSharpCode.Decompiler.Ast.Transforms ForStatement forStmt = stmt as ForStatement; if (forStmt != null && forStmt.Initializers.Count == 1) { // handle the special case of moving a variable into the for initializer - if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName)) + if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName, ilVariable)) continue; } UsingStatement usingStmt = stmt as UsingStatement; if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) { // handle the special case of moving a variable into a using statement - if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName)) + if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName, ilVariable)) continue; } foreach (AstNode child in stmt.Children) { BlockStatement subBlock = child as BlockStatement; if (subBlock != null) { - DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops, ilVariable); } else if (HasNestedBlocks(child)) { foreach (BlockStatement nestedSubBlock in child.Children.OfType()) { - DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops, ilVariable); } } } } } else { // Try converting an assignment expression into a VariableDeclarationStatement - if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { + if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName, ilVariable)) { // Declare the variable in front of declarationPoint - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint, Variable = ilVariable }); } } } - bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName) + bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName, ILVariable ilVariable) { // convert the declarationPoint into a VariableDeclarationStatement ExpressionStatement es = declarationPoint as ExpressionStatement; if (es != null) { - return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName); + return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName, ilVariable); } return false; } - - bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName) + + bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName, ILVariable ilVariable) { AssignmentExpression ae = expression as AssignmentExpression; if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { IdentifierExpression ident = ae.Left as IdentifierExpression; if (ident != null && ident.Identifier == variableName) { - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae, Variable = ilVariable }); return true; } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 0e431e3f8..17cca7650 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } // Now create variables for all fields of the display class (except for those that we already handled as parameters) - List> variablesToDeclare = new List>(); + List> variablesToDeclare = new List>(); foreach (FieldDefinition field in type.Fields) { if (field.IsStatic) continue; // skip static fields @@ -397,8 +397,14 @@ namespace ICSharpCode.Decompiler.Ast.Transforms capturedVariableName = capturedVariableName.Substring(10); EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); currentlyUsedVariableNames.Add(capturedVariableName); - variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), capturedVariableName)); - dict[field] = new IdentifierExpression(capturedVariableName); + ILVariable ilVar = new ILVariable + { + IsGenerated = true, + Name = capturedVariableName, + Type = field.FieldType, + }; + variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar)); + dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar); } // Now figure out where the closure was accessed and use the simpler replacement expression there: @@ -414,15 +420,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); foreach (var tuple in variablesToDeclare) { - var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2); + var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); + newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); } } currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); return null; } - + void EnsureVariableNameIsAvailable(AstNode currentNode, string name) { int pos = currentlyUsedVariableNames.IndexOf(name); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 3d5c0e596..900ef0b4f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -227,6 +227,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Name = variableName, Initializer = m1.Get("initializer").Single().Detach() }.CopyAnnotationsFrom(node.Expression) + .WithAnnotation(m1.Get("variable").Single().Annotation()) } }.CopyAnnotationsFrom(node); } else { @@ -382,7 +383,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms VariableName = itemVar.Identifier, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody - }; + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); if (foreachStatement.InExpression is BaseReferenceExpression) { foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression); } @@ -471,10 +472,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // We just care that we can move it in front of the loop: if (declarationPoint != loop) return null; - - ForeachStatement foreachStatement = new ForeachStatement(); - foreachStatement.VariableType = itemVarDecl.Type.Clone(); - foreachStatement.VariableName = itemVar.Identifier; + + ForeachStatement foreachStatement = new ForeachStatement + { + VariableType = itemVarDecl.Type.Clone(), + VariableName = itemVar.Identifier, + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); BlockStatement body = new BlockStatement(); foreachStatement.EmbeddedStatement = body; ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement); diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index 608a45c64..9c030862a 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler void Write(string text); void WriteLine(); void WriteDefinition(string text, object definition); - void WriteReference(string text, object reference); + void WriteReference(string text, object reference, bool isLocal = false); void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false); void MarkFoldEnd(); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index 86a28a5c8..1ddb6a924 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.Decompiler Write(text); } - public void WriteReference(string text, object reference) + public void WriteReference(string text, object reference, bool isLocal) { Write(text); } diff --git a/ILSpy/AvalonEdit/TextMarkerService.cs b/ILSpy/AvalonEdit/TextMarkerService.cs index caffc37ac..fd67cb22e 100644 --- a/ILSpy/AvalonEdit/TextMarkerService.cs +++ b/ILSpy/AvalonEdit/TextMarkerService.cs @@ -124,7 +124,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit int lineStart = line.Offset; int lineEnd = lineStart + line.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length).Reverse()) { - if (!marker.IsVisible(marker.Bookmark)) + if (marker.Bookmark != null && !marker.IsVisible(marker.Bookmark)) continue; Brush foregroundBrush = null; @@ -167,7 +167,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit int viewStart = visualLines.First().FirstDocumentLine.Offset; int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart).Reverse()) { - if (!marker.IsVisible(marker.Bookmark)) + if (marker.Bookmark != null && !marker.IsVisible(marker.Bookmark)) continue; if (marker.BackgroundColor != null) { diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index 7b247ddb9..7aeedfed3 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -36,6 +36,8 @@ namespace ICSharpCode.ILSpy.TextView sealed class ReferenceSegment : TextSegment { public object Reference; + public bool IsLocal; + public bool IsLocalTarget; } /// @@ -197,17 +199,20 @@ namespace ICSharpCode.ILSpy.TextView public void WriteDefinition(string text, object definition) { WriteIndent(); + int start = this.TextLength; b.Append(text); + int end = this.TextLength; this.DefinitionLookup.AddDefinition(definition, this.TextLength); + references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = definition, IsLocal = true, IsLocalTarget = true }); } - public void WriteReference(string text, object reference) + public void WriteReference(string text, object reference, bool isLocal) { WriteIndent(); int start = this.TextLength; b.Append(text); int end = this.TextLength; - references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference }); + references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = isLocal }); } public void MarkFoldStart(string collapsedText, bool defaultCollapsed) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index fa2a9ce8c..cea5e1749 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -29,6 +29,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Threading; using System.Xml; @@ -68,11 +69,13 @@ namespace ICSharpCode.ILSpy.TextView ILSpyTreeNode[] decompiledNodes; DefinitionLookup definitionLookup; + TextSegmentCollection references; CancellationTokenSource currentCancellationTokenSource; internal readonly IconBarManager manager; readonly IconBarMargin iconMargin; readonly TextMarkerService textMarkerService; + readonly List localReferenceMarks = new List(); [ImportMany(typeof(ITextEditorListener))] IEnumerable textEditorListeners; @@ -315,7 +318,8 @@ namespace ICSharpCode.ILSpy.TextView { Debug.WriteLine("Showing {0} characters of output", textOutput.TextLength); Stopwatch w = Stopwatch.StartNew(); - + + ClearLocalReferenceMarks(); textEditor.ScrollToHome(); if (foldingManager != null) { FoldingManager.Uninstall(foldingManager); @@ -324,6 +328,7 @@ namespace ICSharpCode.ILSpy.TextView textEditor.Document = null; // clear old document while we're changing the highlighting uiElementGenerator.UIElements = textOutput.UIElements; referenceElementGenerator.References = textOutput.References; + references = textOutput.References; definitionLookup = textOutput.DefinitionLookup; textEditor.SyntaxHighlighting = highlighting; @@ -609,6 +614,19 @@ namespace ICSharpCode.ILSpy.TextView internal void JumpToReference(ReferenceSegment referenceSegment) { object reference = referenceSegment.Reference; + if (referenceSegment.IsLocal) { + ClearLocalReferenceMarks(); + if (references != null) { + foreach (var r in references) { + if (r.Reference == reference) { + var mark = textMarkerService.Create(r.StartOffset, r.Length); + mark.BackgroundColor = r.IsLocalTarget ? Colors.LightSeaGreen : Colors.GreenYellow; + localReferenceMarks.Add(mark); + } + } + } + return; + } if (definitionLookup != null) { int pos = definitionLookup.GetDefinitionPosition(reference); if (pos >= 0) { @@ -624,6 +642,14 @@ namespace ICSharpCode.ILSpy.TextView } MainWindow.Instance.JumpToReference(reference); } + + void ClearLocalReferenceMarks() + { + foreach (var mark in localReferenceMarks) { + textMarkerService.Remove(mark); + } + localReferenceMarks.Clear(); + } /// /// Filters all ReferenceSegments that are no real links. diff --git a/ILSpy/TextView/ReferenceElementGenerator.cs b/ILSpy/TextView/ReferenceElementGenerator.cs index e83d89f64..330bdaa0c 100644 --- a/ILSpy/TextView/ReferenceElementGenerator.cs +++ b/ILSpy/TextView/ReferenceElementGenerator.cs @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy.TextView protected override void OnQueryCursor(QueryCursorEventArgs e) { e.Handled = true; - e.Cursor = Cursors.Hand; + e.Cursor = referenceSegment.IsLocal ? Cursors.Arrow : Cursors.Hand; } /// @@ -110,7 +110,8 @@ namespace ICSharpCode.ILSpy.TextView { if (e.ChangedButton == MouseButton.Left && !e.Handled) { parent.JumpToReference(referenceSegment); - e.Handled = true; + if(!referenceSegment.IsLocal) + e.Handled = true; } } From 88a0194a6b638020cf1fcc1814ead69b18691c2c Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Wed, 6 Jul 2011 22:41:16 +0200 Subject: [PATCH 02/18] Breakpoint enhancements - Icon for disabled breakpoints - First step to breakpoint context menu in IconBarMargin - Breakpoints are not deleted and recreated any longer when content of decompile window changes --- .../Commands/BreakpointCommand.cs | 23 ++++++++++++++++++ .../AvalonEdit/IconBarMargin.cs | 16 ++++++++---- .../Bookmarks/BreakpointBookmark.cs | 4 +-- ILSpy.SharpDevelop.LGPL/Images.cs | 1 + ILSpy/ILSpy.csproj | 1 + ILSpy/Images/DisabledBreakpoint.png | Bin 0 -> 243 bytes 6 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 ILSpy/Images/DisabledBreakpoint.png diff --git a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs index d45db6388..52a2a2a24 100644 --- a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs +++ b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs @@ -1,9 +1,13 @@ // 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.Linq; using System.Windows; + using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.AvalonEdit; +using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Services; namespace ICSharpCode.ILSpy.Debugger.Commands @@ -47,4 +51,23 @@ namespace ICSharpCode.ILSpy.Debugger.Commands } } } + + [ExportBookmarkContextMenuEntry(Header="Disable Breakpoint", Category="Debugger")] + public class DisableBreakpointCommand : IBookmarkContextMenuEntry + { + public bool IsVisible(IBookmark[] bookmarks) + { + return bookmarks.Any(b => b is BreakpointBookmark && (b as BreakpointBookmark).IsEnabled); + } + + public bool IsEnabled(IBookmark[] bookmarks) + { + return true; + } + + public void Execute(IBookmark[] bookmarks) + { + throw new NotImplementedException(); + } + } } diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs index 907c1a137..b4e68f10a 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs @@ -266,11 +266,17 @@ namespace ICSharpCode.ILSpy.AvalonEdit member.MetadataToken.ToInt32(), breakpoint.ILRange.From, out isMatch); if (map != null) { - newBookmarks.Add(new BreakpointBookmark( - member, new AstLocation(map.SourceCodeLine, 0), - map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language)); - - BookmarkManager.RemoveMark(breakpoint); + // update bookmark + breakpoint.MemberReference = member; + breakpoint.Location = new AstLocation(map.SourceCodeLine, 0); + breakpoint.ILRange = map.ILInstructionOffset; + + // Why were the breakpoints removed and recreated +// newBookmarks.Add(new BreakpointBookmark( +// member, new AstLocation(map.SourceCodeLine, 0), +// map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language)); +// +// BookmarkManager.RemoveMark(breakpoint); } } diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs index 9212a4adb..832c2c5d6 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs @@ -40,7 +40,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks } } - public ILRange ILRange { get; private set; } + public ILRange ILRange { get; set; } public virtual bool IsHealthy { get { @@ -82,7 +82,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks public override ImageSource Image { get { - return Images.Breakpoint; + return IsEnabled ? Images.Breakpoint : Images.DisabledBreakpoint; } } diff --git a/ILSpy.SharpDevelop.LGPL/Images.cs b/ILSpy.SharpDevelop.LGPL/Images.cs index 50915ab48..47cda62b8 100644 --- a/ILSpy.SharpDevelop.LGPL/Images.cs +++ b/ILSpy.SharpDevelop.LGPL/Images.cs @@ -25,6 +25,7 @@ namespace ICSharpCode.ILSpy.SharpDevelop } public static readonly BitmapImage Breakpoint = LoadBitmap("Breakpoint"); + public static readonly BitmapImage DisabledBreakpoint = LoadBitmap("DisabledBreakpoint"); public static readonly BitmapImage CurrentLine = LoadBitmap("CurrentLine"); public static ImageSource GetImage(string imageName) diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index bbfc2b14b..8c67a56fc 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -210,6 +210,7 @@ + diff --git a/ILSpy/Images/DisabledBreakpoint.png b/ILSpy/Images/DisabledBreakpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..773244ee806f9ececeafb226c6e00c35d26c5a9c GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Y$ZW{!9W@a@|Lkr1X3$JT^vIy zZcUx!$k${b;97sYUjmKW$(^@NR-!b%r4WU z_MV?@xQOol>XIyXBv9GzQ6{WE#HJ1O8;prom49Yy)bk_fVHNDsEwcBg{mqBm; zMawh1IvX(QOyBRUq+ZUG%h@>`)P>}<87Iu{W_~Bb$?-Wew8igFOu#(%Be%>gmt>y! mp0#RU{K1eA Date: Sun, 24 Jul 2011 21:53:18 +0200 Subject: [PATCH 03/18] Provide event for changed breakpoint image --- ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs index 832c2c5d6..91e2d5635 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs @@ -63,6 +63,8 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks isEnabled = value; if (IsEnabledChanged != null) IsEnabledChanged(this, EventArgs.Empty); + if (ImageChanged != null) + ImageChanged(this, EventArgs.Empty); // Image property reflects IsEnabled property Redraw(); } } @@ -86,6 +88,8 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks } } + public event EventHandler ImageChanged; + public override ITextMarker CreateMarker(ITextMarkerService markerService, int offset, int length) { ITextMarker marker = markerService.Create(offset, length); From 8a5b5547d4a087f9cdd99d676b3b3ac15347d2da Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Mon, 8 Aug 2011 13:45:46 +0200 Subject: [PATCH 04/18] Added windows for breakpoints and callstack + fix crash when no MemberReference is found for code mapping --- .../Commands/BreakpointCommand.cs | 12 +- Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj | 10 + .../ILSpy.Debugger/UI/BreakpointPanel.xaml | 73 ++++++ .../ILSpy.Debugger/UI/BreakpointPanel.xaml.cs | 99 ++++++++ .../ILSpy.Debugger/UI/CallStackPanel.xaml | 35 +++ .../ILSpy.Debugger/UI/CallStackPanel.xaml.cs | 215 ++++++++++++++++++ .../AvalonEdit/IconBarMargin.cs | 55 ++++- 7 files changed, 484 insertions(+), 15 deletions(-) create mode 100644 Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml create mode 100644 Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs create mode 100644 Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml create mode 100644 Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs diff --git a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs index 52a2a2a24..5d179bb0b 100644 --- a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs +++ b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs @@ -9,6 +9,7 @@ using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Services; +using Mono.Cecil; namespace ICSharpCode.ILSpy.Debugger.Commands { @@ -27,16 +28,21 @@ namespace ICSharpCode.ILSpy.Debugger.Commands // check if the codemappings exists for this line var storage = DebugInformation.CodeMappings; int token = 0; - foreach (var key in storage.Keys) { - var instruction = storage[key].GetInstructionByLineNumber(line, out token); + foreach (var storageEntry in storage) { + var instruction = storageEntry.Value.GetInstructionByLineNumber(line, out token); if (instruction == null) { continue; } // no bookmark on the line: create a new breakpoint + MemberReference memberReference; + if (!DebugInformation.DecompiledMemberReferences.TryGetValue(storageEntry.Key, out memberReference)) { + continue; + } + DebuggerService.ToggleBreakpointAt( - DebugInformation.DecompiledMemberReferences[key], + memberReference, line, instruction.ILInstructionOffset, DebugInformation.Language); diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj index f5feab262..bd7405a7a 100644 --- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj +++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj @@ -109,6 +109,14 @@ AttachToProcessWindow.xaml Code + + BreakpointPanel.xaml + Code + + + CallStackPanel.xaml + Code + DebuggerSettingsPanel.xaml Code @@ -130,6 +138,8 @@ + + diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml new file mode 100644 index 000000000..8ee9f32c6 --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs new file mode 100644 index 000000000..f5419220c --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs @@ -0,0 +1,99 @@ +// 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.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using ICSharpCode.ILSpy; +using ICSharpCode.ILSpy.Bookmarks; + +namespace ILSpyPlugin +{ + /// + /// Interaction logic for BreakpointPanel.xaml + /// + public partial class BreakpointPanel : UserControl, IPane + { + static BreakpointPanel s_instance; + + public static BreakpointPanel Instance + { + get { + if (null == s_instance) + { + App.Current.VerifyAccess(); + s_instance = new BreakpointPanel(); + } + return s_instance; + } + } + + private BreakpointPanel() + { + InitializeComponent(); + view.ItemsSource = BookmarkManager.Bookmarks; + BookmarkManager.Added += new BookmarkEventHandler(OnAdded); + BookmarkManager.Removed += new BookmarkEventHandler(OnRemoved); + } + + public void Show() + { + if (!IsVisible) + MainWindow.Instance.ShowInBottomPane("Breakpoints", this); + } + + private void OnAdded(object sender, BookmarkEventArgs e) + { + view.ItemsSource = null; + view.ItemsSource = BookmarkManager.Bookmarks; + } + private void OnRemoved(object sender, BookmarkEventArgs e) + { + view.ItemsSource = null; + view.ItemsSource = BookmarkManager.Bookmarks; + } + + + public void Closed() + { + } + + void view_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (MouseButton.Left != e.ChangedButton) + return; + var selectedItem = view.SelectedItem as BookmarkBase; + if (null == selectedItem) + return; + // TODO: Line should be part of jump target + MainWindow.Instance.JumpToReference(selectedItem.MemberReference); + MainWindow.Instance.TextView.UnfoldAndScroll(selectedItem.LineNumber); + e.Handled = true; + } + + void view_KeyUp(object sender, KeyEventArgs e) + { + if (e.Key != Key.Delete) + return; + var selectedItem = view.SelectedItem as BookmarkBase; + if (null == selectedItem) + return; + BookmarkManager.RemoveMark(selectedItem); + e.Handled = true; + } + } + + [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Breakpoints", MenuCategory="Others", MenuOrder=8)] + public class BookmarkManagerPanelCommand : SimpleCommand + { + public override void Execute(object parameter) + { + BreakpointPanel.Instance.Show(); + } + } +} \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml new file mode 100644 index 000000000..7e6665431 --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs new file mode 100644 index 000000000..ab5bc25c2 --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs @@ -0,0 +1,215 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +using Debugger; +using ICSharpCode.ILSpy; +using ICSharpCode.ILSpy.Debugger.Models.TreeModel; +using ICSharpCode.ILSpy.Debugger.Services; +using ICSharpCode.ILSpy.XmlDoc; +using Mono.Cecil; + +namespace ILSpyPlugin +{ + /// + /// Interaction logic for CallStackPanel.xaml + /// + public partial class CallStackPanel : UserControl + { + static CallStackPanel s_instance; + IDebugger m_currentDebugger; + Process debuggedProcess; + + public static CallStackPanel Instance + { + get { + if (null == s_instance) + { + App.Current.VerifyAccess(); + s_instance = new CallStackPanel(); + } + return s_instance; + } + } + + private CallStackPanel() + { + InitializeComponent(); + + DebuggerService.DebugStarted += new EventHandler(OnDebugStarted); + DebuggerService.DebugStopped += new EventHandler(OnDebugStopped); + if (DebuggerService.IsDebuggerStarted) + OnDebugStarted(null, EventArgs.Empty); + } + + public void Show() + { + if (!IsVisible) + MainWindow.Instance.ShowInBottomPane("Callstack", this); + } + + void OnDebugStarted(object sender, EventArgs args) + { + m_currentDebugger = DebuggerService.CurrentDebugger; + m_currentDebugger.IsProcessRunningChanged += new EventHandler(OnProcessRunningChanged); + debuggedProcess = ((WindowsDebugger)m_currentDebugger).DebuggedProcess; + OnProcessRunningChanged(null, EventArgs.Empty); + } + + void OnDebugStopped(object sender, EventArgs args) + { + m_currentDebugger.IsProcessRunningChanged -= new EventHandler(OnProcessRunningChanged); + m_currentDebugger = null; + debuggedProcess = null; + } + + void OnProcessRunningChanged(object sender, EventArgs args) + { + if (m_currentDebugger.IsProcessRunning) + return; + RefreshPad(); + } + + void RefreshPad() + { + if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedThread == null) { + view.ItemsSource = null; + return; + } + + List items = null; + StackFrame activeFrame = null; + try { + Utils.DoEvents(debuggedProcess); + items = CreateItems().ToList(); + activeFrame = debuggedProcess.SelectedThread.SelectedStackFrame; + } catch(AbortedBecauseDebuggeeResumedException) { + } catch(System.Exception) { + if (debuggedProcess == null || debuggedProcess.HasExited) { + // Process unexpectedly exited + } else { + throw; + } + } + view.ItemsSource = items; + view.SelectedItem = items != null ? items.FirstOrDefault(item => object.Equals(activeFrame, item.Frame)) : null; + } + + IEnumerable CreateItems() + { + foreach (StackFrame frame in debuggedProcess.SelectedThread.GetCallstack(100)) { + CallStackItem item; + + // show modules names + string moduleName = frame.MethodInfo.DebugModule.ToString(); + + item = new CallStackItem() { + Name = GetFullName(frame), ModuleName = moduleName + }; + item.Frame = frame; + yield return item; + Utils.DoEvents(debuggedProcess); + } + } + + internal static string GetFullName(StackFrame frame) + { + // disabled by default, my be switched if options / context menu is added + bool showArgumentNames = false; + bool showArgumentValues = false; + + StringBuilder name = new StringBuilder(); + name.Append(frame.MethodInfo.DeclaringType.FullName); + name.Append('.'); + name.Append(frame.MethodInfo.Name); + if (showArgumentNames || showArgumentValues) { + name.Append("("); + for (int i = 0; i < frame.ArgumentCount; i++) { + string parameterName = null; + string argValue = null; + if (showArgumentNames) { + try { + parameterName = frame.MethodInfo.GetParameters()[i].Name; + } catch { } + if (parameterName == "") parameterName = null; + } + if (showArgumentValues) { + try { + argValue = frame.GetArgumentValue(i).AsString(100); + } catch { } + } + if (parameterName != null && argValue != null) { + name.Append(parameterName); + name.Append("="); + name.Append(argValue); + } + if (parameterName != null && argValue == null) { + name.Append(parameterName); + } + if (parameterName == null && argValue != null) { + name.Append(argValue); + } + if (parameterName == null && argValue == null) { + name.Append("Global.NA"); + } + if (i < frame.ArgumentCount - 1) { + name.Append(", "); + } + } + name.Append(")"); + } + + return name.ToString(); + } + + + void view_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (MouseButton.Left != e.ChangedButton) + return; + var selectedItem = view.SelectedItem as CallStackItem; + if (null == selectedItem) + return; + var foundAssembly = MainWindow.Instance.CurrentAssemblyList.OpenAssembly(selectedItem.Frame.MethodInfo.DebugModule.FullPath); + if (null == foundAssembly || null == foundAssembly.AssemblyDefinition) + return; + + MemberReference mr = XmlDocKeyProvider.FindMemberByKey(foundAssembly.AssemblyDefinition.MainModule, "M:" + selectedItem.Name); + if (mr == null) + return; + MainWindow.Instance.JumpToReference(mr); + e.Handled = true; + } + } + + public class CallStackItem + { + public string Name { get; set; } + public string Language { get; set; } + public StackFrame Frame { get; set; } + public string Line { get; set; } + public string ModuleName { get; set; } + + public Brush FontColor { + get { return Brushes.Black; } + } + } + + [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Callstack", MenuCategory="Others", MenuOrder=9)] + public class CallstackPanelcommand : SimpleCommand + { + public override void Execute(object parameter) + { + CallStackPanel.Instance.Show(); + } + } +} \ No newline at end of file diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs index b4e68f10a..74426f437 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs @@ -26,8 +26,8 @@ namespace ICSharpCode.ILSpy.AvalonEdit public IconBarMargin(IconBarManager manager) { - BookmarkManager.Added += delegate { InvalidateVisual(); }; - BookmarkManager.Removed += delegate { InvalidateVisual(); }; + BookmarkManager.Added += new BookmarkEventHandler(OnBookmarkAdded); + BookmarkManager.Removed += new BookmarkEventHandler(OnBookmarkRemoved); this.manager = manager; } @@ -242,6 +242,39 @@ namespace ICSharpCode.ILSpy.AvalonEdit } } + public void OnBookmarkAdded(object sender, BookmarkEventArgs args) + { + var breakpoint = args.Bookmark as BreakpointBookmark; + if (null == breakpoint) + return; + var storage = DebugInformation.CodeMappings; + if (storage == null || storage.Count == 0) + return; + var key = breakpoint.MemberReference.MetadataToken.ToInt32(); + if (storage.ContainsKey(key)) + { + // register to show enabled/disabled state + breakpoint.ImageChanged += delegate { InvalidateVisual(); }; + InvalidateVisual(); + } + } + + public void OnBookmarkRemoved(object sender, BookmarkEventArgs args) + { + var breakpoint = args.Bookmark as BreakpointBookmark; + if (null == breakpoint) + return; + var storage = DebugInformation.CodeMappings; + if (storage == null || storage.Count == 0) + return; + var key = breakpoint.MemberReference.MetadataToken.ToInt32(); + if (storage.ContainsKey(key)) + { + breakpoint.ImageChanged -= delegate { InvalidateVisual(); }; + InvalidateVisual(); + } + } + public void SyncBookmarks() { var storage = DebugInformation.CodeMappings; @@ -249,7 +282,6 @@ namespace ICSharpCode.ILSpy.AvalonEdit return; //remove existing bookmarks and create new ones - List newBookmarks = new List(); for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) { var breakpoint = BookmarkManager.Bookmarks[i] as BreakpointBookmark; if (breakpoint == null) @@ -257,7 +289,12 @@ namespace ICSharpCode.ILSpy.AvalonEdit var key = breakpoint.MemberReference.MetadataToken.ToInt32(); if (!storage.ContainsKey(key)) - continue; + { + // in case this was visible before + breakpoint.ImageChanged -= delegate { InvalidateVisual(); }; + continue; + } + breakpoint.ImageChanged += delegate { InvalidateVisual(); }; var member = DebugInformation.DecompiledMemberReferences[key]; @@ -271,17 +308,11 @@ namespace ICSharpCode.ILSpy.AvalonEdit breakpoint.Location = new AstLocation(map.SourceCodeLine, 0); breakpoint.ILRange = map.ILInstructionOffset; - // Why were the breakpoints removed and recreated -// newBookmarks.Add(new BreakpointBookmark( -// member, new AstLocation(map.SourceCodeLine, 0), -// map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language)); -// -// BookmarkManager.RemoveMark(breakpoint); + // Why were the breakpoints removed and recreated? + // This prevents enable/disable of breakpoints } } - newBookmarks.ForEach(m => BookmarkManager.AddMark(m)); - SyncCurrentLineBookmark(); } From 96c72047891222c6a2b675e74c2d1aa02e546593 Mon Sep 17 00:00:00 2001 From: jbevain Date: Mon, 8 Aug 2011 18:12:15 +0200 Subject: [PATCH 05/18] Annotate VariableInitializers with ILVariable when possible --- .../Ast/AstMethodBodyBuilder.cs | 1 + .../Ast/Transforms/DeclareVariables.cs | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 2e2054d6e..297144498 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -128,6 +128,7 @@ namespace ICSharpCode.Decompiler.Ast else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(type, v.Name); + newVarDecl.Variables.Single().AddAnnotation(v); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs index ed2144085..62f98e7fa 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; @@ -35,6 +36,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms { public AstType Type; public string Name; + public ILVariable ILVariable; public AssignmentExpression ReplacedAssignment; public Statement InsertionPoint; @@ -67,9 +69,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (v.ReplacedAssignment != null) { // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, // which might be still in use by the definite assignment graph. + VariableInitializer initializer = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable); VariableDeclarationStatement varDecl = new VariableDeclarationStatement { Type = (AstType)v.Type.Clone(), - Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) } + Variables = { initializer } }; ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; if (es != null) { @@ -100,9 +103,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms daa = new DefiniteAssignmentAnalysis(block, cancellationToken); } foreach (VariableDeclarationStatement varDecl in variables) { - string variableName = varDecl.Variables.Single().Name; - bool allowPassIntoLoops = varDecl.Variables.Single().Annotation() == null; - DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops); + VariableInitializer initializer = varDecl.Variables.Single(); + string variableName = initializer.Name; + ILVariable v = initializer.Annotation(); + bool allowPassIntoLoops = initializer.Annotation() == null; + DeclareVariableInBlock(daa, block, varDecl.Type, variableName, v, allowPassIntoLoops); } } } @@ -111,7 +116,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } - void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops) + void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, ILVariable v, bool allowPassIntoLoops) { // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block Statement declarationPoint = null; @@ -139,10 +144,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (AstNode child in stmt.Children) { BlockStatement subBlock = child as BlockStatement; if (subBlock != null) { - DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops); } else if (HasNestedBlocks(child)) { foreach (BlockStatement nestedSubBlock in child.Children.OfType()) { - DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, v, allowPassIntoLoops); } } } @@ -151,7 +156,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Try converting an assignment expression into a VariableDeclarationStatement if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { // Declare the variable in front of declarationPoint - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = v, InsertionPoint = declarationPoint }); } } } @@ -172,7 +177,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { IdentifierExpression ident = ae.Left as IdentifierExpression; if (ident != null && ident.Identifier == variableName) { - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = ident.Annotation(), ReplacedAssignment = ae }); return true; } } From ced763f6cf5cb4b5689c1edad09b4f5894413b58 Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Tue, 9 Aug 2011 08:22:12 +0200 Subject: [PATCH 06/18] Add separator before "show breakpoints" --- Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs | 2 +- Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs index f5419220c..b4695105b 100644 --- a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs @@ -88,7 +88,7 @@ namespace ILSpyPlugin } } - [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Breakpoints", MenuCategory="Others", MenuOrder=8)] + [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Breakpoints", MenuCategory="View", MenuOrder=8)] public class BookmarkManagerPanelCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs index ab5bc25c2..f421d921f 100644 --- a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs @@ -204,7 +204,7 @@ namespace ILSpyPlugin } } - [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Callstack", MenuCategory="Others", MenuOrder=9)] + [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Callstack", MenuCategory="View", MenuOrder=9)] public class CallstackPanelcommand : SimpleCommand { public override void Execute(object parameter) From 92966bc3a7beaaa64446e29029a09fd811d7cdb2 Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Tue, 9 Aug 2011 14:16:10 +0200 Subject: [PATCH 07/18] When moving between the type and the method the TextMarker for BP was not updated --- .../AvalonEdit/IconBarMargin.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs index 74426f437..a1cc3131c 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs @@ -282,6 +282,9 @@ namespace ICSharpCode.ILSpy.AvalonEdit return; //remove existing bookmarks and create new ones + // update of existing bookmarks for new position does not update TextMarker + // this is only done in TextMarkerService handlers for BookmarkManager.Added/Removed + List newBookmarks = new List(); for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) { var breakpoint = BookmarkManager.Bookmarks[i] as BreakpointBookmark; if (breakpoint == null) @@ -290,11 +293,8 @@ namespace ICSharpCode.ILSpy.AvalonEdit var key = breakpoint.MemberReference.MetadataToken.ToInt32(); if (!storage.ContainsKey(key)) { - // in case this was visible before - breakpoint.ImageChanged -= delegate { InvalidateVisual(); }; continue; } - breakpoint.ImageChanged += delegate { InvalidateVisual(); }; var member = DebugInformation.DecompiledMemberReferences[key]; @@ -303,16 +303,16 @@ namespace ICSharpCode.ILSpy.AvalonEdit member.MetadataToken.ToInt32(), breakpoint.ILRange.From, out isMatch); if (map != null) { - // update bookmark - breakpoint.MemberReference = member; - breakpoint.Location = new AstLocation(map.SourceCodeLine, 0); - breakpoint.ILRange = map.ILInstructionOffset; - - // Why were the breakpoints removed and recreated? - // This prevents enable/disable of breakpoints + BreakpointBookmark newBookmark = new BreakpointBookmark( + member, new AstLocation(map.SourceCodeLine, 0), + map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language); + newBookmark.IsEnabled = breakpoint.IsEnabled; + + newBookmarks.Add(newBookmark); + BookmarkManager.RemoveMark(breakpoint); } } - + newBookmarks.ForEach(m => BookmarkManager.AddMark(m)); SyncCurrentLineBookmark(); } From ebbbb3e41e60426d6c7bdab26d96b153b036c212 Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Wed, 10 Aug 2011 11:51:52 +0200 Subject: [PATCH 08/18] Scroll to current line in debugger stepping --- Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs | 2 ++ ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs | 2 +- ILSpy/TextView/DecompilerTextView.cs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs index 96c59f889..bd1adbbfd 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.ILAst; +using ICSharpCode.ILSpy; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Tooltips; @@ -189,6 +190,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services public static void JumpToCurrentLine(MemberReference memberReference, int startLine, int startColumn, int endLine, int endColumn) { CurrentLineBookmark.SetPosition(memberReference, startLine, startColumn, endLine, endColumn); + MainWindow.Instance.TextView.UnfoldAndScroll(startLine); } #region Tool tips diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs index f7772a642..2fd25a6b7 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs @@ -53,7 +53,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks get { return 100; } } - public CurrentLineBookmark(MemberReference member, AstLocation location) : base(member, location) + private CurrentLineBookmark(MemberReference member, AstLocation location) : base(member, location) { } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 0c16ac2ea..ca8598c81 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -462,7 +462,6 @@ namespace ICSharpCode.ILSpy.TextView DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line); // update marker - CurrentLineBookmark.Remove(); CurrentLineBookmark.SetPosition(member, line, 0, line, 0); var bm = CurrentLineBookmark.Instance; From d518e3c4e0b54ff262491b6ec61cde8f07c9d1bf Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Thu, 11 Aug 2011 16:56:43 +0200 Subject: [PATCH 09/18] Only breakpoints are visible in breakpoints list (except debugger option "Show all bookmarks" is active) --- Debugger/ILSpy.Debugger/DebuggerSettings.cs | 15 +++++++ .../ILSpy.Debugger/UI/BreakpointPanel.xaml.cs | 39 +++++++++++-------- .../ILSpy.Debugger/UI/CallStackPanel.xaml.cs | 2 + .../UI/DebuggerSettingsPanel.xaml | 1 + .../UI/DebuggerSettingsPanel.xaml.cs | 16 ++++---- 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/Debugger/ILSpy.Debugger/DebuggerSettings.cs b/Debugger/ILSpy.Debugger/DebuggerSettings.cs index d723f0998..7072d3901 100644 --- a/Debugger/ILSpy.Debugger/DebuggerSettings.cs +++ b/Debugger/ILSpy.Debugger/DebuggerSettings.cs @@ -11,6 +11,7 @@ namespace ICSharpCode.ILSpy.Debugger bool showWarnings = true; bool askArguments = true; bool debugWholeTypesOnly = false; + bool showAllBookmarks = false; /// /// Show warnings messages. @@ -56,6 +57,20 @@ namespace ICSharpCode.ILSpy.Debugger } } + /// + /// Show all bookmarks in breakpoints window. + /// + [DefaultValue(false)] + public bool ShowAllBookmarks { + get { return showAllBookmarks; } + set { + if (showAllBookmarks != value) { + showAllBookmarks = value; + OnPropertyChanged("ShowAllBookmarks"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs index b4695105b..81579ea93 100644 --- a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs @@ -2,6 +2,8 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; @@ -9,8 +11,11 @@ using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; + using ICSharpCode.ILSpy; using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Bookmarks; +using ICSharpCode.ILSpy.Options; namespace ILSpyPlugin { @@ -36,9 +41,11 @@ namespace ILSpyPlugin private BreakpointPanel() { InitializeComponent(); - view.ItemsSource = BookmarkManager.Bookmarks; - BookmarkManager.Added += new BookmarkEventHandler(OnAdded); - BookmarkManager.Removed += new BookmarkEventHandler(OnRemoved); + SetItemSource(); + BookmarkManager.Added += delegate { SetItemSource(); }; + BookmarkManager.Removed += delegate { SetItemSource(); }; + DebuggerSettingsPanel.CurrentDebuggerSettings.PropertyChanged += + delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; } public void Show() @@ -46,21 +53,22 @@ namespace ILSpyPlugin if (!IsVisible) MainWindow.Instance.ShowInBottomPane("Breakpoints", this); } - - private void OnAdded(object sender, BookmarkEventArgs e) - { - view.ItemsSource = null; - view.ItemsSource = BookmarkManager.Bookmarks; - } - private void OnRemoved(object sender, BookmarkEventArgs e) - { - view.ItemsSource = null; - view.ItemsSource = BookmarkManager.Bookmarks; - } - + private void SetItemSource() + { + view.ItemsSource = null; + if (DebuggerSettingsPanel.CurrentDebuggerSettings.ShowAllBookmarks) + view.ItemsSource = BookmarkManager.Bookmarks; + else + view.ItemsSource = BookmarkManager.Bookmarks.Where(b => b is BreakpointBookmark); + } + public void Closed() { + BookmarkManager.Added -= delegate { SetItemSource(); }; + BookmarkManager.Removed -= delegate { SetItemSource(); }; + DebuggerSettingsPanel.CurrentDebuggerSettings.PropertyChanged -= + delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; } void view_MouseDoubleClick(object sender, MouseButtonEventArgs e) @@ -70,7 +78,6 @@ namespace ILSpyPlugin var selectedItem = view.SelectedItem as BookmarkBase; if (null == selectedItem) return; - // TODO: Line should be part of jump target MainWindow.Instance.JumpToReference(selectedItem.MemberReference); MainWindow.Instance.TextView.UnfoldAndScroll(selectedItem.LineNumber); e.Handled = true; diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs index f421d921f..d7594d730 100644 --- a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs @@ -187,6 +187,8 @@ namespace ILSpyPlugin if (mr == null) return; MainWindow.Instance.JumpToReference(mr); + // TODO: jump to associated line + // MainWindow.Instance.TextView.UnfoldAndScroll(selectedItem.LineNumber); e.Handled = true; } } diff --git a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml index 37581e90c..92acbd1c9 100644 --- a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml +++ b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml @@ -6,6 +6,7 @@ Show warning messages Ask for arguments and working directory before executing a process + Show all bookmarks in breakpoints window \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs index 0c30e14d6..2f01e5ed9 100644 --- a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs @@ -19,9 +19,10 @@ namespace ICSharpCode.ILSpy.Options [ExportOptionPage(Title = "Debugger", Order = 2)] partial class DebuggerSettingsPanel : UserControl, IOptionPage { - private const string DEBUGGER_SETTINGS = "DebuggerSettings"; - private const string SHOW_WARNINGS = "showWarnings"; - private const string ASK_ARGUMENTS = "askForArguments"; + private static readonly string DEBUGGER_SETTINGS = "DebuggerSettings"; + private static readonly string SHOW_WARNINGS = "showWarnings"; + private static readonly string ASK_ARGUMENTS = "askForArguments"; + private static readonly string SHOW_BOOKMARKS = "showAllBookmarks"; public DebuggerSettingsPanel() { @@ -30,7 +31,7 @@ namespace ICSharpCode.ILSpy.Options public void Load(ILSpySettings settings) { - this.DataContext = LoadDebuggerSettings(settings); + this.DataContext = CurrentDebuggerSettings; } static DebuggerSettings currentDebuggerSettings; @@ -47,7 +48,7 @@ namespace ICSharpCode.ILSpy.Options DebuggerSettings s = new DebuggerSettings(); s.ShowWarnings = (bool?)e.Attribute(SHOW_WARNINGS) ?? s.ShowWarnings; s.AskForArguments = (bool?)e.Attribute(ASK_ARGUMENTS) ?? s.AskForArguments; - + s.ShowAllBookmarks = (bool?)e.Attribute(SHOW_BOOKMARKS) ?? s.ShowAllBookmarks; return s; } @@ -57,14 +58,13 @@ namespace ICSharpCode.ILSpy.Options XElement section = new XElement(DEBUGGER_SETTINGS); section.SetAttributeValue(SHOW_WARNINGS, s.ShowWarnings); section.SetAttributeValue(ASK_ARGUMENTS, s.AskForArguments); - + section.SetAttributeValue(SHOW_BOOKMARKS, s.ShowAllBookmarks); + XElement existingElement = root.Element(DEBUGGER_SETTINGS); if (existingElement != null) existingElement.ReplaceWith(section); else root.Add(section); - - currentDebuggerSettings = null; // invalidate cached settings } } } \ No newline at end of file From 6e3aa7825f6fb74098ade6eb98014785aade282d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 20 Aug 2011 15:28:39 +0200 Subject: [PATCH 10/18] Use gray text color for non-public types and members. Closes #143. --- ILSpy/TreeNodes/EventTreeNode.cs | 8 ++++++++ ILSpy/TreeNodes/FieldTreeNode.cs | 6 ++++++ ILSpy/TreeNodes/ILSpyTreeNode.cs | 13 +++++++++++++ ILSpy/TreeNodes/MethodTreeNode.cs | 6 ++++++ ILSpy/TreeNodes/PropertyTreeNode.cs | 13 +++++++++++++ ILSpy/TreeNodes/TypeTreeNode.cs | 2 +- ILSpy/XmlDoc/XmlDocLoader.cs | 3 +++ SharpTreeView/SharpTreeNode.cs | 5 +++++ SharpTreeView/Themes/Generic.xaml | 4 +++- 9 files changed, 58 insertions(+), 2 deletions(-) diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 465048177..2f585369c 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -108,6 +108,14 @@ namespace ICSharpCode.ILSpy.TreeNodes language.DecompileEvent(ev, output, options); } + + public override bool IsPublicAPI { + get { + MethodDefinition accessor = ev.AddMethod ?? ev.RemoveMethod; + return accessor != null && (accessor.IsPublic || accessor.IsFamilyOrAssembly || accessor.IsFamily); + } + } + MemberReference IMemberTreeNode.Member { get { return ev; } diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index ecd28f28f..90942a7a9 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -112,6 +112,12 @@ namespace ICSharpCode.ILSpy.TreeNodes { language.DecompileField(field, output, options); } + + public override bool IsPublicAPI { + get { + return field.IsPublic || field.IsFamily || field.IsFamilyOrAssembly; + } + } MemberReference IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 8b4835fe5..f43e6fb5e 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -166,5 +166,18 @@ namespace ICSharpCode.ILSpy.TreeNodes ApplyFilterToChild(node); } } + + public virtual bool IsPublicAPI { + get { return true; } + } + + public override System.Windows.Media.Brush Foreground { + get { + if (IsPublicAPI) + return base.Foreground; + else + return System.Windows.SystemColors.GrayTextBrush; + } + } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 6435cbfb7..d16cce924 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -137,6 +137,12 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } + public override bool IsPublicAPI { + get { + return method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly; + } + } + MemberReference IMemberTreeNode.Member { get { return method; } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 2ead5dc71..4872b93e8 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -148,6 +148,19 @@ namespace ICSharpCode.ILSpy.TreeNodes { language.DecompileProperty(property, output, options); } + + public override bool IsPublicAPI { + get { + switch (GetAttributesOfMostAccessibleMethod(property) & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Public: + case MethodAttributes.Family: + case MethodAttributes.FamORAssem: + return true; + default: + return false; + } + } + } MemberReference IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 15bb054d8..cd25c916d 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes get { return HighlightSearchMatch(this.Language.FormatTypeName(type)); } } - public bool IsPublicAPI { + public override bool IsPublicAPI { get { switch (type.Attributes & TypeAttributes.VisibilityMask) { case TypeAttributes.Public: diff --git a/ILSpy/XmlDoc/XmlDocLoader.cs b/ILSpy/XmlDoc/XmlDocLoader.cs index 3cc8384bb..4b91ad84d 100644 --- a/ILSpy/XmlDoc/XmlDocLoader.cs +++ b/ILSpy/XmlDoc/XmlDocLoader.cs @@ -99,6 +99,9 @@ namespace ICSharpCode.ILSpy.XmlDoc static string LookupLocalizedXmlDoc(string fileName) { + if (string.IsNullOrEmpty(fileName)) + return null; + string xmlFileName = Path.ChangeExtension(fileName, ".xml"); string currentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; string localizedXmlDocFile = GetLocalizedName(xmlFileName, currentCulture); diff --git a/SharpTreeView/SharpTreeNode.cs b/SharpTreeView/SharpTreeNode.cs index ae0a0e646..65561ea93 100644 --- a/SharpTreeView/SharpTreeNode.cs +++ b/SharpTreeView/SharpTreeNode.cs @@ -12,6 +12,7 @@ using System.Collections.ObjectModel; using System.Windows.Controls; using System.Collections.Specialized; using System.Windows.Input; +using System.Windows.Media; namespace ICSharpCode.TreeView { @@ -101,6 +102,10 @@ namespace ICSharpCode.TreeView get { return null; } } + public virtual Brush Foreground { + get { return SystemColors.WindowTextBrush; } + } + public virtual object Icon { get { return null; } diff --git a/SharpTreeView/Themes/Generic.xaml b/SharpTreeView/Themes/Generic.xaml index a10e19c75..00b807af0 100644 --- a/SharpTreeView/Themes/Generic.xaml +++ b/SharpTreeView/Themes/Generic.xaml @@ -181,6 +181,7 @@ @@ -210,7 +211,8 @@ --> - From 0cdaf3bec4e58208a70050bdcba42ee83c3dcf62 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 20 Aug 2011 18:38:22 +0200 Subject: [PATCH 11/18] Ensure the value parameter of a property setter is called 'value'. --- .../Ast/AstMethodBodyBuilder.cs | 2336 ++++++++--------- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 4 + 2 files changed, 1172 insertions(+), 1168 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 88ccd41d4..94c67e503 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -1,1174 +1,1174 @@ -// 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.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; - -using ICSharpCode.Decompiler.Ast.Transforms; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.Ast -{ - using Ast = ICSharpCode.NRefactory.CSharp; - using Cecil = Mono.Cecil; - - public class AstMethodBodyBuilder - { - MethodDefinition methodDef; - TypeSystem typeSystem; - DecompilerContext context; - HashSet localVariablesToDefine = new HashSet(); // local variables that are missing a definition - - /// - /// Creates the body for the method definition. - /// - /// Method definition to decompile. - /// Decompilation context. - /// Parameter declarations of the method being decompiled. - /// These are used to update the parameter names when the decompiler generates names for the parameters. - /// Local variables storage that will be filled/updated with the local variables. - /// Block for the method body - public static BlockStatement CreateMethodBody(MethodDefinition methodDef, - DecompilerContext context, - IEnumerable parameters = null, - ConcurrentDictionary> localVariables = null) - { - if (localVariables == null) - localVariables = new ConcurrentDictionary>(); - - MethodDefinition oldCurrentMethod = context.CurrentMethod; - Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); - context.CurrentMethod = methodDef; - try { - AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); - builder.methodDef = methodDef; - builder.context = context; - builder.typeSystem = methodDef.Module.TypeSystem; - if (Debugger.IsAttached) { - return builder.CreateMethodBody(parameters, localVariables); - } else { - try { - return builder.CreateMethodBody(parameters, localVariables); - } catch (OperationCanceledException) { - throw; - } catch (Exception ex) { - throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); - } - } - } finally { - context.CurrentMethod = oldCurrentMethod; - } - } - - public BlockStatement CreateMethodBody(IEnumerable parameters, - ConcurrentDictionary> localVariables) - { - if (methodDef.Body == null) return null; - - if (localVariables == null) - throw new ArgumentException("localVariables must be instantiated"); - - context.CancellationToken.ThrowIfCancellationRequested(); - ILBlock ilMethod = new ILBlock(); - ILAstBuilder astBuilder = new ILAstBuilder(); - ilMethod.Body = astBuilder.Build(methodDef, true, context); - - context.CancellationToken.ThrowIfCancellationRequested(); - ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(context, ilMethod); - context.CancellationToken.ThrowIfCancellationRequested(); - - var allVariables = ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable) - .Where(v => v != null && !v.IsParameter).Distinct(); - Debug.Assert(context.CurrentMethod == methodDef); - NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); - - if (parameters != null) { - foreach (var pair in (from p in parameters - join v in astBuilder.Parameters on p.Annotation() equals v.OriginalParameter - select new { p, v.Name })) - { - pair.p.Name = pair.Name; - } - } - - context.CancellationToken.ThrowIfCancellationRequested(); - Ast.BlockStatement astBlock = TransformBlock(ilMethod); - CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments - - Statement insertionPoint = astBlock.Statements.FirstOrDefault(); - foreach (ILVariable v in localVariablesToDefine) { - AstType type; - if (v.Type.ContainsAnonymousType()) - type = new SimpleType("var"); - else - type = AstBuilder.ConvertType(v.Type); - var newVarDecl = new VariableDeclarationStatement(type, v.Name); - newVarDecl.Variables.Single().AddAnnotation(v); - astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); - } - - // store the variables - used for debugger - int token = methodDef.MetadataToken.ToInt32(); - localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables); - - return astBlock; - } - - Ast.BlockStatement TransformBlock(ILBlock block) - { - Ast.BlockStatement astBlock = new BlockStatement(); - if (block != null) { - foreach(ILNode node in block.GetChildren()) { - astBlock.AddRange(TransformNode(node)); - } - } - return astBlock; - } - - IEnumerable TransformNode(ILNode node) - { - if (node is ILLabel) { - yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name }; - } else if (node is ILExpression) { - List ilRanges = ILRange.OrderAndJoint(node.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); - AstNode codeExpr = TransformExpression((ILExpression)node); - if (codeExpr != null) { - codeExpr = codeExpr.WithAnnotation(ilRanges); - if (codeExpr is Ast.Expression) { - yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; - } else if (codeExpr is Ast.Statement) { - yield return (Ast.Statement)codeExpr; - } else { - throw new Exception(); - } - } - } else if (node is ILWhileLoop) { - ILWhileLoop ilLoop = (ILWhileLoop)node; - WhileStatement whileStmt = new WhileStatement() { - Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true), - EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) - }; - yield return whileStmt; - } else if (node is ILCondition) { - ILCondition conditionalNode = (ILCondition)node; - bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0; - yield return new Ast.IfElseStatement { - Condition = (Expression)TransformExpression(conditionalNode.Condition), - TrueStatement = TransformBlock(conditionalNode.TrueBlock), - FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null - }; - } else if (node is ILSwitch) { - ILSwitch ilSwitch = (ILSwitch)node; - SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; - foreach (var caseBlock in ilSwitch.CaseBlocks) { - SwitchSection section = new SwitchSection(); - if (caseBlock.Values != null) { - section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); - } else { - section.CaseLabels.Add(new CaseLabel()); - } - section.Statements.Add(TransformBlock(caseBlock)); - switchStmt.SwitchSections.Add(section); - } - yield return switchStmt; - } else if (node is ILTryCatchBlock) { - ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); - var tryCatchStmt = new Ast.TryCatchStatement(); - tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); - foreach (var catchClause in tryCatchNode.CatchBlocks) { - if (catchClause.ExceptionVariable == null - && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) - { - tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) }); - } else { - tryCatchStmt.CatchClauses.Add( - new Ast.CatchClause { - Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, - Body = TransformBlock(catchClause) - }.WithAnnotation(catchClause.ExceptionVariable)); - } - } - if (tryCatchNode.FinallyBlock != null) - tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); - if (tryCatchNode.FaultBlock != null) { - CatchClause cc = new CatchClause(); - cc.Body = TransformBlock(tryCatchNode.FaultBlock); - cc.Body.Add(new ThrowStatement()); // rethrow - tryCatchStmt.CatchClauses.Add(cc); - } - yield return tryCatchStmt; - } else if (node is ILFixedStatement) { - ILFixedStatement fixedNode = (ILFixedStatement)node; - FixedStatement fixedStatement = new FixedStatement(); - foreach (ILExpression initializer in fixedNode.Initializers) { - Debug.Assert(initializer.Code == ILCode.Stloc); - ILVariable v = (ILVariable)initializer.Operand; - fixedStatement.Variables.Add( - new VariableInitializer { - Name = v.Name, - Initializer = (Expression)TransformExpression(initializer.Arguments[0]) - }.WithAnnotation(v)); - } - fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type); - fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); - yield return fixedStatement; - } else if (node is ILBlock) { - yield return TransformBlock((ILBlock)node); - } else { - throw new Exception("Unknown node type"); - } - } - - AstNode TransformExpression(ILExpression expr) - { - AstNode node = TransformByteCode(expr); - Expression astExpr = node as Expression; - - // get IL ranges - used in debugger - List ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); - AstNode result; - - if (astExpr != null) - result = Convert(astExpr, expr.InferredType, expr.ExpectedType); - else - result = node; - - if (result != null) - result = result.WithAnnotation(new TypeInformation(expr.InferredType)); - - if (result != null) - return result.WithAnnotation(ilRanges); - - return result; - } - - AstNode TransformByteCode(ILExpression byteCode) - { - object operand = byteCode.Operand; - AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); - - List args = new List(); - foreach(ILExpression arg in byteCode.Arguments) { - args.Add((Ast.Expression)TransformExpression(arg)); - } - Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; - Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; - Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; - - switch (byteCode.Code) { - #region Arithmetic - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - { - BinaryOperatorExpression boe; - if (byteCode.InferredType is PointerType) { - if (byteCode.Arguments[0].ExpectedType is PointerType) { - arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else if (byteCode.Arguments[1].ExpectedType is PointerType) { - arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - } - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - } - boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return boe; - } - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - { - BinaryOperatorExpression boe; - if (byteCode.InferredType is PointerType) { - if (byteCode.Arguments[0].ExpectedType is PointerType) { - arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - } - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - } - boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return boe; - } - case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); - case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); - case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); - case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); - case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); - case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); - case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); - case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); - case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); - case ILCode.PostIncrement: - case ILCode.PostIncrement_Ovf: - case ILCode.PostIncrement_Ovf_Un: - { - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - var uoe = new Ast.UnaryOperatorExpression( - (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1); - uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return uoe; - } - #endregion - #region Arrays - case ILCode.Newarr: { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - if (ct != null) { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - } - if (byteCode.Code == ILCode.InitArray) { - ace.Initializer = new ArrayInitializerExpression(); - ace.Initializer.Elements.AddRange(args); - } else { - ace.Arguments.Add(arg1); - } - return ace; - } - case ILCode.InitArray: { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - var arrayType = (ArrayType) operand; - if (ct != null) - { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Initializer = new ArrayInitializerExpression(); - var first = ace.AdditionalArraySpecifiers.First(); - first.Remove(); - ace.Arguments.AddRange(Enumerable.Repeat(0, first.Dimensions).Select(i => new EmptyExpression())); - } - var newArgs = new List(); - foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse()) - { - int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound; - for (int j = 0; j < args.Count; j += length) - { - var child = new ArrayInitializerExpression(); - child.Elements.AddRange(args.GetRange(j, length)); - newArgs.Add(child); - } - var temp = args; - args = newArgs; - newArgs = temp; - newArgs.Clear(); - } - ace.Initializer.Elements.AddRange(args); - return ace; - } - case ILCode.Ldlen: return arg1.Member("Length"); - case ILCode.Ldelem_I: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - case ILCode.Ldelem_Any: - return arg1.Indexer(arg2); - case ILCode.Ldelema: - return MakeRef(arg1.Indexer(arg2)); - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - case ILCode.Stelem_Any: - return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); - case ILCode.CompoundAssignment: - { - CastExpression cast = arg1 as CastExpression; - BinaryOperatorExpression boe = (BinaryOperatorExpression)(cast != null ? cast.Expression : arg1); - var assignment = new Ast.AssignmentExpression { - Left = boe.Left.Detach(), - Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator), - Right = boe.Right.Detach() - }.CopyAnnotationsFrom(boe); - // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because - // the operator cannot be translated back to the expanded form (as the left-hand expression - // would be evaluated twice, and might have side-effects) - if (cast != null) { - cast.Expression = assignment; - return cast; - } else { - return assignment; - } - } - #endregion - #region Comparison - case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case ILCode.Cgt_Un: { - // can also mean Inequality, when used with object references - TypeReference arg1Type = byteCode.Arguments[0].InferredType; - if (arg1Type != null && !arg1Type.IsValueType) - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - else - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - } - case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - #endregion - #region Logical - case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); - case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); - case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; - case ILCode.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2); - #endregion - #region Branch - case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); - case ILCode.Brtrue: - return new Ast.IfElseStatement() { - Condition = arg1, - TrueStatement = new BlockStatement() { - new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) - } - }; - case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); - case ILCode.LoopContinue: return new Ast.ContinueStatement(); - #endregion - #region Conversions - case ILCode.Conv_I1: - case ILCode.Conv_I2: - case ILCode.Conv_I4: - case ILCode.Conv_I8: - case ILCode.Conv_U1: - case ILCode.Conv_U2: - case ILCode.Conv_U4: - case ILCode.Conv_U8: - case ILCode.Conv_I: - case ILCode.Conv_U: - { - // conversion was handled by Convert() function using the info from type analysis - CastExpression cast = arg1 as CastExpression; - if (cast != null) { - cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); - } - return arg1; - } - case ILCode.Conv_R4: - case ILCode.Conv_R8: - case ILCode.Conv_R_Un: // TODO - return arg1; - case ILCode.Conv_Ovf_I1: - case ILCode.Conv_Ovf_I2: - case ILCode.Conv_Ovf_I4: - case ILCode.Conv_Ovf_I8: - case ILCode.Conv_Ovf_U1: - case ILCode.Conv_Ovf_U2: - case ILCode.Conv_Ovf_U4: - case ILCode.Conv_Ovf_U8: - case ILCode.Conv_Ovf_I1_Un: - case ILCode.Conv_Ovf_I2_Un: - case ILCode.Conv_Ovf_I4_Un: - case ILCode.Conv_Ovf_I8_Un: - case ILCode.Conv_Ovf_U1_Un: - case ILCode.Conv_Ovf_U2_Un: - case ILCode.Conv_Ovf_U4_Un: - case ILCode.Conv_Ovf_U8_Un: - { - // conversion was handled by Convert() function using the info from type analysis - CastExpression cast = arg1 as CastExpression; - if (cast != null) { - cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); - } - return arg1; - } - case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO - case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); - case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); - case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); - case ILCode.Castclass: return arg1.CastTo(operandAsTypeRef); - case ILCode.Unbox_Any: - // unboxing does not require a cast if the argument was an isinst instruction - if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference)) - return arg1; - else - return arg1.CastTo(operandAsTypeRef); - case ILCode.Isinst: - return arg1.CastAs(operandAsTypeRef); - case ILCode.Box: - return arg1; - case ILCode.Unbox: - return MakeRef(arg1.CastTo(operandAsTypeRef)); - #endregion - #region Indirect - case ILCode.Ldind_Ref: - case ILCode.Ldobj: - if (arg1 is DirectionExpression) - return ((DirectionExpression)arg1).Expression.Detach(); - else - return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1); - case ILCode.Stind_Ref: - case ILCode.Stobj: - if (arg1 is DirectionExpression) - return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2); - else - return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2); - #endregion - case ILCode.Arglist: - return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess }; - case ILCode.Break: return InlineAssembly(byteCode, args); - case ILCode.Call: - case ILCode.CallGetter: - case ILCode.CallSetter: - return TransformCall(false, byteCode, args); - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - case ILCode.CallvirtSetter: - return TransformCall(true, byteCode, args); - case ILCode.Ldftn: { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); - } - case ILCode.Ldvirtftn: { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldvirtftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); - } - case ILCode.Calli: return InlineAssembly(byteCode, args); - case ILCode.Ckfinite: return InlineAssembly(byteCode, args); - case ILCode.Constrained: return InlineAssembly(byteCode, args); - case ILCode.Cpblk: return InlineAssembly(byteCode, args); - case ILCode.Cpobj: return InlineAssembly(byteCode, args); - case ILCode.Dup: return arg1; - case ILCode.Endfilter: return InlineAssembly(byteCode, args); - case ILCode.Endfinally: return null; - case ILCode.Initblk: return InlineAssembly(byteCode, args); - case ILCode.Initobj: return InlineAssembly(byteCode, args); - case ILCode.DefaultValue: - return MakeDefaultValue((TypeReference)operand); - case ILCode.Jmp: return InlineAssembly(byteCode, args); - case ILCode.Ldc_I4: - return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); - case ILCode.Ldc_I8: - return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType); - case ILCode.Ldc_R4: - case ILCode.Ldc_R8: - case ILCode.Ldc_Decimal: - return new Ast.PrimitiveExpression(operand); - case ILCode.Ldfld: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); - case ILCode.Ldsfld: - return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand); - case ILCode.Stfld: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); - case ILCode.Stsfld: - return new AssignmentExpression( - AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand), - arg1); - case ILCode.Ldflda: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); - case ILCode.Ldsflda: - return MakeRef( - AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand)); - case ILCode.Ldloc: { - ILVariable v = (ILVariable)operand; - if (!v.IsParameter) - localVariablesToDefine.Add((ILVariable)operand); - Expression expr; - if (v.IsParameter && v.OriginalParameter.Index < 0) - expr = new ThisReferenceExpression(); - else - expr = new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); - return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr; - } - case ILCode.Ldloca: { - ILVariable v = (ILVariable)operand; - if (v.IsParameter && v.OriginalParameter.Index < 0) - return MakeRef(new ThisReferenceExpression()); - if (!v.IsParameter) - localVariablesToDefine.Add((ILVariable)operand); - return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); - } - case ILCode.Ldnull: return new Ast.NullReferenceExpression(); - case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); - case ILCode.Ldtoken: - if (operand is Cecil.TypeReference) { - return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); - } else { - return InlineAssembly(byteCode, args); - } - case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; - case ILCode.Localloc: - { - PointerType ptrType = byteCode.InferredType as PointerType; - TypeReference type; - if (ptrType != null) { - type = ptrType.ElementType; - } else { - type = typeSystem.Byte; - } - return new StackAllocExpression { - Type = AstBuilder.ConvertType(type), - CountExpression = DivideBySize(arg1, type) - }; - } - case ILCode.Mkrefany: - { - DirectionExpression dir = arg1 as DirectionExpression; - if (dir != null) { - return new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, - Arguments = { dir.Expression.Detach() } - }; - } else { - return InlineAssembly(byteCode, args); - } - } - case ILCode.Refanytype: - return new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.RefType, - Arguments = { arg1 } - }.Member("TypeHandle"); - case ILCode.Refanyval: - return MakeRef( - new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.RefValue, - Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) } - }); - case ILCode.Newobj: { - Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; - if (declaringType is ArrayType) { - ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; - if (ct != null && ct.ArraySpecifiers.Count >= 1) { - var ace = new Ast.ArrayCreateExpression(); - ct.ArraySpecifiers.First().Remove(); - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Type = ct; - ace.Arguments.AddRange(args); - return ace; - } - } - var oce = new Ast.ObjectCreateExpression(); - if (declaringType.IsAnonymousType()) { - MethodDefinition ctor = ((MethodReference)operand).Resolve(); - if (methodDef != null) { - oce.Initializer = new ArrayInitializerExpression(); - for (int i = 0; i < args.Count; i++) { - oce.Initializer.Elements.Add( - new NamedArgumentExpression { - Identifier = ctor.Parameters[i].Name, - Expression = args[i] - }); - } - } - return oce; - } - oce.Type = AstBuilder.ConvertType(declaringType); - oce.Arguments.AddRange(args); - return oce.WithAnnotation(operand); - } - case ILCode.No: return InlineAssembly(byteCode, args); - case ILCode.Nop: return null; - case ILCode.Pop: return arg1; - case ILCode.Readonly: return InlineAssembly(byteCode, args); - case ILCode.Ret: - if (methodDef.ReturnType.FullName != "System.Void") { - return new Ast.ReturnStatement { Expression = arg1 }; - } else { - return new Ast.ReturnStatement(); - } - case ILCode.Rethrow: return new Ast.ThrowStatement(); - case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; - case ILCode.Stloc: { - ILVariable locVar = (ILVariable)operand; - if (!locVar.IsParameter) - localVariablesToDefine.Add(locVar); - return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); - } - case ILCode.Switch: return InlineAssembly(byteCode, args); - case ILCode.Tail: return InlineAssembly(byteCode, args); - case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; - case ILCode.Unaligned: return InlineAssembly(byteCode, args); - case ILCode.Volatile: return InlineAssembly(byteCode, args); - case ILCode.YieldBreak: - return new Ast.YieldBreakStatement(); - case ILCode.YieldReturn: - return new Ast.YieldStatement { Expression = arg1 }; - case ILCode.InitObject: - case ILCode.InitCollection: - { - ArrayInitializerExpression initializer = new ArrayInitializerExpression(); - for (int i = 1; i < args.Count; i++) { - Match m = objectInitializerPattern.Match(args[i]); - if (m.Success) { - MemberReferenceExpression mre = m.Get("left").Single(); - initializer.Elements.Add( - new NamedArgumentExpression { - Identifier = mre.MemberName, - Expression = m.Get("right").Single().Detach() - }.CopyAnnotationsFrom(mre)); - } else { - m = collectionInitializerPattern.Match(args[i]); - if (m.Success) { - if (m.Get("arg").Count() == 1) { - initializer.Elements.Add(m.Get("arg").Single().Detach()); - } else { - ArrayInitializerExpression argList = new ArrayInitializerExpression(); - foreach (var expr in m.Get("arg")) { - argList.Elements.Add(expr.Detach()); - } - initializer.Elements.Add(argList); - } - } else { - initializer.Elements.Add(args[i]); - } - } - } - ObjectCreateExpression oce = arg1 as ObjectCreateExpression; - DefaultValueExpression dve = arg1 as DefaultValueExpression; - if (oce != null) { - oce.Initializer = initializer; - return oce; - } else if (dve != null) { - oce = new ObjectCreateExpression(dve.Type.Detach()); - oce.CopyAnnotationsFrom(dve); - oce.Initializer = initializer; - return oce; - } else { - return new AssignmentExpression(arg1, initializer); - } - } - case ILCode.InitializedObject: - return new InitializedObjectExpression(); - case ILCode.AddressOf: - return MakeRef(arg1); - default: - throw new Exception("Unknown OpCode: " + byteCode.Code); - } - } - - static readonly AstNode objectInitializerPattern = new AssignmentExpression( - new MemberReferenceExpression { - Target = new InitializedObjectExpression() - }.WithName("left"), - new AnyNode("right") - ); - - static readonly AstNode collectionInitializerPattern = new InvocationExpression { - Target = new MemberReferenceExpression { - Target = new InitializedObjectExpression(), - MemberName = "Add" - }, - Arguments = { new Repeat(new AnyNode("arg")) } - }; - - sealed class InitializedObjectExpression : IdentifierExpression - { - public InitializedObjectExpression() : base("__initialized_object__") {} - - protected override bool DoMatch(AstNode other, Match match) - { - return other is InitializedObjectExpression; - } - } - - /// - /// Divides expr by the size of 'type'. - /// - Expression DivideBySize(Expression expr, TypeReference type) - { - CastExpression cast = expr as CastExpression; - if (cast != null && cast.Type is PrimitiveType && ((PrimitiveType)cast.Type).Keyword == "int") - expr = cast.Expression.Detach(); - - Expression sizeOfExpression; - switch (TypeAnalysis.GetInformationAmount(type)) { - case 1: - case 8: - sizeOfExpression = new PrimitiveExpression(1); - break; - case 16: - sizeOfExpression = new PrimitiveExpression(2); - break; - case 32: - sizeOfExpression = new PrimitiveExpression(4); - break; - case 64: - sizeOfExpression = new PrimitiveExpression(8); - break; - default: - sizeOfExpression = new SizeOfExpression { Type = AstBuilder.ConvertType(type) }; - break; - } - - BinaryOperatorExpression boe = expr as BinaryOperatorExpression; - if (boe != null && boe.Operator == BinaryOperatorType.Multiply && sizeOfExpression.IsMatch(boe.Right)) - return boe.Left.Detach(); - - if (sizeOfExpression.IsMatch(expr)) - return new PrimitiveExpression(1); - - return new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, sizeOfExpression); - } - - Expression MakeDefaultValue(TypeReference type) - { - TypeDefinition typeDef = type.Resolve(); - if (typeDef != null) { - if (TypeAnalysis.IsIntegerOrEnum(typeDef)) - return AstBuilder.MakePrimitive(0, typeDef); - else if (!typeDef.IsValueType) - return new NullReferenceExpression(); - switch (typeDef.FullName) { - case "System.Nullable`1": - return new NullReferenceExpression(); - case "System.Single": - return new PrimitiveExpression(0f); - case "System.Double": - return new PrimitiveExpression(0.0); - case "System.Decimal": - return new PrimitiveExpression(0m); - } - } - return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) }; - } - - AstNode TransformCall(bool isVirtual, ILExpression byteCode, List args) - { - Cecil.MethodReference cecilMethod = (MethodReference)byteCode.Operand; - Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); - Ast.Expression target; - List methodArgs = new List(args); - if (cecilMethod.HasThis) { - target = methodArgs[0]; - methodArgs.RemoveAt(0); - - // Unpack any DirectionExpression that is used as target for the call - // (calling methods on value types implicitly passes the first argument by reference) - if (target is DirectionExpression) { - target = ((DirectionExpression)target).Expression; - target.Remove(); // detach from DirectionExpression - } - - if (cecilMethodDef != null) { - // convert null.ToLower() to ((string)null).ToLower() - if (target is NullReferenceExpression) - target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); - - if (cecilMethodDef.DeclaringType.IsInterface) { - TypeReference tr = byteCode.Arguments[0].InferredType; - if (tr != null) { - TypeDefinition td = tr.Resolve(); - if (td != null && !td.IsInterface) { - // Calling an interface method on a non-interface object: - // we need to introduce an explicit cast - target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); - } - } - } - } - } else { - target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) }; - } - if (target is ThisReferenceExpression && !isVirtual) { - // a non-virtual call on "this" might be a "base"-call. - if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) { - // If we're not calling a method in the current class; we must be calling one in the base class. - target = new BaseReferenceExpression(); - } - } - - if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) { - // On value types, the constructor can be called. - // This is equivalent to 'target = new ValueType(args);'. - ObjectCreateExpression oce = new ObjectCreateExpression(); - oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType); - AdjustArgumentsForMethodCall(cecilMethod, methodArgs); - oce.Arguments.AddRange(methodArgs); - return new AssignmentExpression(target, oce); - } - - if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { - return target.Indexer(methodArgs); - } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { - return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last()); - } - - // Test whether the method is an accessor: - if (cecilMethodDef != null) { - if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +using ICSharpCode.Decompiler.Ast.Transforms; +using ICSharpCode.Decompiler.ILAst; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.Utils; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.Ast +{ + using Ast = ICSharpCode.NRefactory.CSharp; + using Cecil = Mono.Cecil; + + public class AstMethodBodyBuilder + { + MethodDefinition methodDef; + TypeSystem typeSystem; + DecompilerContext context; + HashSet localVariablesToDefine = new HashSet(); // local variables that are missing a definition + + /// + /// Creates the body for the method definition. + /// + /// Method definition to decompile. + /// Decompilation context. + /// Parameter declarations of the method being decompiled. + /// These are used to update the parameter names when the decompiler generates names for the parameters. + /// Local variables storage that will be filled/updated with the local variables. + /// Block for the method body + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, + DecompilerContext context, + IEnumerable parameters = null, + ConcurrentDictionary> localVariables = null) + { + if (localVariables == null) + localVariables = new ConcurrentDictionary>(); + + MethodDefinition oldCurrentMethod = context.CurrentMethod; + Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); + context.CurrentMethod = methodDef; + try { + AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); + builder.methodDef = methodDef; + builder.context = context; + builder.typeSystem = methodDef.Module.TypeSystem; + if (Debugger.IsAttached) { + return builder.CreateMethodBody(parameters, localVariables); + } else { + try { + return builder.CreateMethodBody(parameters, localVariables); + } catch (OperationCanceledException) { + throw; + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } + } + } finally { + context.CurrentMethod = oldCurrentMethod; + } + } + + public BlockStatement CreateMethodBody(IEnumerable parameters, + ConcurrentDictionary> localVariables) + { + if (methodDef.Body == null) return null; + + if (localVariables == null) + throw new ArgumentException("localVariables must be instantiated"); + + context.CancellationToken.ThrowIfCancellationRequested(); + ILBlock ilMethod = new ILBlock(); + ILAstBuilder astBuilder = new ILAstBuilder(); + ilMethod.Body = astBuilder.Build(methodDef, true, context); + + context.CancellationToken.ThrowIfCancellationRequested(); + ILAstOptimizer bodyGraph = new ILAstOptimizer(); + bodyGraph.Optimize(context, ilMethod); + context.CancellationToken.ThrowIfCancellationRequested(); + + var allVariables = ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable) + .Where(v => v != null && !v.IsParameter).Distinct(); + Debug.Assert(context.CurrentMethod == methodDef); + NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); + + if (parameters != null) { + foreach (var pair in (from p in parameters + join v in astBuilder.Parameters on p.Annotation() equals v.OriginalParameter + select new { p, v.Name })) + { + pair.p.Name = pair.Name; + } + } + + context.CancellationToken.ThrowIfCancellationRequested(); + Ast.BlockStatement astBlock = TransformBlock(ilMethod); + CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments + + Statement insertionPoint = astBlock.Statements.FirstOrDefault(); + foreach (ILVariable v in localVariablesToDefine) { + AstType type; + if (v.Type.ContainsAnonymousType()) + type = new SimpleType("var"); + else + type = AstBuilder.ConvertType(v.Type); + var newVarDecl = new VariableDeclarationStatement(type, v.Name); + newVarDecl.Variables.Single().AddAnnotation(v); + astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); + } + + // store the variables - used for debugger + int token = methodDef.MetadataToken.ToInt32(); + localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables); + + return astBlock; + } + + Ast.BlockStatement TransformBlock(ILBlock block) + { + Ast.BlockStatement astBlock = new BlockStatement(); + if (block != null) { + foreach(ILNode node in block.GetChildren()) { + astBlock.AddRange(TransformNode(node)); + } + } + return astBlock; + } + + IEnumerable TransformNode(ILNode node) + { + if (node is ILLabel) { + yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name }; + } else if (node is ILExpression) { + List ilRanges = ILRange.OrderAndJoint(node.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); + AstNode codeExpr = TransformExpression((ILExpression)node); + if (codeExpr != null) { + codeExpr = codeExpr.WithAnnotation(ilRanges); + if (codeExpr is Ast.Expression) { + yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; + } else if (codeExpr is Ast.Statement) { + yield return (Ast.Statement)codeExpr; + } else { + throw new Exception(); + } + } + } else if (node is ILWhileLoop) { + ILWhileLoop ilLoop = (ILWhileLoop)node; + WhileStatement whileStmt = new WhileStatement() { + Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true), + EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) + }; + yield return whileStmt; + } else if (node is ILCondition) { + ILCondition conditionalNode = (ILCondition)node; + bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0; + yield return new Ast.IfElseStatement { + Condition = (Expression)TransformExpression(conditionalNode.Condition), + TrueStatement = TransformBlock(conditionalNode.TrueBlock), + FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null + }; + } else if (node is ILSwitch) { + ILSwitch ilSwitch = (ILSwitch)node; + SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; + foreach (var caseBlock in ilSwitch.CaseBlocks) { + SwitchSection section = new SwitchSection(); + if (caseBlock.Values != null) { + section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); + } else { + section.CaseLabels.Add(new CaseLabel()); + } + section.Statements.Add(TransformBlock(caseBlock)); + switchStmt.SwitchSections.Add(section); + } + yield return switchStmt; + } else if (node is ILTryCatchBlock) { + ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); + var tryCatchStmt = new Ast.TryCatchStatement(); + tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); + foreach (var catchClause in tryCatchNode.CatchBlocks) { + if (catchClause.ExceptionVariable == null + && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) + { + tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) }); + } else { + tryCatchStmt.CatchClauses.Add( + new Ast.CatchClause { + Type = AstBuilder.ConvertType(catchClause.ExceptionType), + VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, + Body = TransformBlock(catchClause) + }.WithAnnotation(catchClause.ExceptionVariable)); + } + } + if (tryCatchNode.FinallyBlock != null) + tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); + if (tryCatchNode.FaultBlock != null) { + CatchClause cc = new CatchClause(); + cc.Body = TransformBlock(tryCatchNode.FaultBlock); + cc.Body.Add(new ThrowStatement()); // rethrow + tryCatchStmt.CatchClauses.Add(cc); + } + yield return tryCatchStmt; + } else if (node is ILFixedStatement) { + ILFixedStatement fixedNode = (ILFixedStatement)node; + FixedStatement fixedStatement = new FixedStatement(); + foreach (ILExpression initializer in fixedNode.Initializers) { + Debug.Assert(initializer.Code == ILCode.Stloc); + ILVariable v = (ILVariable)initializer.Operand; + fixedStatement.Variables.Add( + new VariableInitializer { + Name = v.Name, + Initializer = (Expression)TransformExpression(initializer.Arguments[0]) + }.WithAnnotation(v)); + } + fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type); + fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); + yield return fixedStatement; + } else if (node is ILBlock) { + yield return TransformBlock((ILBlock)node); + } else { + throw new Exception("Unknown node type"); + } + } + + AstNode TransformExpression(ILExpression expr) + { + AstNode node = TransformByteCode(expr); + Expression astExpr = node as Expression; + + // get IL ranges - used in debugger + List ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); + AstNode result; + + if (astExpr != null) + result = Convert(astExpr, expr.InferredType, expr.ExpectedType); + else + result = node; + + if (result != null) + result = result.WithAnnotation(new TypeInformation(expr.InferredType)); + + if (result != null) + return result.WithAnnotation(ilRanges); + + return result; + } + + AstNode TransformByteCode(ILExpression byteCode) + { + object operand = byteCode.Operand; + AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); + + List args = new List(); + foreach(ILExpression arg in byteCode.Arguments) { + args.Add((Ast.Expression)TransformExpression(arg)); + } + Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; + Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; + Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; + + switch (byteCode.Code) { + #region Arithmetic + case ILCode.Add: + case ILCode.Add_Ovf: + case ILCode.Add_Ovf_Un: + { + BinaryOperatorExpression boe; + if (byteCode.InferredType is PointerType) { + if (byteCode.Arguments[0].ExpectedType is PointerType) { + arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); + } else if (byteCode.Arguments[1].ExpectedType is PointerType) { + arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType); + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + } + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + } + boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); + return boe; + } + case ILCode.Sub: + case ILCode.Sub_Ovf: + case ILCode.Sub_Ovf_Un: + { + BinaryOperatorExpression boe; + if (byteCode.InferredType is PointerType) { + if (byteCode.Arguments[0].ExpectedType is PointerType) { + arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + } + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + } + boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); + return boe; + } + case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); + case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); + case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); + case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); + case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); + case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); + case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); + case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); + case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); + case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); + case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); + case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); + case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); + case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); + case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); + case ILCode.PostIncrement: + case ILCode.PostIncrement_Ovf: + case ILCode.PostIncrement_Ovf_Un: + { + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + var uoe = new Ast.UnaryOperatorExpression( + (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1); + uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); + return uoe; + } + #endregion + #region Arrays + case ILCode.Newarr: { + var ace = new Ast.ArrayCreateExpression(); + ace.Type = operandAsTypeRef; + ComposedType ct = operandAsTypeRef as ComposedType; + if (ct != null) { + // change "new (int[,])[10] to new int[10][,]" + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + } + if (byteCode.Code == ILCode.InitArray) { + ace.Initializer = new ArrayInitializerExpression(); + ace.Initializer.Elements.AddRange(args); + } else { + ace.Arguments.Add(arg1); + } + return ace; + } + case ILCode.InitArray: { + var ace = new Ast.ArrayCreateExpression(); + ace.Type = operandAsTypeRef; + ComposedType ct = operandAsTypeRef as ComposedType; + var arrayType = (ArrayType) operand; + if (ct != null) + { + // change "new (int[,])[10] to new int[10][,]" + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + ace.Initializer = new ArrayInitializerExpression(); + var first = ace.AdditionalArraySpecifiers.First(); + first.Remove(); + ace.Arguments.AddRange(Enumerable.Repeat(0, first.Dimensions).Select(i => new EmptyExpression())); + } + var newArgs = new List(); + foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse()) + { + int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound; + for (int j = 0; j < args.Count; j += length) + { + var child = new ArrayInitializerExpression(); + child.Elements.AddRange(args.GetRange(j, length)); + newArgs.Add(child); + } + var temp = args; + args = newArgs; + newArgs = temp; + newArgs.Clear(); + } + ace.Initializer.Elements.AddRange(args); + return ace; + } + case ILCode.Ldlen: return arg1.Member("Length"); + case ILCode.Ldelem_I: + case ILCode.Ldelem_I1: + case ILCode.Ldelem_I2: + case ILCode.Ldelem_I4: + case ILCode.Ldelem_I8: + case ILCode.Ldelem_U1: + case ILCode.Ldelem_U2: + case ILCode.Ldelem_U4: + case ILCode.Ldelem_R4: + case ILCode.Ldelem_R8: + case ILCode.Ldelem_Ref: + case ILCode.Ldelem_Any: + return arg1.Indexer(arg2); + case ILCode.Ldelema: + return MakeRef(arg1.Indexer(arg2)); + case ILCode.Stelem_I: + case ILCode.Stelem_I1: + case ILCode.Stelem_I2: + case ILCode.Stelem_I4: + case ILCode.Stelem_I8: + case ILCode.Stelem_R4: + case ILCode.Stelem_R8: + case ILCode.Stelem_Ref: + case ILCode.Stelem_Any: + return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); + case ILCode.CompoundAssignment: + { + CastExpression cast = arg1 as CastExpression; + BinaryOperatorExpression boe = (BinaryOperatorExpression)(cast != null ? cast.Expression : arg1); + var assignment = new Ast.AssignmentExpression { + Left = boe.Left.Detach(), + Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator), + Right = boe.Right.Detach() + }.CopyAnnotationsFrom(boe); + // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because + // the operator cannot be translated back to the expanded form (as the left-hand expression + // would be evaluated twice, and might have side-effects) + if (cast != null) { + cast.Expression = assignment; + return cast; + } else { + return assignment; + } + } + #endregion + #region Comparison + case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case ILCode.Cgt_Un: { + // can also mean Inequality, when used with object references + TypeReference arg1Type = byteCode.Arguments[0].InferredType; + if (arg1Type != null && !arg1Type.IsValueType) + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + else + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + } + case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + #endregion + #region Logical + case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); + case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); + case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); + case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; + case ILCode.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2); + #endregion + #region Branch + case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); + case ILCode.Brtrue: + return new Ast.IfElseStatement() { + Condition = arg1, + TrueStatement = new BlockStatement() { + new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) + } + }; + case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); + case ILCode.LoopContinue: return new Ast.ContinueStatement(); + #endregion + #region Conversions + case ILCode.Conv_I1: + case ILCode.Conv_I2: + case ILCode.Conv_I4: + case ILCode.Conv_I8: + case ILCode.Conv_U1: + case ILCode.Conv_U2: + case ILCode.Conv_U4: + case ILCode.Conv_U8: + case ILCode.Conv_I: + case ILCode.Conv_U: + { + // conversion was handled by Convert() function using the info from type analysis + CastExpression cast = arg1 as CastExpression; + if (cast != null) { + cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); + } + return arg1; + } + case ILCode.Conv_R4: + case ILCode.Conv_R8: + case ILCode.Conv_R_Un: // TODO + return arg1; + case ILCode.Conv_Ovf_I1: + case ILCode.Conv_Ovf_I2: + case ILCode.Conv_Ovf_I4: + case ILCode.Conv_Ovf_I8: + case ILCode.Conv_Ovf_U1: + case ILCode.Conv_Ovf_U2: + case ILCode.Conv_Ovf_U4: + case ILCode.Conv_Ovf_U8: + case ILCode.Conv_Ovf_I1_Un: + case ILCode.Conv_Ovf_I2_Un: + case ILCode.Conv_Ovf_I4_Un: + case ILCode.Conv_Ovf_I8_Un: + case ILCode.Conv_Ovf_U1_Un: + case ILCode.Conv_Ovf_U2_Un: + case ILCode.Conv_Ovf_U4_Un: + case ILCode.Conv_Ovf_U8_Un: + { + // conversion was handled by Convert() function using the info from type analysis + CastExpression cast = arg1 as CastExpression; + if (cast != null) { + cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); + } + return arg1; + } + case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO + case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); + case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); + case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); + case ILCode.Castclass: return arg1.CastTo(operandAsTypeRef); + case ILCode.Unbox_Any: + // unboxing does not require a cast if the argument was an isinst instruction + if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference)) + return arg1; + else + return arg1.CastTo(operandAsTypeRef); + case ILCode.Isinst: + return arg1.CastAs(operandAsTypeRef); + case ILCode.Box: + return arg1; + case ILCode.Unbox: + return MakeRef(arg1.CastTo(operandAsTypeRef)); + #endregion + #region Indirect + case ILCode.Ldind_Ref: + case ILCode.Ldobj: + if (arg1 is DirectionExpression) + return ((DirectionExpression)arg1).Expression.Detach(); + else + return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1); + case ILCode.Stind_Ref: + case ILCode.Stobj: + if (arg1 is DirectionExpression) + return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2); + else + return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2); + #endregion + case ILCode.Arglist: + return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess }; + case ILCode.Break: return InlineAssembly(byteCode, args); + case ILCode.Call: + case ILCode.CallGetter: + case ILCode.CallSetter: + return TransformCall(false, byteCode, args); + case ILCode.Callvirt: + case ILCode.CallvirtGetter: + case ILCode.CallvirtSetter: + return TransformCall(true, byteCode, args); + case ILCode.Ldftn: { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); + } + case ILCode.Ldvirtftn: { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldvirtftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); + } + case ILCode.Calli: return InlineAssembly(byteCode, args); + case ILCode.Ckfinite: return InlineAssembly(byteCode, args); + case ILCode.Constrained: return InlineAssembly(byteCode, args); + case ILCode.Cpblk: return InlineAssembly(byteCode, args); + case ILCode.Cpobj: return InlineAssembly(byteCode, args); + case ILCode.Dup: return arg1; + case ILCode.Endfilter: return InlineAssembly(byteCode, args); + case ILCode.Endfinally: return null; + case ILCode.Initblk: return InlineAssembly(byteCode, args); + case ILCode.Initobj: return InlineAssembly(byteCode, args); + case ILCode.DefaultValue: + return MakeDefaultValue((TypeReference)operand); + case ILCode.Jmp: return InlineAssembly(byteCode, args); + case ILCode.Ldc_I4: + return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); + case ILCode.Ldc_I8: + return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType); + case ILCode.Ldc_R4: + case ILCode.Ldc_R8: + case ILCode.Ldc_Decimal: + return new Ast.PrimitiveExpression(operand); + case ILCode.Ldfld: + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); + case ILCode.Ldsfld: + return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) + .Member(((FieldReference)operand).Name).WithAnnotation(operand); + case ILCode.Stfld: + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); + case ILCode.Stsfld: + return new AssignmentExpression( + AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) + .Member(((FieldReference)operand).Name).WithAnnotation(operand), + arg1); + case ILCode.Ldflda: + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); + case ILCode.Ldsflda: + return MakeRef( + AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) + .Member(((FieldReference)operand).Name).WithAnnotation(operand)); + case ILCode.Ldloc: { + ILVariable v = (ILVariable)operand; + if (!v.IsParameter) + localVariablesToDefine.Add((ILVariable)operand); + Expression expr; + if (v.IsParameter && v.OriginalParameter.Index < 0) + expr = new ThisReferenceExpression(); + else + expr = new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); + return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr; + } + case ILCode.Ldloca: { + ILVariable v = (ILVariable)operand; + if (v.IsParameter && v.OriginalParameter.Index < 0) + return MakeRef(new ThisReferenceExpression()); + if (!v.IsParameter) + localVariablesToDefine.Add((ILVariable)operand); + return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); + } + case ILCode.Ldnull: return new Ast.NullReferenceExpression(); + case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); + case ILCode.Ldtoken: + if (operand is Cecil.TypeReference) { + return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); + } else { + return InlineAssembly(byteCode, args); + } + case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; + case ILCode.Localloc: + { + PointerType ptrType = byteCode.InferredType as PointerType; + TypeReference type; + if (ptrType != null) { + type = ptrType.ElementType; + } else { + type = typeSystem.Byte; + } + return new StackAllocExpression { + Type = AstBuilder.ConvertType(type), + CountExpression = DivideBySize(arg1, type) + }; + } + case ILCode.Mkrefany: + { + DirectionExpression dir = arg1 as DirectionExpression; + if (dir != null) { + return new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, + Arguments = { dir.Expression.Detach() } + }; + } else { + return InlineAssembly(byteCode, args); + } + } + case ILCode.Refanytype: + return new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.RefType, + Arguments = { arg1 } + }.Member("TypeHandle"); + case ILCode.Refanyval: + return MakeRef( + new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.RefValue, + Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) } + }); + case ILCode.Newobj: { + Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; + if (declaringType is ArrayType) { + ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; + if (ct != null && ct.ArraySpecifiers.Count >= 1) { + var ace = new Ast.ArrayCreateExpression(); + ct.ArraySpecifiers.First().Remove(); + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + ace.Type = ct; + ace.Arguments.AddRange(args); + return ace; + } + } + var oce = new Ast.ObjectCreateExpression(); + if (declaringType.IsAnonymousType()) { + MethodDefinition ctor = ((MethodReference)operand).Resolve(); + if (methodDef != null) { + oce.Initializer = new ArrayInitializerExpression(); + for (int i = 0; i < args.Count; i++) { + oce.Initializer.Elements.Add( + new NamedArgumentExpression { + Identifier = ctor.Parameters[i].Name, + Expression = args[i] + }); + } + } + return oce; + } + oce.Type = AstBuilder.ConvertType(declaringType); + oce.Arguments.AddRange(args); + return oce.WithAnnotation(operand); + } + case ILCode.No: return InlineAssembly(byteCode, args); + case ILCode.Nop: return null; + case ILCode.Pop: return arg1; + case ILCode.Readonly: return InlineAssembly(byteCode, args); + case ILCode.Ret: + if (methodDef.ReturnType.FullName != "System.Void") { + return new Ast.ReturnStatement { Expression = arg1 }; + } else { + return new Ast.ReturnStatement(); + } + case ILCode.Rethrow: return new Ast.ThrowStatement(); + case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; + case ILCode.Stloc: { + ILVariable locVar = (ILVariable)operand; + if (!locVar.IsParameter) + localVariablesToDefine.Add(locVar); + return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); + } + case ILCode.Switch: return InlineAssembly(byteCode, args); + case ILCode.Tail: return InlineAssembly(byteCode, args); + case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; + case ILCode.Unaligned: return InlineAssembly(byteCode, args); + case ILCode.Volatile: return InlineAssembly(byteCode, args); + case ILCode.YieldBreak: + return new Ast.YieldBreakStatement(); + case ILCode.YieldReturn: + return new Ast.YieldStatement { Expression = arg1 }; + case ILCode.InitObject: + case ILCode.InitCollection: + { + ArrayInitializerExpression initializer = new ArrayInitializerExpression(); + for (int i = 1; i < args.Count; i++) { + Match m = objectInitializerPattern.Match(args[i]); + if (m.Success) { + MemberReferenceExpression mre = m.Get("left").Single(); + initializer.Elements.Add( + new NamedArgumentExpression { + Identifier = mre.MemberName, + Expression = m.Get("right").Single().Detach() + }.CopyAnnotationsFrom(mre)); + } else { + m = collectionInitializerPattern.Match(args[i]); + if (m.Success) { + if (m.Get("arg").Count() == 1) { + initializer.Elements.Add(m.Get("arg").Single().Detach()); + } else { + ArrayInitializerExpression argList = new ArrayInitializerExpression(); + foreach (var expr in m.Get("arg")) { + argList.Elements.Add(expr.Detach()); + } + initializer.Elements.Add(argList); + } + } else { + initializer.Elements.Add(args[i]); + } + } + } + ObjectCreateExpression oce = arg1 as ObjectCreateExpression; + DefaultValueExpression dve = arg1 as DefaultValueExpression; + if (oce != null) { + oce.Initializer = initializer; + return oce; + } else if (dve != null) { + oce = new ObjectCreateExpression(dve.Type.Detach()); + oce.CopyAnnotationsFrom(dve); + oce.Initializer = initializer; + return oce; + } else { + return new AssignmentExpression(arg1, initializer); + } + } + case ILCode.InitializedObject: + return new InitializedObjectExpression(); + case ILCode.AddressOf: + return MakeRef(arg1); + default: + throw new Exception("Unknown OpCode: " + byteCode.Code); + } + } + + static readonly AstNode objectInitializerPattern = new AssignmentExpression( + new MemberReferenceExpression { + Target = new InitializedObjectExpression() + }.WithName("left"), + new AnyNode("right") + ); + + static readonly AstNode collectionInitializerPattern = new InvocationExpression { + Target = new MemberReferenceExpression { + Target = new InitializedObjectExpression(), + MemberName = "Add" + }, + Arguments = { new Repeat(new AnyNode("arg")) } + }; + + sealed class InitializedObjectExpression : IdentifierExpression + { + public InitializedObjectExpression() : base("__initialized_object__") {} + + protected override bool DoMatch(AstNode other, Match match) + { + return other is InitializedObjectExpression; + } + } + + /// + /// Divides expr by the size of 'type'. + /// + Expression DivideBySize(Expression expr, TypeReference type) + { + CastExpression cast = expr as CastExpression; + if (cast != null && cast.Type is PrimitiveType && ((PrimitiveType)cast.Type).Keyword == "int") + expr = cast.Expression.Detach(); + + Expression sizeOfExpression; + switch (TypeAnalysis.GetInformationAmount(type)) { + case 1: + case 8: + sizeOfExpression = new PrimitiveExpression(1); + break; + case 16: + sizeOfExpression = new PrimitiveExpression(2); + break; + case 32: + sizeOfExpression = new PrimitiveExpression(4); + break; + case 64: + sizeOfExpression = new PrimitiveExpression(8); + break; + default: + sizeOfExpression = new SizeOfExpression { Type = AstBuilder.ConvertType(type) }; + break; + } + + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null && boe.Operator == BinaryOperatorType.Multiply && sizeOfExpression.IsMatch(boe.Right)) + return boe.Left.Detach(); + + if (sizeOfExpression.IsMatch(expr)) + return new PrimitiveExpression(1); + + return new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, sizeOfExpression); + } + + Expression MakeDefaultValue(TypeReference type) + { + TypeDefinition typeDef = type.Resolve(); + if (typeDef != null) { + if (TypeAnalysis.IsIntegerOrEnum(typeDef)) + return AstBuilder.MakePrimitive(0, typeDef); + else if (!typeDef.IsValueType) + return new NullReferenceExpression(); + switch (typeDef.FullName) { + case "System.Nullable`1": + return new NullReferenceExpression(); + case "System.Single": + return new PrimitiveExpression(0f); + case "System.Double": + return new PrimitiveExpression(0.0); + case "System.Decimal": + return new PrimitiveExpression(0m); + } + } + return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) }; + } + + AstNode TransformCall(bool isVirtual, ILExpression byteCode, List args) + { + Cecil.MethodReference cecilMethod = (MethodReference)byteCode.Operand; + Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); + Ast.Expression target; + List methodArgs = new List(args); + if (cecilMethod.HasThis) { + target = methodArgs[0]; + methodArgs.RemoveAt(0); + + // Unpack any DirectionExpression that is used as target for the call + // (calling methods on value types implicitly passes the first argument by reference) + if (target is DirectionExpression) { + target = ((DirectionExpression)target).Expression; + target.Remove(); // detach from DirectionExpression + } + + if (cecilMethodDef != null) { + // convert null.ToLower() to ((string)null).ToLower() + if (target is NullReferenceExpression) + target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); + + if (cecilMethodDef.DeclaringType.IsInterface) { + TypeReference tr = byteCode.Arguments[0].InferredType; + if (tr != null) { + TypeDefinition td = tr.Resolve(); + if (td != null && !td.IsInterface) { + // Calling an interface method on a non-interface object: + // we need to introduce an explicit cast + target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); + } + } + } + } + } else { + target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) }; + } + if (target is ThisReferenceExpression && !isVirtual) { + // a non-virtual call on "this" might be a "base"-call. + if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) { + // If we're not calling a method in the current class; we must be calling one in the base class. + target = new BaseReferenceExpression(); + } + } + + if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) { + // On value types, the constructor can be called. + // This is equivalent to 'target = new ValueType(args);'. + ObjectCreateExpression oce = new ObjectCreateExpression(); + oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType); + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + oce.Arguments.AddRange(methodArgs); + return new AssignmentExpression(target, oce); + } + + if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { + return target.Indexer(methodArgs); + } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { + return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last()); + } + + // Test whether the method is an accessor: + if (cecilMethodDef != null) { + if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { + foreach (var prop in cecilMethodDef.DeclaringType.Properties) { if (prop.GetMethod == cecilMethodDef) - return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod); - } - } else if (cecilMethodDef.IsGetter) { // with parameters - PropertyDefinition indexer = GetIndexer(cecilMethodDef); - if (indexer != null) - return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod); - } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { + return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod); + } + } else if (cecilMethodDef.IsGetter) { // with parameters + PropertyDefinition indexer = GetIndexer(cecilMethodDef); + if (indexer != null) + return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod); + } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { + foreach (var prop in cecilMethodDef.DeclaringType.Properties) { if (prop.SetMethod == cecilMethodDef) - return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]); - } - } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) { - PropertyDefinition indexer = GetIndexer(cecilMethodDef); - if (indexer != null) + return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]); + } + } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) { + PropertyDefinition indexer = GetIndexer(cecilMethodDef); + if (indexer != null) return new AssignmentExpression( - target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod), - methodArgs[methodArgs.Count - 1] - ); - } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.AddMethod == cecilMethodDef) { + target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod), + methodArgs[methodArgs.Count - 1] + ); + } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { + foreach (var ev in cecilMethodDef.DeclaringType.Events) { + if (ev.AddMethod == cecilMethodDef) { return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), - Operator = AssignmentOperatorType.Add, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.RemoveMethod == cecilMethodDef) { + Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), + Operator = AssignmentOperatorType.Add, + Right = methodArgs[0] + }; + } + } + } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { + foreach (var ev in cecilMethodDef.DeclaringType.Events) { + if (ev.RemoveMethod == cecilMethodDef) { return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), - Operator = AssignmentOperatorType.Subtract, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { + Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), + Operator = AssignmentOperatorType.Subtract, + Right = methodArgs[0] + }; + } + } + } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { AdjustArgumentsForMethodCall(cecilMethod, methodArgs); - return target.Invoke(methodArgs).WithAnnotation(cecilMethod); - } - } - // Default invocation - AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs); - return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); - } - - static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List methodArgs) - { - // Convert 'ref' into 'out' where necessary - for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) { - DirectionExpression dir = methodArgs[i] as DirectionExpression; - ParameterDefinition p = cecilMethod.Parameters[i]; - if (dir != null && p.IsOut && !p.IsIn) - dir.FieldDirection = FieldDirection.Out; - } - } - - static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef) - { - TypeDefinition typeDef = cecilMethodDef.DeclaringType; - string indexerName = null; - foreach (CustomAttribute ca in typeDef.CustomAttributes) { - if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { - indexerName = ca.ConstructorArguments.Single().Value as string; - break; - } - } - if (indexerName == null) - return null; - foreach (PropertyDefinition prop in typeDef.Properties) { - if (prop.Name == indexerName) { - if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef) - return prop; - } - } - return null; - } - - #if DEBUG - static readonly ConcurrentDictionary unhandledOpcodes = new ConcurrentDictionary(); - #endif - - [Conditional("DEBUG")] - public static void ClearUnhandledOpcodes() - { - #if DEBUG - unhandledOpcodes.Clear(); - #endif - } - - [Conditional("DEBUG")] - public static void PrintNumberOfUnhandledOpcodes() - { - #if DEBUG - foreach (var pair in unhandledOpcodes) { - Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value); - } - #endif - } - - static Expression InlineAssembly(ILExpression byteCode, List args) - { - #if DEBUG - unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1); - #endif - // Output the operand of the unknown IL code as well - if (byteCode.Operand != null) { - args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); - } - return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); - } - - static string FormatByteCodeOperand(object operand) - { - if (operand == null) { - return string.Empty; - //} else if (operand is ILExpression) { - // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); - } else if (operand is MethodReference) { - return ((MethodReference)operand).Name + "()"; - } else if (operand is Cecil.TypeReference) { - return ((Cecil.TypeReference)operand).FullName; - } else if (operand is VariableDefinition) { - return ((VariableDefinition)operand).Name; - } else if (operand is ParameterDefinition) { - return ((ParameterDefinition)operand).Name; - } else if (operand is FieldReference) { - return ((FieldReference)operand).Name; - } else if (operand is string) { - return "\"" + operand + "\""; - } else if (operand is int) { - return operand.ToString(); - } else { - return operand.ToString(); - } - } - - static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) - { - GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; - if (g == null) - return null; - if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType())) - return null; - return g.GenericArguments.Select(t => AstBuilder.ConvertType(t)); - } - - static Ast.DirectionExpression MakeRef(Ast.Expression expr) - { - return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; - } - - Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType) - { - if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) { - return expr; - } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) { - return Convert( - new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()), - new PointerType(((ByReferenceType)actualType).ElementType), - reqType); - } else if (actualType is PointerType && reqType is ByReferenceType) { - expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType)); - return new DirectionExpression { - FieldDirection = FieldDirection.Ref, - Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr) - }; - } else if (actualType is PointerType && reqType is PointerType) { - if (actualType.FullName != reqType.FullName) - return expr.CastTo(AstBuilder.ConvertType(reqType)); - else - return expr; - } else { - bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType); - bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType); - - if (TypeAnalysis.IsBoolean(reqType)) { - if (TypeAnalysis.IsBoolean(actualType)) - return expr; - if (actualIsIntegerOrEnum) { - return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType)); - } else { - return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression()); - } - } - if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) { - return new ConditionalExpression { - Condition = expr, - TrueExpression = AstBuilder.MakePrimitive(1, reqType), - FalseExpression = AstBuilder.MakePrimitive(0, reqType) - }; - } - - if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType)) - { - return expr.CastTo(AstBuilder.ConvertType(actualType)); - } - - bool actualIsPrimitiveType = actualIsIntegerOrEnum - || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double; - bool requiredIsPrimitiveType = requiredIsIntegerOrEnum - || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double; - if (actualIsPrimitiveType && requiredIsPrimitiveType) { - return expr.CastTo(AstBuilder.ConvertType(reqType)); - } - return expr; - } - } - } -} + return target.Invoke(methodArgs).WithAnnotation(cecilMethod); + } + } + // Default invocation + AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs); + return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); + } + + static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List methodArgs) + { + // Convert 'ref' into 'out' where necessary + for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) { + DirectionExpression dir = methodArgs[i] as DirectionExpression; + ParameterDefinition p = cecilMethod.Parameters[i]; + if (dir != null && p.IsOut && !p.IsIn) + dir.FieldDirection = FieldDirection.Out; + } + } + + static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef) + { + TypeDefinition typeDef = cecilMethodDef.DeclaringType; + string indexerName = null; + foreach (CustomAttribute ca in typeDef.CustomAttributes) { + if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { + indexerName = ca.ConstructorArguments.Single().Value as string; + break; + } + } + if (indexerName == null) + return null; + foreach (PropertyDefinition prop in typeDef.Properties) { + if (prop.Name == indexerName) { + if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef) + return prop; + } + } + return null; + } + + #if DEBUG + static readonly ConcurrentDictionary unhandledOpcodes = new ConcurrentDictionary(); + #endif + + [Conditional("DEBUG")] + public static void ClearUnhandledOpcodes() + { + #if DEBUG + unhandledOpcodes.Clear(); + #endif + } + + [Conditional("DEBUG")] + public static void PrintNumberOfUnhandledOpcodes() + { + #if DEBUG + foreach (var pair in unhandledOpcodes) { + Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value); + } + #endif + } + + static Expression InlineAssembly(ILExpression byteCode, List args) + { + #if DEBUG + unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1); + #endif + // Output the operand of the unknown IL code as well + if (byteCode.Operand != null) { + args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); + } + return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); + } + + static string FormatByteCodeOperand(object operand) + { + if (operand == null) { + return string.Empty; + //} else if (operand is ILExpression) { + // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); + } else if (operand is MethodReference) { + return ((MethodReference)operand).Name + "()"; + } else if (operand is Cecil.TypeReference) { + return ((Cecil.TypeReference)operand).FullName; + } else if (operand is VariableDefinition) { + return ((VariableDefinition)operand).Name; + } else if (operand is ParameterDefinition) { + return ((ParameterDefinition)operand).Name; + } else if (operand is FieldReference) { + return ((FieldReference)operand).Name; + } else if (operand is string) { + return "\"" + operand + "\""; + } else if (operand is int) { + return operand.ToString(); + } else { + return operand.ToString(); + } + } + + static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) + { + GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; + if (g == null) + return null; + if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType())) + return null; + return g.GenericArguments.Select(t => AstBuilder.ConvertType(t)); + } + + static Ast.DirectionExpression MakeRef(Ast.Expression expr) + { + return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; + } + + Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType) + { + if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) { + return expr; + } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) { + return Convert( + new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()), + new PointerType(((ByReferenceType)actualType).ElementType), + reqType); + } else if (actualType is PointerType && reqType is ByReferenceType) { + expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType)); + return new DirectionExpression { + FieldDirection = FieldDirection.Ref, + Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr) + }; + } else if (actualType is PointerType && reqType is PointerType) { + if (actualType.FullName != reqType.FullName) + return expr.CastTo(AstBuilder.ConvertType(reqType)); + else + return expr; + } else { + bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType); + bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType); + + if (TypeAnalysis.IsBoolean(reqType)) { + if (TypeAnalysis.IsBoolean(actualType)) + return expr; + if (actualIsIntegerOrEnum) { + return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType)); + } else { + return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression()); + } + } + if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) { + return new ConditionalExpression { + Condition = expr, + TrueExpression = AstBuilder.MakePrimitive(1, reqType), + FalseExpression = AstBuilder.MakePrimitive(0, reqType) + }; + } + + if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType)) + { + return expr.CastTo(AstBuilder.ConvertType(actualType)); + } + + bool actualIsPrimitiveType = actualIsIntegerOrEnum + || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double; + bool requiredIsPrimitiveType = requiredIsIntegerOrEnum + || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double; + if (actualIsPrimitiveType && requiredIsPrimitiveType) { + return expr.CastTo(AstBuilder.ConvertType(reqType)); + } + return expr; + } + } + } +} diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 9cc487cea..065ba220b 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -632,6 +632,10 @@ namespace ICSharpCode.Decompiler.ILAst foreach (ParameterDefinition p in methodDef.Parameters) { this.Parameters.Add(new ILVariable { Type = p.ParameterType, Name = p.Name, OriginalParameter = p }); } + if (this.Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { + // last parameter must be 'value', so rename it + this.Parameters.Last().Name = "value"; + } foreach (ByteCode byteCode in body) { ParameterDefinition p; switch (byteCode.Code) { From ac683e20e75339a7e941aedffd061513b461551b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 20 Aug 2011 18:48:06 +0200 Subject: [PATCH 12/18] Yet another try at making proxy authentication work. Closes #46. --- ILSpy/AboutPage.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index 60c576369..91237ddf8 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -183,7 +183,9 @@ namespace ICSharpCode.ILSpy { var tcs = new TaskCompletionSource(); WebClient wc = new WebClient(); - wc.UseDefaultCredentials = true; + IWebProxy systemWebProxy = WebRequest.GetSystemWebProxy(); + systemWebProxy.Credentials = CredentialCache.DefaultCredentials; + wc.Proxy = systemWebProxy; wc.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) { if (e.Error != null) { tcs.SetException(e.Error); From c0fe6d0173f9a6587c24bc2c1a0f7339af0ed3b5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 21 Aug 2011 15:30:34 +0200 Subject: [PATCH 13/18] fix http://community.sharpdevelop.net/forums/t/13658.aspx - type was not found due to type forwarding --- ILSpy.BamlDecompiler/CecilTypeResolver.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ILSpy.BamlDecompiler/CecilTypeResolver.cs b/ILSpy.BamlDecompiler/CecilTypeResolver.cs index 1bc79ab11..8d57fa705 100644 --- a/ILSpy.BamlDecompiler/CecilTypeResolver.cs +++ b/ILSpy.BamlDecompiler/CecilTypeResolver.cs @@ -48,15 +48,38 @@ namespace ILSpy.BamlDecompiler string assemblyName = name.Substring(comma + 1).Trim(); var type = thisAssembly.MainModule.GetType(fullName); + + if (type == null) { + type = TryFindInExportedTypes(fullName, thisAssembly); + } + if (type == null) { var otherAssembly = resolver.Resolve(assemblyName); if (otherAssembly == null) throw new Exception("could not resolve '" + assemblyName + "'!"); type = otherAssembly.MainModule.GetType(fullName.Replace('+', '/')); + + if (type == null) { + type = TryFindInExportedTypes(fullName, otherAssembly); + } } + if (type == null) + throw new Exception("could not resolve '" + name + "'!"); + return new CecilType(type); } + + TypeDefinition TryFindInExportedTypes(string fullName, AssemblyDefinition asm) + { + foreach (var exportedType in asm.MainModule.ExportedTypes) { + if (exportedType.IsForwarder && exportedType.FullName == fullName) { + return exportedType.Resolve(); + } + } + + return null; + } public IDependencyPropertyDescriptor GetDependencyPropertyDescriptor(string name, IType ownerType, IType targetType) { From 489bd6fb7a92d2697d6a4c07fef218d9adc3a95c Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Sun, 21 Aug 2011 15:40:54 +0200 Subject: [PATCH 14/18] fix setting breakpoints in properties.fix UI issues; fix null reference issues.remove old code mappings (not needed anymore). --- .../Commands/BreakpointCommand.cs | 3 +- .../Commands/DebuggerCommands.cs | 1 + Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj | 5 -- .../Services/Debugger/WindowsDebugger.cs | 11 ++-- .../AvalonEdit/IconBarMargin.cs | 52 +++++++++---------- .../Bookmarks/BreakpointBookmark.cs | 19 +++++-- .../Bookmarks/CurrentLineBookmark.cs | 10 ++-- ILSpy.SharpDevelop.LGPL/DebugInformation.cs | 5 -- .../ILSpy.SharpDevelop.LGPL.csproj | 8 ++- .../Models/ToolTipRequestEventArgs.cs | 2 +- .../Services}/DebuggerService.cs | 19 ++++--- .../Services}/IDebugger.cs | 0 .../Services}/ParserService.cs | 11 +++- ILSpy/App.xaml.cs | 10 +++- ILSpy/TextView/DecompilerTextView.cs | 7 ++- 15 files changed, 96 insertions(+), 67 deletions(-) rename {Debugger/ILSpy.Debugger/ToolTips => ILSpy.SharpDevelop.LGPL}/Models/ToolTipRequestEventArgs.cs (96%) rename {Debugger/ILSpy.Debugger/Services/Debugger => ILSpy.SharpDevelop.LGPL/Services}/DebuggerService.cs (96%) rename {Debugger/ILSpy.Debugger/Services/Debugger => ILSpy.SharpDevelop.LGPL/Services}/IDebugger.cs (100%) rename {Debugger/ILSpy.Debugger/Services/ParserService => ILSpy.SharpDevelop.LGPL/Services}/ParserService.cs (90%) diff --git a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs index d45db6388..fa8a1f634 100644 --- a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs +++ b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs @@ -32,8 +32,9 @@ namespace ICSharpCode.ILSpy.Debugger.Commands // no bookmark on the line: create a new breakpoint DebuggerService.ToggleBreakpointAt( - DebugInformation.DecompiledMemberReferences[key], + instruction.MemberMapping.MemberReference, line, + token, instruction.ILInstructionOffset, DebugInformation.Language); break; diff --git a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs index c3bbff34d..8a09da569 100644 --- a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs +++ b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs @@ -307,6 +307,7 @@ namespace ICSharpCode.ILSpy.Debugger.Commands public override void Execute(object parameter) { if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) { + CurrentLineBookmark.Remove(); CurrentDebugger.Continue(); MainWindow.Instance.SetStatus("Running...", Brushes.Black); } diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj index f5feab262..64059f086 100644 --- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj +++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj @@ -87,8 +87,6 @@ - - @@ -96,13 +94,11 @@ - DebuggerTooltipControl.xaml - @@ -123,7 +119,6 @@ - diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index 8aa104bdd..7bd9b2dc4 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -1,6 +1,7 @@ // 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.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; @@ -27,6 +28,7 @@ using StackFrame = Debugger.StackFrame; namespace ICSharpCode.ILSpy.Debugger.Services { + [Export(typeof(IDebugger))] public class WindowsDebugger : IDebugger { enum StopAttachedProcessDialogResult { @@ -283,7 +285,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services int key = frame.MethodInfo.MetadataToken; // get the mapped instruction from the current line marker or the next one - if (!DebugInformation.CodeMappings.ContainsKey(key)) + if (DebugInformation.CodeMappings == null || !DebugInformation.CodeMappings.ContainsKey(key)) return null; return DebugInformation.CodeMappings[key].GetInstructionByTokenAndOffset(key, frame.IP, out isMatch); @@ -550,7 +552,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services debugger, bookmark.MemberReference.DeclaringType.FullName, bookmark.LineNumber, - bookmark.MemberReference.MetadataToken.ToInt32(), + bookmark.FunctionToken, bookmark.ILRange.From, bookmark.IsEnabled); @@ -796,11 +798,12 @@ namespace ICSharpCode.ILSpy.Debugger.Services int line; MemberReference memberReference; - if (DebugInformation.CodeMappings.ContainsKey(token) && + if (DebugInformation.CodeMappings != null && + DebugInformation.CodeMappings.ContainsKey(token) && DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out memberReference, out line)) { DebugInformation.DebugStepInformation = null; // we do not need to step into/out DebuggerService.RemoveCurrentLineMarker(); - DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0); + DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0, ilOffset); } else { StepIntoUnknownFrame(frame); diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs index 907c1a137..95ad7b7bf 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs @@ -15,6 +15,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger; using ICSharpCode.ILSpy.Debugger.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -70,10 +71,15 @@ namespace ICSharpCode.ILSpy.AvalonEdit // create a dictionary line number => first bookmark Dictionary bookmarkDict = new Dictionary(); foreach (var bm in BookmarkManager.Bookmarks) { - if (DebugInformation.DecompiledMemberReferences == null || DebugInformation.DecompiledMemberReferences.Count == 0 || - !DebugInformation.DecompiledMemberReferences.ContainsKey(bm.MemberReference.MetadataToken.ToInt32())) - continue; - + if (bm is BreakpointBookmark) { + if (DebugInformation.CodeMappings == null || DebugInformation.CodeMappings.Count == 0 || + !DebugInformation.CodeMappings.ContainsKey(((BreakpointBookmark)bm).FunctionToken)) + continue; + } else { + if (DebugInformation.DecompiledMemberReferences == null || DebugInformation.DecompiledMemberReferences.Count == 0 || + !DebugInformation.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)bm).MemberReference.MetadataToken.ToInt32())) + continue; + } int line = bm.LineNumber; IBookmark existingBookmark; if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZOrder > existingBookmark.ZOrder) @@ -248,26 +254,24 @@ namespace ICSharpCode.ILSpy.AvalonEdit if (storage == null || storage.Count == 0) return; - //remove existing bookmarks and create new ones + // TODO: handle other types of bookmarks + // remove existing bookmarks and create new ones List newBookmarks = new List(); for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) { var breakpoint = BookmarkManager.Bookmarks[i] as BreakpointBookmark; if (breakpoint == null) continue; - var key = breakpoint.MemberReference.MetadataToken.ToInt32(); + var key = breakpoint.FunctionToken; if (!storage.ContainsKey(key)) continue; - var member = DebugInformation.DecompiledMemberReferences[key]; - bool isMatch; - SourceCodeMapping map = storage[key].GetInstructionByTokenAndOffset( - member.MetadataToken.ToInt32(), breakpoint.ILRange.From, out isMatch); + SourceCodeMapping map = storage[key].GetInstructionByTokenAndOffset(key, breakpoint.ILRange.From, out isMatch); if (map != null) { newBookmarks.Add(new BreakpointBookmark( - member, new AstLocation(map.SourceCodeLine, 0), + breakpoint.MemberReference, new AstLocation(map.SourceCodeLine, 0), breakpoint.FunctionToken, map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language)); BookmarkManager.RemoveMark(breakpoint); @@ -285,33 +289,25 @@ namespace ICSharpCode.ILSpy.AvalonEdit if (CurrentLineBookmark.Instance == null) return; - var oldMappings = DebugInformation.OldCodeMappings; - var newMappings = DebugInformation.CodeMappings; - - if (oldMappings == null || newMappings == null) + var codeMappings = DebugInformation.CodeMappings; + if (codeMappings == null) return; // 1. Save it's data int line = CurrentLineBookmark.Instance.LineNumber; var markerType = CurrentLineBookmark.Instance.MemberReference; + int token = markerType.MetadataToken.ToInt32(); + int offset = CurrentLineBookmark.Instance.ILOffset; - if (!oldMappings.ContainsKey(markerType.MetadataToken.ToInt32()) || !newMappings.ContainsKey(markerType.MetadataToken.ToInt32())) + if (!codeMappings.ContainsKey(token)) return; - // 2. Remove it - CurrentLineBookmark.Remove(); - - // 3. map the marker line - int token; - var instruction = oldMappings[markerType.MetadataToken.ToInt32()].GetInstructionByLineNumber(line, out token); - if (instruction == null) - return; - + // 2. map the marker line MemberReference memberReference; int newline; - if (newMappings[markerType.MetadataToken.ToInt32()].GetInstructionByTokenAndOffset(token, instruction.ILInstructionOffset.From, out memberReference, out newline)) { - // 4. create breakpoint for new languages - CurrentLineBookmark.SetPosition(memberReference, newline, 0, newline, 0); + if (codeMappings[token].GetInstructionByTokenAndOffset(token, offset, out memberReference, out newline)) { + // 3. create breakpoint for new languages + DebuggerService.JumpToCurrentLine(memberReference, newline, 0, newline, 0, offset); } } } diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs index 9212a4adb..efef263e5 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs @@ -40,6 +40,15 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks } } + /// + /// Gets the function/method where the breakpoint is set. + /// + /// In case of methods, it is the same as the MemberReference metadata token.
+ /// In case of properties and events, it's the GetMethod/SetMethod|AddMethod/RemoveMethod token. + ///
+ ///
+ public int FunctionToken { get; private set; } + public ILRange ILRange { get; private set; } public virtual bool IsHealthy { @@ -72,12 +81,14 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks public string Tooltip { get; private set; } - public BreakpointBookmark(MemberReference member, AstLocation location, ILRange range, BreakpointAction action, DecompiledLanguages language) : base(member, location) + public BreakpointBookmark(MemberReference member, AstLocation location, int functionToken, ILRange range, BreakpointAction action, DecompiledLanguages language) + : base(member, location) { this.action = action; + this.FunctionToken = functionToken; this.ILRange = range; this.Tooltip = string.Format("Language:{0}, Line:{1}, IL range:{2}-{3}", language.ToString(), location.Line, range.From, range.To); - this.Language = language; + this.Language = language; } public override ImageSource Image { @@ -91,8 +102,8 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks ITextMarker marker = markerService.Create(offset, length); marker.BackgroundColor = Color.FromRgb(180, 38, 38); marker.ForegroundColor = Colors.White; - marker.IsVisible = b => b is MarkerBookmark && DebugInformation.DecompiledMemberReferences != null && - DebugInformation.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)b).MemberReference.MetadataToken.ToInt32()); + marker.IsVisible = b => b is BreakpointBookmark && DebugInformation.CodeMappings != null && + DebugInformation.CodeMappings.ContainsKey(((BreakpointBookmark)b).FunctionToken); marker.Bookmark = this; this.Marker = marker; diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs index f7772a642..101fbed0e 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks static int endLine; static int endColumn; - public static void SetPosition(MemberReference memberReference, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn) + public static void SetPosition(MemberReference memberReference, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn, int ilOffset) { Remove(); @@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks endLine = makerEndLine; endColumn = makerEndColumn; - instance = new CurrentLineBookmark(memberReference, new AstLocation(startLine, startColumn)); + instance = new CurrentLineBookmark(memberReference, new AstLocation(startLine, startColumn), ilOffset); BookmarkManager.AddMark(instance); } @@ -53,11 +53,13 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks get { return 100; } } - public CurrentLineBookmark(MemberReference member, AstLocation location) : base(member, location) + public CurrentLineBookmark(MemberReference member, AstLocation location, int ilOffset) : base(member, location) { - + this.ILOffset = ilOffset; } + public int ILOffset { get; private set; } + public override ImageSource Image { get { return Images.CurrentLine; } } diff --git a/ILSpy.SharpDevelop.LGPL/DebugInformation.cs b/ILSpy.SharpDevelop.LGPL/DebugInformation.cs index 8c6aa4390..adeac55b3 100644 --- a/ILSpy.SharpDevelop.LGPL/DebugInformation.cs +++ b/ILSpy.SharpDevelop.LGPL/DebugInformation.cs @@ -47,11 +47,6 @@ namespace ICSharpCode.ILSpy.Debugger ///
public static ConcurrentDictionary> LocalVariables { get; set; } - /// - /// Gets or sets the old code mappings. - /// - public static Dictionary> OldCodeMappings { get; set; } - /// /// Gets or sets the MembeReference that was decompiled (a TypeDefinition, MethodDefinition, etc) /// diff --git a/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj b/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj index 3bc9f8095..ef1586d5d 100644 --- a/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj +++ b/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj @@ -32,7 +32,7 @@ True False TRACE - + AnyCPU @@ -76,11 +76,17 @@ + + + + + + diff --git a/Debugger/ILSpy.Debugger/ToolTips/Models/ToolTipRequestEventArgs.cs b/ILSpy.SharpDevelop.LGPL/Models/ToolTipRequestEventArgs.cs similarity index 96% rename from Debugger/ILSpy.Debugger/ToolTips/Models/ToolTipRequestEventArgs.cs rename to ILSpy.SharpDevelop.LGPL/Models/ToolTipRequestEventArgs.cs index e2559a27a..d6a3a9f0c 100644 --- a/Debugger/ILSpy.Debugger/ToolTips/Models/ToolTipRequestEventArgs.cs +++ b/ILSpy.SharpDevelop.LGPL/Models/ToolTipRequestEventArgs.cs @@ -7,7 +7,7 @@ using ICSharpCode.NRefactory.CSharp; namespace ICSharpCode.ILSpy.Debugger.Tooltips { - internal class ToolTipRequestEventArgs : EventArgs + public class ToolTipRequestEventArgs : EventArgs { /// /// Gets whether the tool tip request was handled. diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs similarity index 96% rename from Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs rename to ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs index 96c59f889..51585bbdd 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs +++ b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs @@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services static IDebugger GetCompatibleDebugger() { - return currentDebugger = new WindowsDebugger(); + return currentDebugger; } /// @@ -38,6 +38,8 @@ namespace ICSharpCode.ILSpy.Debugger.Services get { if (currentDebugger == null) { currentDebugger = GetCompatibleDebugger(); + if (currentDebugger == null) + return null; currentDebugger.DebugStarting += new EventHandler(OnDebugStarting); currentDebugger.DebugStarted += new EventHandler(OnDebugStarted); currentDebugger.DebugStopped += new EventHandler(OnDebugStopped); @@ -165,12 +167,12 @@ namespace ICSharpCode.ILSpy.Debugger.Services } } - public static void ToggleBreakpointAt(MemberReference member, int lineNumber, ILRange range, DecompiledLanguages language) + public static void ToggleBreakpointAt(MemberReference member, int lineNumber, int functionToken, ILRange range, DecompiledLanguages language) { BookmarkManager.ToggleBookmark( member.FullName, lineNumber, b => b.CanToggle && b is BreakpointBookmark, - location => new BreakpointBookmark(member, location, range, BreakpointAction.Break, language)); + location => new BreakpointBookmark(member, location, functionToken, range, BreakpointAction.Break, language)); } /* TODO: reimplement this stuff @@ -186,9 +188,9 @@ namespace ICSharpCode.ILSpy.Debugger.Services CurrentLineBookmark.Remove(); } - public static void JumpToCurrentLine(MemberReference memberReference, int startLine, int startColumn, int endLine, int endColumn) + public static void JumpToCurrentLine(MemberReference memberReference, int startLine, int startColumn, int endLine, int endColumn, int ilOffset) { - CurrentLineBookmark.SetPosition(memberReference, startLine, startColumn, endLine, endColumn); + CurrentLineBookmark.SetPosition(memberReference, startLine, startColumn, endLine, endColumn, ilOffset); } #region Tool tips @@ -198,7 +200,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services /// showing its current value (when in debugging mode) can be returned /// through the ToolTipRequestEventArgs.SetTooltip() method. /// - internal static void HandleToolTipRequest(ToolTipRequestEventArgs e) + public static void HandleToolTipRequest(ToolTipRequestEventArgs e) { if (!e.InDocument) return; @@ -384,5 +386,10 @@ namespace ICSharpCode.ILSpy.Debugger.Services // return text.ToString(); // } #endregion + + public static void SetDebugger(Lazy debugger) + { + currentDebugger = debugger.Value; + } } } diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/IDebugger.cs b/ILSpy.SharpDevelop.LGPL/Services/IDebugger.cs similarity index 100% rename from Debugger/ILSpy.Debugger/Services/Debugger/IDebugger.cs rename to ILSpy.SharpDevelop.LGPL/Services/IDebugger.cs diff --git a/Debugger/ILSpy.Debugger/Services/ParserService/ParserService.cs b/ILSpy.SharpDevelop.LGPL/Services/ParserService.cs similarity index 90% rename from Debugger/ILSpy.Debugger/Services/ParserService/ParserService.cs rename to ILSpy.SharpDevelop.LGPL/Services/ParserService.cs index d31b499a0..8cd603d36 100644 --- a/Debugger/ILSpy.Debugger/Services/ParserService/ParserService.cs +++ b/ILSpy.SharpDevelop.LGPL/Services/ParserService.cs @@ -15,7 +15,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services static ParserService() { - mySet.AddRange((new [] { + mySet.AddRange((new string [] { ".", "{", "}", @@ -41,7 +41,14 @@ namespace ICSharpCode.ILSpy.Debugger.Services @"\t", @"\r", "|" - }).AsReadOnly()); + })); + } + + static void AddRange(this ICollection list, IEnumerable items) + { + foreach (T item in items) + if (!list.Contains(item)) + list.Add(item); } /// diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index 735991119..573a86542 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -17,16 +17,16 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; using System.ComponentModel.Composition.Hosting; using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading; using System.Windows; using System.Windows.Documents; using System.Windows.Navigation; using System.Windows.Threading; + +using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy @@ -73,6 +73,12 @@ namespace ICSharpCode.ILSpy EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler(Window_RequestNavigate)); + + try { + DebuggerService.SetDebugger(compositionContainer.GetExport()); + } catch { + // unable to find a IDebugger + } } string FullyQualifyPath(string argument) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 96283611f..39eb1e59a 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -46,6 +46,7 @@ using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger; using ICSharpCode.ILSpy.Debugger.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.XmlDoc; @@ -452,7 +453,7 @@ namespace ICSharpCode.ILSpy.TextView iconMargin.SyncBookmarks(); if (isDecompilationOk) { - if (DebugInformation.DebugStepInformation != null) { + if (DebugInformation.DebugStepInformation != null && DebuggerService.CurrentDebugger != null) { // repaint bookmarks iconMargin.InvalidateVisual(); @@ -467,8 +468,7 @@ namespace ICSharpCode.ILSpy.TextView DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line); // update marker - CurrentLineBookmark.Remove(); - CurrentLineBookmark.SetPosition(member, line, 0, line, 0); + DebuggerService.JumpToCurrentLine(member, line, 0, line, 0, ilOffset); var bm = CurrentLineBookmark.Instance; DocumentLine docline = textEditor.Document.GetLineByNumber(line); @@ -530,7 +530,6 @@ namespace ICSharpCode.ILSpy.TextView void DecompileNodes(DecompilationContext context, ITextOutput textOutput) { // reset data - DebugInformation.OldCodeMappings = DebugInformation.CodeMappings; DebugInformation.CodeMappings = null; DebugInformation.LocalVariables = null; DebugInformation.DecompiledMemberReferences = null; From ae42a1447696d733910375b53cd4d84662a2dd17 Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Sun, 21 Aug 2011 23:46:59 +0200 Subject: [PATCH 15/18] Show argument names and values in callstack (customizable through context menu) --- .../Commands/DebuggerCommands.cs | 2 +- Debugger/ILSpy.Debugger/DebuggerSettings.cs | 107 +++++++++++++++++- .../Services/Debugger/WindowsDebugger.cs | 3 +- .../ILSpy.Debugger/UI/BreakpointPanel.xaml | 2 +- .../ILSpy.Debugger/UI/BreakpointPanel.xaml.cs | 22 ++-- .../ILSpy.Debugger/UI/CallStackPanel.xaml | 22 +++- .../ILSpy.Debugger/UI/CallStackPanel.xaml.cs | 92 ++++++++++++--- .../UI/DebuggerSettingsPanel.xaml.cs | 38 +------ 8 files changed, 219 insertions(+), 69 deletions(-) diff --git a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs index c3bbff34d..d69c8867e 100644 --- a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs +++ b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs @@ -138,7 +138,7 @@ namespace ICSharpCode.ILSpy.Debugger.Commands foreach (var item in items.First().Items.OfType()) { string header = (string)item.Header; - if (header.StartsWith("Remove")) continue; + if (header.StartsWith("Remove") || header.StartsWith("Show")) continue; if (header.StartsWith("Attach") || header.StartsWith("Debug")) item.IsEnabled = enable; diff --git a/Debugger/ILSpy.Debugger/DebuggerSettings.cs b/Debugger/ILSpy.Debugger/DebuggerSettings.cs index 7072d3901..ab9f7e6d9 100644 --- a/Debugger/ILSpy.Debugger/DebuggerSettings.cs +++ b/Debugger/ILSpy.Debugger/DebuggerSettings.cs @@ -3,16 +3,73 @@ using System; using System.ComponentModel; +using System.Xml.Linq; namespace ICSharpCode.ILSpy.Debugger { public class DebuggerSettings : INotifyPropertyChanged { - bool showWarnings = true; - bool askArguments = true; - bool debugWholeTypesOnly = false; - bool showAllBookmarks = false; + #region members + private static readonly string DEBUGGER_SETTINGS = "DebuggerSettings"; + private static readonly string SHOW_WARNINGS = "showWarnings"; + private static readonly string ASK_ARGUMENTS = "askForArguments"; + private static readonly string SHOW_BOOKMARKS = "showAllBookmarks"; + private static readonly string SHOW_MODULE = "showModuleName"; + private static readonly string SHOW_ARGUMENTS = "showArguments"; + private static readonly string SHOW_ARGUMENTVALUE = "showArgumentValues"; + + private bool showWarnings = true; + private bool askArguments = true; + private bool debugWholeTypesOnly = false; + private bool showAllBookmarks = false; + private bool showModuleName = true; + private bool showArguments = false; + private bool showArgumentValues = false; + private static DebuggerSettings s_instance; + #endregion + + public static DebuggerSettings Instance + { + get { + if (null == s_instance) + s_instance = new DebuggerSettings(); + return s_instance; + } + } + + private DebuggerSettings() + { + } + + public void Load(ILSpySettings settings) + { + XElement e = settings[DEBUGGER_SETTINGS]; + ShowWarnings = (bool?)e.Attribute(SHOW_WARNINGS) ?? ShowWarnings; + AskForArguments = (bool?)e.Attribute(ASK_ARGUMENTS) ?? AskForArguments; + ShowAllBookmarks = (bool?)e.Attribute(SHOW_BOOKMARKS) ?? ShowAllBookmarks; + ShowModuleName = (bool?)e.Attribute(SHOW_MODULE) ?? ShowModuleName; + ShowArguments = (bool?)e.Attribute(SHOW_ARGUMENTS) ?? ShowArguments; + ShowArgumentValues = (bool?)e.Attribute(SHOW_ARGUMENTVALUE) ?? ShowArgumentValues; + } + + public void Save(XElement root) + { + XElement section = new XElement(DEBUGGER_SETTINGS); + section.SetAttributeValue(SHOW_WARNINGS, ShowWarnings); + section.SetAttributeValue(ASK_ARGUMENTS, AskForArguments); + section.SetAttributeValue(SHOW_BOOKMARKS, ShowAllBookmarks); + section.SetAttributeValue(SHOW_MODULE, ShowModuleName); + section.SetAttributeValue(SHOW_ARGUMENTS, ShowArguments); + section.SetAttributeValue(SHOW_ARGUMENTVALUE, ShowArgumentValues); + + XElement existingElement = root.Element(DEBUGGER_SETTINGS); + if (existingElement != null) + existingElement.ReplaceWith(section); + else + root.Add(section); + } + /// /// Show warnings messages. /// Default value is true. @@ -71,6 +128,47 @@ namespace ICSharpCode.ILSpy.Debugger } } + /// + /// Show module name in callstack panel. + /// + [DefaultValue(true)] + public bool ShowModuleName { + get { return showModuleName; } + set { + if (showModuleName != value) { + showModuleName = value; + OnPropertyChanged("ShowModuleName"); + } + } + } + + /// + /// Show module name in callstack panel. + /// + [DefaultValue(false)] + public bool ShowArguments { + get { return showArguments; } + set { + if (showArguments != value) { + showArguments = value; + OnPropertyChanged("ShowArguments"); + } + } + } + /// + /// Show module name in callstack panel. + /// + [DefaultValue(false)] + public bool ShowArgumentValues { + get { return showArgumentValues; } + set { + if (showArgumentValues != value) { + showArgumentValues = value; + OnPropertyChanged("ShowArgumentValues"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) @@ -79,5 +177,6 @@ namespace ICSharpCode.ILSpy.Debugger PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } + } } diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index 8aa104bdd..6d3b37163 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -796,7 +796,8 @@ namespace ICSharpCode.ILSpy.Debugger.Services int line; MemberReference memberReference; - if (DebugInformation.CodeMappings.ContainsKey(token) && + if (null != DebugInformation.CodeMappings && + DebugInformation.CodeMappings.ContainsKey(token) && DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out memberReference, out line)) { DebugInformation.DebugStepInformation = null; // we do not need to step into/out DebuggerService.RemoveCurrentLineMarker(); diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml index 8ee9f32c6..e04647066 100644 --- a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml @@ -1,6 +1,6 @@  + x:Class="ICSharpCode.ILSpy.Debugger.UI.BreakpointPanel" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:bm="clr-namespace:ICSharpCode.ILSpy.Bookmarks"> /// Interaction logic for BreakpointPanel.xaml @@ -41,23 +41,27 @@ namespace ILSpyPlugin private BreakpointPanel() { InitializeComponent(); - SetItemSource(); - BookmarkManager.Added += delegate { SetItemSource(); }; - BookmarkManager.Removed += delegate { SetItemSource(); }; - DebuggerSettingsPanel.CurrentDebuggerSettings.PropertyChanged += - delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; } public void Show() { if (!IsVisible) - MainWindow.Instance.ShowInBottomPane("Breakpoints", this); + { + SetItemSource(); + + MainWindow.Instance.ShowInBottomPane("Breakpoints", this); + + BookmarkManager.Added += delegate { SetItemSource(); }; + BookmarkManager.Removed += delegate { SetItemSource(); }; + DebuggerSettings.Instance.PropertyChanged += + delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; + } } private void SetItemSource() { view.ItemsSource = null; - if (DebuggerSettingsPanel.CurrentDebuggerSettings.ShowAllBookmarks) + if (DebuggerSettings.Instance.ShowAllBookmarks) view.ItemsSource = BookmarkManager.Bookmarks; else view.ItemsSource = BookmarkManager.Bookmarks.Where(b => b is BreakpointBookmark); @@ -67,7 +71,7 @@ namespace ILSpyPlugin { BookmarkManager.Added -= delegate { SetItemSource(); }; BookmarkManager.Removed -= delegate { SetItemSource(); }; - DebuggerSettingsPanel.CurrentDebuggerSettings.PropertyChanged -= + DebuggerSettings.Instance.PropertyChanged -= delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; } diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml index 7e6665431..675b63af8 100644 --- a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml @@ -1,7 +1,6 @@  + x:Class="ICSharpCode.ILSpy.Debugger.UI.CallStackPanel" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:debugger="clr-namespace:ICSharpCode.ILSpy.Debugger"> + + + + + + + \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs index d7594d730..2b743841c 100644 --- a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Windows; @@ -10,6 +11,7 @@ using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using System.Xml.Linq; using Debugger; using ICSharpCode.ILSpy; @@ -17,17 +19,17 @@ using ICSharpCode.ILSpy.Debugger.Models.TreeModel; using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.ILSpy.XmlDoc; using Mono.Cecil; +using Mono.CSharp; -namespace ILSpyPlugin +namespace ICSharpCode.ILSpy.Debugger.UI { /// /// Interaction logic for CallStackPanel.xaml /// - public partial class CallStackPanel : UserControl + public partial class CallStackPanel : UserControl, IPane { static CallStackPanel s_instance; IDebugger m_currentDebugger; - Process debuggedProcess; public static CallStackPanel Instance { @@ -44,24 +46,59 @@ namespace ILSpyPlugin private CallStackPanel() { InitializeComponent(); - - DebuggerService.DebugStarted += new EventHandler(OnDebugStarted); - DebuggerService.DebugStopped += new EventHandler(OnDebugStopped); - if (DebuggerService.IsDebuggerStarted) - OnDebugStarted(null, EventArgs.Empty); } public void Show() { if (!IsVisible) - MainWindow.Instance.ShowInBottomPane("Callstack", this); + { + // load debugger settings (to update context menu) + ILSpySettings settings = ILSpySettings.Load(); + DebuggerSettings.Instance.Load(settings); + DebuggerSettings.Instance.PropertyChanged += new PropertyChangedEventHandler(OnDebuggerSettingChanged); + + SwitchModuleColumn(); + MainWindow.Instance.ShowInBottomPane("Callstack", this); + + DebuggerService.DebugStarted += new EventHandler(OnDebugStarted); + DebuggerService.DebugStopped += new EventHandler(OnDebugStopped); + if (DebuggerService.IsDebuggerStarted) + OnDebugStarted(null, EventArgs.Empty); + } + } + + public void Closed() + { + DebuggerService.DebugStarted -= new EventHandler(OnDebugStarted); + DebuggerService.DebugStopped -= new EventHandler(OnDebugStopped); + if (null != m_currentDebugger) + OnDebugStopped(null, EventArgs.Empty); + + // save settings + DebuggerSettings.Instance.PropertyChanged -= new PropertyChangedEventHandler(OnDebuggerSettingChanged); + ILSpySettings.Update( + delegate (XElement root) { + DebuggerSettings.Instance.Save(root); + }); + } + + void OnDebuggerSettingChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == "ShowModuleName") { + SwitchModuleColumn(); + } + else if (args.PropertyName == "ShowArguments" + || args.PropertyName == "ShowArgumentValues") { + RefreshPad(); + } + } void OnDebugStarted(object sender, EventArgs args) { m_currentDebugger = DebuggerService.CurrentDebugger; m_currentDebugger.IsProcessRunningChanged += new EventHandler(OnProcessRunningChanged); - debuggedProcess = ((WindowsDebugger)m_currentDebugger).DebuggedProcess; + OnProcessRunningChanged(null, EventArgs.Empty); } @@ -69,7 +106,7 @@ namespace ILSpyPlugin { m_currentDebugger.IsProcessRunningChanged -= new EventHandler(OnProcessRunningChanged); m_currentDebugger = null; - debuggedProcess = null; + view.ItemsSource = null; } void OnProcessRunningChanged(object sender, EventArgs args) @@ -79,18 +116,28 @@ namespace ILSpyPlugin RefreshPad(); } + void SwitchModuleColumn() + { + foreach (GridViewColumn c in ((GridView)view.View).Columns) { + if ((string)c.Header == "Module") { + c.Width = DebuggerSettings.Instance.ShowModuleName ? double.NaN : 0d; + } + } + } + void RefreshPad() { + Process debuggedProcess = ((WindowsDebugger)m_currentDebugger).DebuggedProcess; if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedThread == null) { view.ItemsSource = null; return; } - List items = null; + IList items = null; StackFrame activeFrame = null; try { Utils.DoEvents(debuggedProcess); - items = CreateItems().ToList(); + items = CreateItems(debuggedProcess); activeFrame = debuggedProcess.SelectedThread.SelectedStackFrame; } catch(AbortedBecauseDebuggeeResumedException) { } catch(System.Exception) { @@ -104,8 +151,9 @@ namespace ILSpyPlugin view.SelectedItem = items != null ? items.FirstOrDefault(item => object.Equals(activeFrame, item.Frame)) : null; } - IEnumerable CreateItems() + IList CreateItems(Process debuggedProcess) { + List items = new List(); foreach (StackFrame frame in debuggedProcess.SelectedThread.GetCallstack(100)) { CallStackItem item; @@ -116,16 +164,17 @@ namespace ILSpyPlugin Name = GetFullName(frame), ModuleName = moduleName }; item.Frame = frame; - yield return item; + items.Add(item); Utils.DoEvents(debuggedProcess); } + return items; } internal static string GetFullName(StackFrame frame) { // disabled by default, my be switched if options / context menu is added - bool showArgumentNames = false; - bool showArgumentValues = false; + bool showArgumentNames = DebuggerSettings.Instance.ShowArguments; + bool showArgumentValues = DebuggerSettings.Instance.ShowArgumentValues; StringBuilder name = new StringBuilder(); name.Append(frame.MethodInfo.DeclaringType.FullName); @@ -179,6 +228,7 @@ namespace ILSpyPlugin var selectedItem = view.SelectedItem as CallStackItem; if (null == selectedItem) return; + var foundAssembly = MainWindow.Instance.CurrentAssemblyList.OpenAssembly(selectedItem.Frame.MethodInfo.DebugModule.FullPath); if (null == foundAssembly || null == foundAssembly.AssemblyDefinition) return; @@ -191,6 +241,14 @@ namespace ILSpyPlugin // MainWindow.Instance.TextView.UnfoldAndScroll(selectedItem.LineNumber); e.Handled = true; } + + void SwitchIsChecked(object sender, EventArgs args) + { + if (sender is MenuItem) { + var mi = (MenuItem)sender; + mi.IsChecked = !mi.IsChecked; + } + } } public class CallStackItem diff --git a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs index 2f01e5ed9..690a2a5ce 100644 --- a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs @@ -19,11 +19,6 @@ namespace ICSharpCode.ILSpy.Options [ExportOptionPage(Title = "Debugger", Order = 2)] partial class DebuggerSettingsPanel : UserControl, IOptionPage { - private static readonly string DEBUGGER_SETTINGS = "DebuggerSettings"; - private static readonly string SHOW_WARNINGS = "showWarnings"; - private static readonly string ASK_ARGUMENTS = "askForArguments"; - private static readonly string SHOW_BOOKMARKS = "showAllBookmarks"; - public DebuggerSettingsPanel() { InitializeComponent(); @@ -31,40 +26,15 @@ namespace ICSharpCode.ILSpy.Options public void Load(ILSpySettings settings) { - this.DataContext = CurrentDebuggerSettings; - } - - static DebuggerSettings currentDebuggerSettings; - - public static DebuggerSettings CurrentDebuggerSettings { - get { - return currentDebuggerSettings ?? (currentDebuggerSettings = LoadDebuggerSettings(ILSpySettings.Load())); - } - } - - public static DebuggerSettings LoadDebuggerSettings(ILSpySettings settings) - { - XElement e = settings[DEBUGGER_SETTINGS]; - DebuggerSettings s = new DebuggerSettings(); - s.ShowWarnings = (bool?)e.Attribute(SHOW_WARNINGS) ?? s.ShowWarnings; - s.AskForArguments = (bool?)e.Attribute(ASK_ARGUMENTS) ?? s.AskForArguments; - s.ShowAllBookmarks = (bool?)e.Attribute(SHOW_BOOKMARKS) ?? s.ShowAllBookmarks; - return s; + var s = DebuggerSettings.Instance; + s.Load(settings); + this.DataContext = s; } public void Save(XElement root) { var s = (DebuggerSettings)this.DataContext; - XElement section = new XElement(DEBUGGER_SETTINGS); - section.SetAttributeValue(SHOW_WARNINGS, s.ShowWarnings); - section.SetAttributeValue(ASK_ARGUMENTS, s.AskForArguments); - section.SetAttributeValue(SHOW_BOOKMARKS, s.ShowAllBookmarks); - - XElement existingElement = root.Element(DEBUGGER_SETTINGS); - if (existingElement != null) - existingElement.ReplaceWith(section); - else - root.Add(section); + s.Save(root); } } } \ No newline at end of file From d5e5db311e285e3015837411e8ecd67715507460 Mon Sep 17 00:00:00 2001 From: Ronny Klier Date: Mon, 22 Aug 2011 10:27:20 +0200 Subject: [PATCH 16/18] Fix attach to IDebugger events from DebuggerService --- ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs index acd23810a..d1c8b75bc 100644 --- a/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs +++ b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs @@ -390,7 +390,19 @@ namespace ICSharpCode.ILSpy.Debugger.Services public static void SetDebugger(Lazy debugger) { + if (currentDebugger != null) + { + currentDebugger.DebugStarting -= new EventHandler(OnDebugStarting); + currentDebugger.DebugStarted -= new EventHandler(OnDebugStarted); + currentDebugger.DebugStopped -= new EventHandler(OnDebugStopped); + } currentDebugger = debugger.Value; + if (currentDebugger != null) + { + currentDebugger.DebugStarting += new EventHandler(OnDebugStarting); + currentDebugger.DebugStarted += new EventHandler(OnDebugStarted); + currentDebugger.DebugStopped += new EventHandler(OnDebugStopped); + } } } } From a131e944c6f2ee820609efdf85cbf8630ec879ec Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Wed, 24 Aug 2011 11:57:06 +0200 Subject: [PATCH 17/18] Show full name of type in Breakpoint pad. --- Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml index e04647066..7f8f8a032 100644 --- a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml @@ -40,7 +40,7 @@ From eba7aa2ca2ed7ea27f00a2398902636735bf8f46 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Aug 2011 15:54:38 +0200 Subject: [PATCH 18/18] Normalize line endings to LF. --- .../Ast/AstMethodBodyBuilder.cs | 2348 ++++++++--------- 1 file changed, 1174 insertions(+), 1174 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 94c67e503..e90562f69 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -1,1174 +1,1174 @@ -// 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.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; - -using ICSharpCode.Decompiler.Ast.Transforms; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.Ast -{ - using Ast = ICSharpCode.NRefactory.CSharp; - using Cecil = Mono.Cecil; - - public class AstMethodBodyBuilder - { - MethodDefinition methodDef; - TypeSystem typeSystem; - DecompilerContext context; - HashSet localVariablesToDefine = new HashSet(); // local variables that are missing a definition - - /// - /// Creates the body for the method definition. - /// - /// Method definition to decompile. - /// Decompilation context. - /// Parameter declarations of the method being decompiled. - /// These are used to update the parameter names when the decompiler generates names for the parameters. - /// Local variables storage that will be filled/updated with the local variables. - /// Block for the method body - public static BlockStatement CreateMethodBody(MethodDefinition methodDef, - DecompilerContext context, - IEnumerable parameters = null, - ConcurrentDictionary> localVariables = null) - { - if (localVariables == null) - localVariables = new ConcurrentDictionary>(); - - MethodDefinition oldCurrentMethod = context.CurrentMethod; - Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); - context.CurrentMethod = methodDef; - try { - AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); - builder.methodDef = methodDef; - builder.context = context; - builder.typeSystem = methodDef.Module.TypeSystem; - if (Debugger.IsAttached) { - return builder.CreateMethodBody(parameters, localVariables); - } else { - try { - return builder.CreateMethodBody(parameters, localVariables); - } catch (OperationCanceledException) { - throw; - } catch (Exception ex) { - throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); - } - } - } finally { - context.CurrentMethod = oldCurrentMethod; - } - } - - public BlockStatement CreateMethodBody(IEnumerable parameters, - ConcurrentDictionary> localVariables) - { - if (methodDef.Body == null) return null; - - if (localVariables == null) - throw new ArgumentException("localVariables must be instantiated"); - - context.CancellationToken.ThrowIfCancellationRequested(); - ILBlock ilMethod = new ILBlock(); - ILAstBuilder astBuilder = new ILAstBuilder(); - ilMethod.Body = astBuilder.Build(methodDef, true, context); - - context.CancellationToken.ThrowIfCancellationRequested(); - ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(context, ilMethod); - context.CancellationToken.ThrowIfCancellationRequested(); - - var allVariables = ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable) - .Where(v => v != null && !v.IsParameter).Distinct(); - Debug.Assert(context.CurrentMethod == methodDef); - NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); - - if (parameters != null) { - foreach (var pair in (from p in parameters - join v in astBuilder.Parameters on p.Annotation() equals v.OriginalParameter - select new { p, v.Name })) - { - pair.p.Name = pair.Name; - } - } - - context.CancellationToken.ThrowIfCancellationRequested(); - Ast.BlockStatement astBlock = TransformBlock(ilMethod); - CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments - - Statement insertionPoint = astBlock.Statements.FirstOrDefault(); - foreach (ILVariable v in localVariablesToDefine) { - AstType type; - if (v.Type.ContainsAnonymousType()) - type = new SimpleType("var"); - else - type = AstBuilder.ConvertType(v.Type); - var newVarDecl = new VariableDeclarationStatement(type, v.Name); - newVarDecl.Variables.Single().AddAnnotation(v); - astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); - } - - // store the variables - used for debugger - int token = methodDef.MetadataToken.ToInt32(); - localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables); - - return astBlock; - } - - Ast.BlockStatement TransformBlock(ILBlock block) - { - Ast.BlockStatement astBlock = new BlockStatement(); - if (block != null) { - foreach(ILNode node in block.GetChildren()) { - astBlock.AddRange(TransformNode(node)); - } - } - return astBlock; - } - - IEnumerable TransformNode(ILNode node) - { - if (node is ILLabel) { - yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name }; - } else if (node is ILExpression) { - List ilRanges = ILRange.OrderAndJoint(node.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); - AstNode codeExpr = TransformExpression((ILExpression)node); - if (codeExpr != null) { - codeExpr = codeExpr.WithAnnotation(ilRanges); - if (codeExpr is Ast.Expression) { - yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; - } else if (codeExpr is Ast.Statement) { - yield return (Ast.Statement)codeExpr; - } else { - throw new Exception(); - } - } - } else if (node is ILWhileLoop) { - ILWhileLoop ilLoop = (ILWhileLoop)node; - WhileStatement whileStmt = new WhileStatement() { - Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true), - EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) - }; - yield return whileStmt; - } else if (node is ILCondition) { - ILCondition conditionalNode = (ILCondition)node; - bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0; - yield return new Ast.IfElseStatement { - Condition = (Expression)TransformExpression(conditionalNode.Condition), - TrueStatement = TransformBlock(conditionalNode.TrueBlock), - FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null - }; - } else if (node is ILSwitch) { - ILSwitch ilSwitch = (ILSwitch)node; - SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; - foreach (var caseBlock in ilSwitch.CaseBlocks) { - SwitchSection section = new SwitchSection(); - if (caseBlock.Values != null) { - section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); - } else { - section.CaseLabels.Add(new CaseLabel()); - } - section.Statements.Add(TransformBlock(caseBlock)); - switchStmt.SwitchSections.Add(section); - } - yield return switchStmt; - } else if (node is ILTryCatchBlock) { - ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); - var tryCatchStmt = new Ast.TryCatchStatement(); - tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); - foreach (var catchClause in tryCatchNode.CatchBlocks) { - if (catchClause.ExceptionVariable == null - && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) - { - tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) }); - } else { - tryCatchStmt.CatchClauses.Add( - new Ast.CatchClause { - Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, - Body = TransformBlock(catchClause) - }.WithAnnotation(catchClause.ExceptionVariable)); - } - } - if (tryCatchNode.FinallyBlock != null) - tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); - if (tryCatchNode.FaultBlock != null) { - CatchClause cc = new CatchClause(); - cc.Body = TransformBlock(tryCatchNode.FaultBlock); - cc.Body.Add(new ThrowStatement()); // rethrow - tryCatchStmt.CatchClauses.Add(cc); - } - yield return tryCatchStmt; - } else if (node is ILFixedStatement) { - ILFixedStatement fixedNode = (ILFixedStatement)node; - FixedStatement fixedStatement = new FixedStatement(); - foreach (ILExpression initializer in fixedNode.Initializers) { - Debug.Assert(initializer.Code == ILCode.Stloc); - ILVariable v = (ILVariable)initializer.Operand; - fixedStatement.Variables.Add( - new VariableInitializer { - Name = v.Name, - Initializer = (Expression)TransformExpression(initializer.Arguments[0]) - }.WithAnnotation(v)); - } - fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type); - fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); - yield return fixedStatement; - } else if (node is ILBlock) { - yield return TransformBlock((ILBlock)node); - } else { - throw new Exception("Unknown node type"); - } - } - - AstNode TransformExpression(ILExpression expr) - { - AstNode node = TransformByteCode(expr); - Expression astExpr = node as Expression; - - // get IL ranges - used in debugger - List ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); - AstNode result; - - if (astExpr != null) - result = Convert(astExpr, expr.InferredType, expr.ExpectedType); - else - result = node; - - if (result != null) - result = result.WithAnnotation(new TypeInformation(expr.InferredType)); - - if (result != null) - return result.WithAnnotation(ilRanges); - - return result; - } - - AstNode TransformByteCode(ILExpression byteCode) - { - object operand = byteCode.Operand; - AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); - - List args = new List(); - foreach(ILExpression arg in byteCode.Arguments) { - args.Add((Ast.Expression)TransformExpression(arg)); - } - Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; - Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; - Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; - - switch (byteCode.Code) { - #region Arithmetic - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - { - BinaryOperatorExpression boe; - if (byteCode.InferredType is PointerType) { - if (byteCode.Arguments[0].ExpectedType is PointerType) { - arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else if (byteCode.Arguments[1].ExpectedType is PointerType) { - arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - } - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - } - boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return boe; - } - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - { - BinaryOperatorExpression boe; - if (byteCode.InferredType is PointerType) { - if (byteCode.Arguments[0].ExpectedType is PointerType) { - arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - } - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - } - boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return boe; - } - case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); - case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); - case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); - case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); - case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); - case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); - case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); - case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); - case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); - case ILCode.PostIncrement: - case ILCode.PostIncrement_Ovf: - case ILCode.PostIncrement_Ovf_Un: - { - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - var uoe = new Ast.UnaryOperatorExpression( - (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1); - uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return uoe; - } - #endregion - #region Arrays - case ILCode.Newarr: { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - if (ct != null) { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - } - if (byteCode.Code == ILCode.InitArray) { - ace.Initializer = new ArrayInitializerExpression(); - ace.Initializer.Elements.AddRange(args); - } else { - ace.Arguments.Add(arg1); - } - return ace; - } - case ILCode.InitArray: { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - var arrayType = (ArrayType) operand; - if (ct != null) - { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Initializer = new ArrayInitializerExpression(); - var first = ace.AdditionalArraySpecifiers.First(); - first.Remove(); - ace.Arguments.AddRange(Enumerable.Repeat(0, first.Dimensions).Select(i => new EmptyExpression())); - } - var newArgs = new List(); - foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse()) - { - int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound; - for (int j = 0; j < args.Count; j += length) - { - var child = new ArrayInitializerExpression(); - child.Elements.AddRange(args.GetRange(j, length)); - newArgs.Add(child); - } - var temp = args; - args = newArgs; - newArgs = temp; - newArgs.Clear(); - } - ace.Initializer.Elements.AddRange(args); - return ace; - } - case ILCode.Ldlen: return arg1.Member("Length"); - case ILCode.Ldelem_I: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - case ILCode.Ldelem_Any: - return arg1.Indexer(arg2); - case ILCode.Ldelema: - return MakeRef(arg1.Indexer(arg2)); - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - case ILCode.Stelem_Any: - return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); - case ILCode.CompoundAssignment: - { - CastExpression cast = arg1 as CastExpression; - BinaryOperatorExpression boe = (BinaryOperatorExpression)(cast != null ? cast.Expression : arg1); - var assignment = new Ast.AssignmentExpression { - Left = boe.Left.Detach(), - Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator), - Right = boe.Right.Detach() - }.CopyAnnotationsFrom(boe); - // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because - // the operator cannot be translated back to the expanded form (as the left-hand expression - // would be evaluated twice, and might have side-effects) - if (cast != null) { - cast.Expression = assignment; - return cast; - } else { - return assignment; - } - } - #endregion - #region Comparison - case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case ILCode.Cgt_Un: { - // can also mean Inequality, when used with object references - TypeReference arg1Type = byteCode.Arguments[0].InferredType; - if (arg1Type != null && !arg1Type.IsValueType) - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - else - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - } - case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - #endregion - #region Logical - case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); - case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); - case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; - case ILCode.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2); - #endregion - #region Branch - case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); - case ILCode.Brtrue: - return new Ast.IfElseStatement() { - Condition = arg1, - TrueStatement = new BlockStatement() { - new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) - } - }; - case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); - case ILCode.LoopContinue: return new Ast.ContinueStatement(); - #endregion - #region Conversions - case ILCode.Conv_I1: - case ILCode.Conv_I2: - case ILCode.Conv_I4: - case ILCode.Conv_I8: - case ILCode.Conv_U1: - case ILCode.Conv_U2: - case ILCode.Conv_U4: - case ILCode.Conv_U8: - case ILCode.Conv_I: - case ILCode.Conv_U: - { - // conversion was handled by Convert() function using the info from type analysis - CastExpression cast = arg1 as CastExpression; - if (cast != null) { - cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); - } - return arg1; - } - case ILCode.Conv_R4: - case ILCode.Conv_R8: - case ILCode.Conv_R_Un: // TODO - return arg1; - case ILCode.Conv_Ovf_I1: - case ILCode.Conv_Ovf_I2: - case ILCode.Conv_Ovf_I4: - case ILCode.Conv_Ovf_I8: - case ILCode.Conv_Ovf_U1: - case ILCode.Conv_Ovf_U2: - case ILCode.Conv_Ovf_U4: - case ILCode.Conv_Ovf_U8: - case ILCode.Conv_Ovf_I1_Un: - case ILCode.Conv_Ovf_I2_Un: - case ILCode.Conv_Ovf_I4_Un: - case ILCode.Conv_Ovf_I8_Un: - case ILCode.Conv_Ovf_U1_Un: - case ILCode.Conv_Ovf_U2_Un: - case ILCode.Conv_Ovf_U4_Un: - case ILCode.Conv_Ovf_U8_Un: - { - // conversion was handled by Convert() function using the info from type analysis - CastExpression cast = arg1 as CastExpression; - if (cast != null) { - cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); - } - return arg1; - } - case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO - case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); - case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); - case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); - case ILCode.Castclass: return arg1.CastTo(operandAsTypeRef); - case ILCode.Unbox_Any: - // unboxing does not require a cast if the argument was an isinst instruction - if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference)) - return arg1; - else - return arg1.CastTo(operandAsTypeRef); - case ILCode.Isinst: - return arg1.CastAs(operandAsTypeRef); - case ILCode.Box: - return arg1; - case ILCode.Unbox: - return MakeRef(arg1.CastTo(operandAsTypeRef)); - #endregion - #region Indirect - case ILCode.Ldind_Ref: - case ILCode.Ldobj: - if (arg1 is DirectionExpression) - return ((DirectionExpression)arg1).Expression.Detach(); - else - return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1); - case ILCode.Stind_Ref: - case ILCode.Stobj: - if (arg1 is DirectionExpression) - return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2); - else - return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2); - #endregion - case ILCode.Arglist: - return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess }; - case ILCode.Break: return InlineAssembly(byteCode, args); - case ILCode.Call: - case ILCode.CallGetter: - case ILCode.CallSetter: - return TransformCall(false, byteCode, args); - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - case ILCode.CallvirtSetter: - return TransformCall(true, byteCode, args); - case ILCode.Ldftn: { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); - } - case ILCode.Ldvirtftn: { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldvirtftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); - } - case ILCode.Calli: return InlineAssembly(byteCode, args); - case ILCode.Ckfinite: return InlineAssembly(byteCode, args); - case ILCode.Constrained: return InlineAssembly(byteCode, args); - case ILCode.Cpblk: return InlineAssembly(byteCode, args); - case ILCode.Cpobj: return InlineAssembly(byteCode, args); - case ILCode.Dup: return arg1; - case ILCode.Endfilter: return InlineAssembly(byteCode, args); - case ILCode.Endfinally: return null; - case ILCode.Initblk: return InlineAssembly(byteCode, args); - case ILCode.Initobj: return InlineAssembly(byteCode, args); - case ILCode.DefaultValue: - return MakeDefaultValue((TypeReference)operand); - case ILCode.Jmp: return InlineAssembly(byteCode, args); - case ILCode.Ldc_I4: - return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); - case ILCode.Ldc_I8: - return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType); - case ILCode.Ldc_R4: - case ILCode.Ldc_R8: - case ILCode.Ldc_Decimal: - return new Ast.PrimitiveExpression(operand); - case ILCode.Ldfld: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); - case ILCode.Ldsfld: - return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand); - case ILCode.Stfld: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); - case ILCode.Stsfld: - return new AssignmentExpression( - AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand), - arg1); - case ILCode.Ldflda: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); - case ILCode.Ldsflda: - return MakeRef( - AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand)); - case ILCode.Ldloc: { - ILVariable v = (ILVariable)operand; - if (!v.IsParameter) - localVariablesToDefine.Add((ILVariable)operand); - Expression expr; - if (v.IsParameter && v.OriginalParameter.Index < 0) - expr = new ThisReferenceExpression(); - else - expr = new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); - return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr; - } - case ILCode.Ldloca: { - ILVariable v = (ILVariable)operand; - if (v.IsParameter && v.OriginalParameter.Index < 0) - return MakeRef(new ThisReferenceExpression()); - if (!v.IsParameter) - localVariablesToDefine.Add((ILVariable)operand); - return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); - } - case ILCode.Ldnull: return new Ast.NullReferenceExpression(); - case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); - case ILCode.Ldtoken: - if (operand is Cecil.TypeReference) { - return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); - } else { - return InlineAssembly(byteCode, args); - } - case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; - case ILCode.Localloc: - { - PointerType ptrType = byteCode.InferredType as PointerType; - TypeReference type; - if (ptrType != null) { - type = ptrType.ElementType; - } else { - type = typeSystem.Byte; - } - return new StackAllocExpression { - Type = AstBuilder.ConvertType(type), - CountExpression = DivideBySize(arg1, type) - }; - } - case ILCode.Mkrefany: - { - DirectionExpression dir = arg1 as DirectionExpression; - if (dir != null) { - return new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, - Arguments = { dir.Expression.Detach() } - }; - } else { - return InlineAssembly(byteCode, args); - } - } - case ILCode.Refanytype: - return new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.RefType, - Arguments = { arg1 } - }.Member("TypeHandle"); - case ILCode.Refanyval: - return MakeRef( - new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.RefValue, - Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) } - }); - case ILCode.Newobj: { - Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; - if (declaringType is ArrayType) { - ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; - if (ct != null && ct.ArraySpecifiers.Count >= 1) { - var ace = new Ast.ArrayCreateExpression(); - ct.ArraySpecifiers.First().Remove(); - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Type = ct; - ace.Arguments.AddRange(args); - return ace; - } - } - var oce = new Ast.ObjectCreateExpression(); - if (declaringType.IsAnonymousType()) { - MethodDefinition ctor = ((MethodReference)operand).Resolve(); - if (methodDef != null) { - oce.Initializer = new ArrayInitializerExpression(); - for (int i = 0; i < args.Count; i++) { - oce.Initializer.Elements.Add( - new NamedArgumentExpression { - Identifier = ctor.Parameters[i].Name, - Expression = args[i] - }); - } - } - return oce; - } - oce.Type = AstBuilder.ConvertType(declaringType); - oce.Arguments.AddRange(args); - return oce.WithAnnotation(operand); - } - case ILCode.No: return InlineAssembly(byteCode, args); - case ILCode.Nop: return null; - case ILCode.Pop: return arg1; - case ILCode.Readonly: return InlineAssembly(byteCode, args); - case ILCode.Ret: - if (methodDef.ReturnType.FullName != "System.Void") { - return new Ast.ReturnStatement { Expression = arg1 }; - } else { - return new Ast.ReturnStatement(); - } - case ILCode.Rethrow: return new Ast.ThrowStatement(); - case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; - case ILCode.Stloc: { - ILVariable locVar = (ILVariable)operand; - if (!locVar.IsParameter) - localVariablesToDefine.Add(locVar); - return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); - } - case ILCode.Switch: return InlineAssembly(byteCode, args); - case ILCode.Tail: return InlineAssembly(byteCode, args); - case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; - case ILCode.Unaligned: return InlineAssembly(byteCode, args); - case ILCode.Volatile: return InlineAssembly(byteCode, args); - case ILCode.YieldBreak: - return new Ast.YieldBreakStatement(); - case ILCode.YieldReturn: - return new Ast.YieldStatement { Expression = arg1 }; - case ILCode.InitObject: - case ILCode.InitCollection: - { - ArrayInitializerExpression initializer = new ArrayInitializerExpression(); - for (int i = 1; i < args.Count; i++) { - Match m = objectInitializerPattern.Match(args[i]); - if (m.Success) { - MemberReferenceExpression mre = m.Get("left").Single(); - initializer.Elements.Add( - new NamedArgumentExpression { - Identifier = mre.MemberName, - Expression = m.Get("right").Single().Detach() - }.CopyAnnotationsFrom(mre)); - } else { - m = collectionInitializerPattern.Match(args[i]); - if (m.Success) { - if (m.Get("arg").Count() == 1) { - initializer.Elements.Add(m.Get("arg").Single().Detach()); - } else { - ArrayInitializerExpression argList = new ArrayInitializerExpression(); - foreach (var expr in m.Get("arg")) { - argList.Elements.Add(expr.Detach()); - } - initializer.Elements.Add(argList); - } - } else { - initializer.Elements.Add(args[i]); - } - } - } - ObjectCreateExpression oce = arg1 as ObjectCreateExpression; - DefaultValueExpression dve = arg1 as DefaultValueExpression; - if (oce != null) { - oce.Initializer = initializer; - return oce; - } else if (dve != null) { - oce = new ObjectCreateExpression(dve.Type.Detach()); - oce.CopyAnnotationsFrom(dve); - oce.Initializer = initializer; - return oce; - } else { - return new AssignmentExpression(arg1, initializer); - } - } - case ILCode.InitializedObject: - return new InitializedObjectExpression(); - case ILCode.AddressOf: - return MakeRef(arg1); - default: - throw new Exception("Unknown OpCode: " + byteCode.Code); - } - } - - static readonly AstNode objectInitializerPattern = new AssignmentExpression( - new MemberReferenceExpression { - Target = new InitializedObjectExpression() - }.WithName("left"), - new AnyNode("right") - ); - - static readonly AstNode collectionInitializerPattern = new InvocationExpression { - Target = new MemberReferenceExpression { - Target = new InitializedObjectExpression(), - MemberName = "Add" - }, - Arguments = { new Repeat(new AnyNode("arg")) } - }; - - sealed class InitializedObjectExpression : IdentifierExpression - { - public InitializedObjectExpression() : base("__initialized_object__") {} - - protected override bool DoMatch(AstNode other, Match match) - { - return other is InitializedObjectExpression; - } - } - - /// - /// Divides expr by the size of 'type'. - /// - Expression DivideBySize(Expression expr, TypeReference type) - { - CastExpression cast = expr as CastExpression; - if (cast != null && cast.Type is PrimitiveType && ((PrimitiveType)cast.Type).Keyword == "int") - expr = cast.Expression.Detach(); - - Expression sizeOfExpression; - switch (TypeAnalysis.GetInformationAmount(type)) { - case 1: - case 8: - sizeOfExpression = new PrimitiveExpression(1); - break; - case 16: - sizeOfExpression = new PrimitiveExpression(2); - break; - case 32: - sizeOfExpression = new PrimitiveExpression(4); - break; - case 64: - sizeOfExpression = new PrimitiveExpression(8); - break; - default: - sizeOfExpression = new SizeOfExpression { Type = AstBuilder.ConvertType(type) }; - break; - } - - BinaryOperatorExpression boe = expr as BinaryOperatorExpression; - if (boe != null && boe.Operator == BinaryOperatorType.Multiply && sizeOfExpression.IsMatch(boe.Right)) - return boe.Left.Detach(); - - if (sizeOfExpression.IsMatch(expr)) - return new PrimitiveExpression(1); - - return new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, sizeOfExpression); - } - - Expression MakeDefaultValue(TypeReference type) - { - TypeDefinition typeDef = type.Resolve(); - if (typeDef != null) { - if (TypeAnalysis.IsIntegerOrEnum(typeDef)) - return AstBuilder.MakePrimitive(0, typeDef); - else if (!typeDef.IsValueType) - return new NullReferenceExpression(); - switch (typeDef.FullName) { - case "System.Nullable`1": - return new NullReferenceExpression(); - case "System.Single": - return new PrimitiveExpression(0f); - case "System.Double": - return new PrimitiveExpression(0.0); - case "System.Decimal": - return new PrimitiveExpression(0m); - } - } - return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) }; - } - - AstNode TransformCall(bool isVirtual, ILExpression byteCode, List args) - { - Cecil.MethodReference cecilMethod = (MethodReference)byteCode.Operand; - Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); - Ast.Expression target; - List methodArgs = new List(args); - if (cecilMethod.HasThis) { - target = methodArgs[0]; - methodArgs.RemoveAt(0); - - // Unpack any DirectionExpression that is used as target for the call - // (calling methods on value types implicitly passes the first argument by reference) - if (target is DirectionExpression) { - target = ((DirectionExpression)target).Expression; - target.Remove(); // detach from DirectionExpression - } - - if (cecilMethodDef != null) { - // convert null.ToLower() to ((string)null).ToLower() - if (target is NullReferenceExpression) - target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); - - if (cecilMethodDef.DeclaringType.IsInterface) { - TypeReference tr = byteCode.Arguments[0].InferredType; - if (tr != null) { - TypeDefinition td = tr.Resolve(); - if (td != null && !td.IsInterface) { - // Calling an interface method on a non-interface object: - // we need to introduce an explicit cast - target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); - } - } - } - } - } else { - target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) }; - } - if (target is ThisReferenceExpression && !isVirtual) { - // a non-virtual call on "this" might be a "base"-call. - if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) { - // If we're not calling a method in the current class; we must be calling one in the base class. - target = new BaseReferenceExpression(); - } - } - - if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) { - // On value types, the constructor can be called. - // This is equivalent to 'target = new ValueType(args);'. - ObjectCreateExpression oce = new ObjectCreateExpression(); - oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType); - AdjustArgumentsForMethodCall(cecilMethod, methodArgs); - oce.Arguments.AddRange(methodArgs); - return new AssignmentExpression(target, oce); - } - - if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { - return target.Indexer(methodArgs); - } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { - return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last()); - } - - // Test whether the method is an accessor: - if (cecilMethodDef != null) { - if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.GetMethod == cecilMethodDef) - return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod); - } - } else if (cecilMethodDef.IsGetter) { // with parameters - PropertyDefinition indexer = GetIndexer(cecilMethodDef); - if (indexer != null) - return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod); - } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.SetMethod == cecilMethodDef) - return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]); - } - } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) { - PropertyDefinition indexer = GetIndexer(cecilMethodDef); - if (indexer != null) - return new AssignmentExpression( - target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod), - methodArgs[methodArgs.Count - 1] - ); - } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.AddMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), - Operator = AssignmentOperatorType.Add, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.RemoveMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), - Operator = AssignmentOperatorType.Subtract, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { - AdjustArgumentsForMethodCall(cecilMethod, methodArgs); - return target.Invoke(methodArgs).WithAnnotation(cecilMethod); - } - } - // Default invocation - AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs); - return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); - } - - static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List methodArgs) - { - // Convert 'ref' into 'out' where necessary - for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) { - DirectionExpression dir = methodArgs[i] as DirectionExpression; - ParameterDefinition p = cecilMethod.Parameters[i]; - if (dir != null && p.IsOut && !p.IsIn) - dir.FieldDirection = FieldDirection.Out; - } - } - - static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef) - { - TypeDefinition typeDef = cecilMethodDef.DeclaringType; - string indexerName = null; - foreach (CustomAttribute ca in typeDef.CustomAttributes) { - if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { - indexerName = ca.ConstructorArguments.Single().Value as string; - break; - } - } - if (indexerName == null) - return null; - foreach (PropertyDefinition prop in typeDef.Properties) { - if (prop.Name == indexerName) { - if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef) - return prop; - } - } - return null; - } - - #if DEBUG - static readonly ConcurrentDictionary unhandledOpcodes = new ConcurrentDictionary(); - #endif - - [Conditional("DEBUG")] - public static void ClearUnhandledOpcodes() - { - #if DEBUG - unhandledOpcodes.Clear(); - #endif - } - - [Conditional("DEBUG")] - public static void PrintNumberOfUnhandledOpcodes() - { - #if DEBUG - foreach (var pair in unhandledOpcodes) { - Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value); - } - #endif - } - - static Expression InlineAssembly(ILExpression byteCode, List args) - { - #if DEBUG - unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1); - #endif - // Output the operand of the unknown IL code as well - if (byteCode.Operand != null) { - args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); - } - return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); - } - - static string FormatByteCodeOperand(object operand) - { - if (operand == null) { - return string.Empty; - //} else if (operand is ILExpression) { - // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); - } else if (operand is MethodReference) { - return ((MethodReference)operand).Name + "()"; - } else if (operand is Cecil.TypeReference) { - return ((Cecil.TypeReference)operand).FullName; - } else if (operand is VariableDefinition) { - return ((VariableDefinition)operand).Name; - } else if (operand is ParameterDefinition) { - return ((ParameterDefinition)operand).Name; - } else if (operand is FieldReference) { - return ((FieldReference)operand).Name; - } else if (operand is string) { - return "\"" + operand + "\""; - } else if (operand is int) { - return operand.ToString(); - } else { - return operand.ToString(); - } - } - - static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) - { - GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; - if (g == null) - return null; - if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType())) - return null; - return g.GenericArguments.Select(t => AstBuilder.ConvertType(t)); - } - - static Ast.DirectionExpression MakeRef(Ast.Expression expr) - { - return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; - } - - Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType) - { - if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) { - return expr; - } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) { - return Convert( - new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()), - new PointerType(((ByReferenceType)actualType).ElementType), - reqType); - } else if (actualType is PointerType && reqType is ByReferenceType) { - expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType)); - return new DirectionExpression { - FieldDirection = FieldDirection.Ref, - Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr) - }; - } else if (actualType is PointerType && reqType is PointerType) { - if (actualType.FullName != reqType.FullName) - return expr.CastTo(AstBuilder.ConvertType(reqType)); - else - return expr; - } else { - bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType); - bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType); - - if (TypeAnalysis.IsBoolean(reqType)) { - if (TypeAnalysis.IsBoolean(actualType)) - return expr; - if (actualIsIntegerOrEnum) { - return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType)); - } else { - return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression()); - } - } - if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) { - return new ConditionalExpression { - Condition = expr, - TrueExpression = AstBuilder.MakePrimitive(1, reqType), - FalseExpression = AstBuilder.MakePrimitive(0, reqType) - }; - } - - if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType)) - { - return expr.CastTo(AstBuilder.ConvertType(actualType)); - } - - bool actualIsPrimitiveType = actualIsIntegerOrEnum - || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double; - bool requiredIsPrimitiveType = requiredIsIntegerOrEnum - || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double; - if (actualIsPrimitiveType && requiredIsPrimitiveType) { - return expr.CastTo(AstBuilder.ConvertType(reqType)); - } - return expr; - } - } - } -} +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +using ICSharpCode.Decompiler.Ast.Transforms; +using ICSharpCode.Decompiler.ILAst; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.Utils; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.Ast +{ + using Ast = ICSharpCode.NRefactory.CSharp; + using Cecil = Mono.Cecil; + + public class AstMethodBodyBuilder + { + MethodDefinition methodDef; + TypeSystem typeSystem; + DecompilerContext context; + HashSet localVariablesToDefine = new HashSet(); // local variables that are missing a definition + + /// + /// Creates the body for the method definition. + /// + /// Method definition to decompile. + /// Decompilation context. + /// Parameter declarations of the method being decompiled. + /// These are used to update the parameter names when the decompiler generates names for the parameters. + /// Local variables storage that will be filled/updated with the local variables. + /// Block for the method body + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, + DecompilerContext context, + IEnumerable parameters = null, + ConcurrentDictionary> localVariables = null) + { + if (localVariables == null) + localVariables = new ConcurrentDictionary>(); + + MethodDefinition oldCurrentMethod = context.CurrentMethod; + Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); + context.CurrentMethod = methodDef; + try { + AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); + builder.methodDef = methodDef; + builder.context = context; + builder.typeSystem = methodDef.Module.TypeSystem; + if (Debugger.IsAttached) { + return builder.CreateMethodBody(parameters, localVariables); + } else { + try { + return builder.CreateMethodBody(parameters, localVariables); + } catch (OperationCanceledException) { + throw; + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } + } + } finally { + context.CurrentMethod = oldCurrentMethod; + } + } + + public BlockStatement CreateMethodBody(IEnumerable parameters, + ConcurrentDictionary> localVariables) + { + if (methodDef.Body == null) return null; + + if (localVariables == null) + throw new ArgumentException("localVariables must be instantiated"); + + context.CancellationToken.ThrowIfCancellationRequested(); + ILBlock ilMethod = new ILBlock(); + ILAstBuilder astBuilder = new ILAstBuilder(); + ilMethod.Body = astBuilder.Build(methodDef, true, context); + + context.CancellationToken.ThrowIfCancellationRequested(); + ILAstOptimizer bodyGraph = new ILAstOptimizer(); + bodyGraph.Optimize(context, ilMethod); + context.CancellationToken.ThrowIfCancellationRequested(); + + var allVariables = ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable) + .Where(v => v != null && !v.IsParameter).Distinct(); + Debug.Assert(context.CurrentMethod == methodDef); + NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); + + if (parameters != null) { + foreach (var pair in (from p in parameters + join v in astBuilder.Parameters on p.Annotation() equals v.OriginalParameter + select new { p, v.Name })) + { + pair.p.Name = pair.Name; + } + } + + context.CancellationToken.ThrowIfCancellationRequested(); + Ast.BlockStatement astBlock = TransformBlock(ilMethod); + CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments + + Statement insertionPoint = astBlock.Statements.FirstOrDefault(); + foreach (ILVariable v in localVariablesToDefine) { + AstType type; + if (v.Type.ContainsAnonymousType()) + type = new SimpleType("var"); + else + type = AstBuilder.ConvertType(v.Type); + var newVarDecl = new VariableDeclarationStatement(type, v.Name); + newVarDecl.Variables.Single().AddAnnotation(v); + astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); + } + + // store the variables - used for debugger + int token = methodDef.MetadataToken.ToInt32(); + localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables); + + return astBlock; + } + + Ast.BlockStatement TransformBlock(ILBlock block) + { + Ast.BlockStatement astBlock = new BlockStatement(); + if (block != null) { + foreach(ILNode node in block.GetChildren()) { + astBlock.AddRange(TransformNode(node)); + } + } + return astBlock; + } + + IEnumerable TransformNode(ILNode node) + { + if (node is ILLabel) { + yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name }; + } else if (node is ILExpression) { + List ilRanges = ILRange.OrderAndJoint(node.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); + AstNode codeExpr = TransformExpression((ILExpression)node); + if (codeExpr != null) { + codeExpr = codeExpr.WithAnnotation(ilRanges); + if (codeExpr is Ast.Expression) { + yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; + } else if (codeExpr is Ast.Statement) { + yield return (Ast.Statement)codeExpr; + } else { + throw new Exception(); + } + } + } else if (node is ILWhileLoop) { + ILWhileLoop ilLoop = (ILWhileLoop)node; + WhileStatement whileStmt = new WhileStatement() { + Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true), + EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) + }; + yield return whileStmt; + } else if (node is ILCondition) { + ILCondition conditionalNode = (ILCondition)node; + bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0; + yield return new Ast.IfElseStatement { + Condition = (Expression)TransformExpression(conditionalNode.Condition), + TrueStatement = TransformBlock(conditionalNode.TrueBlock), + FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null + }; + } else if (node is ILSwitch) { + ILSwitch ilSwitch = (ILSwitch)node; + SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; + foreach (var caseBlock in ilSwitch.CaseBlocks) { + SwitchSection section = new SwitchSection(); + if (caseBlock.Values != null) { + section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); + } else { + section.CaseLabels.Add(new CaseLabel()); + } + section.Statements.Add(TransformBlock(caseBlock)); + switchStmt.SwitchSections.Add(section); + } + yield return switchStmt; + } else if (node is ILTryCatchBlock) { + ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); + var tryCatchStmt = new Ast.TryCatchStatement(); + tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); + foreach (var catchClause in tryCatchNode.CatchBlocks) { + if (catchClause.ExceptionVariable == null + && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) + { + tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) }); + } else { + tryCatchStmt.CatchClauses.Add( + new Ast.CatchClause { + Type = AstBuilder.ConvertType(catchClause.ExceptionType), + VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, + Body = TransformBlock(catchClause) + }.WithAnnotation(catchClause.ExceptionVariable)); + } + } + if (tryCatchNode.FinallyBlock != null) + tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); + if (tryCatchNode.FaultBlock != null) { + CatchClause cc = new CatchClause(); + cc.Body = TransformBlock(tryCatchNode.FaultBlock); + cc.Body.Add(new ThrowStatement()); // rethrow + tryCatchStmt.CatchClauses.Add(cc); + } + yield return tryCatchStmt; + } else if (node is ILFixedStatement) { + ILFixedStatement fixedNode = (ILFixedStatement)node; + FixedStatement fixedStatement = new FixedStatement(); + foreach (ILExpression initializer in fixedNode.Initializers) { + Debug.Assert(initializer.Code == ILCode.Stloc); + ILVariable v = (ILVariable)initializer.Operand; + fixedStatement.Variables.Add( + new VariableInitializer { + Name = v.Name, + Initializer = (Expression)TransformExpression(initializer.Arguments[0]) + }.WithAnnotation(v)); + } + fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type); + fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); + yield return fixedStatement; + } else if (node is ILBlock) { + yield return TransformBlock((ILBlock)node); + } else { + throw new Exception("Unknown node type"); + } + } + + AstNode TransformExpression(ILExpression expr) + { + AstNode node = TransformByteCode(expr); + Expression astExpr = node as Expression; + + // get IL ranges - used in debugger + List ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); + AstNode result; + + if (astExpr != null) + result = Convert(astExpr, expr.InferredType, expr.ExpectedType); + else + result = node; + + if (result != null) + result = result.WithAnnotation(new TypeInformation(expr.InferredType)); + + if (result != null) + return result.WithAnnotation(ilRanges); + + return result; + } + + AstNode TransformByteCode(ILExpression byteCode) + { + object operand = byteCode.Operand; + AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); + + List args = new List(); + foreach(ILExpression arg in byteCode.Arguments) { + args.Add((Ast.Expression)TransformExpression(arg)); + } + Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; + Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; + Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; + + switch (byteCode.Code) { + #region Arithmetic + case ILCode.Add: + case ILCode.Add_Ovf: + case ILCode.Add_Ovf_Un: + { + BinaryOperatorExpression boe; + if (byteCode.InferredType is PointerType) { + if (byteCode.Arguments[0].ExpectedType is PointerType) { + arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); + } else if (byteCode.Arguments[1].ExpectedType is PointerType) { + arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType); + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + } + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + } + boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); + return boe; + } + case ILCode.Sub: + case ILCode.Sub_Ovf: + case ILCode.Sub_Ovf_Un: + { + BinaryOperatorExpression boe; + if (byteCode.InferredType is PointerType) { + if (byteCode.Arguments[0].ExpectedType is PointerType) { + arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + } + } else { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + } + boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); + return boe; + } + case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); + case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); + case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); + case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); + case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); + case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); + case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); + case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); + case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); + case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); + case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); + case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); + case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); + case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); + case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); + case ILCode.PostIncrement: + case ILCode.PostIncrement_Ovf: + case ILCode.PostIncrement_Ovf_Un: + { + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + var uoe = new Ast.UnaryOperatorExpression( + (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1); + uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); + return uoe; + } + #endregion + #region Arrays + case ILCode.Newarr: { + var ace = new Ast.ArrayCreateExpression(); + ace.Type = operandAsTypeRef; + ComposedType ct = operandAsTypeRef as ComposedType; + if (ct != null) { + // change "new (int[,])[10] to new int[10][,]" + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + } + if (byteCode.Code == ILCode.InitArray) { + ace.Initializer = new ArrayInitializerExpression(); + ace.Initializer.Elements.AddRange(args); + } else { + ace.Arguments.Add(arg1); + } + return ace; + } + case ILCode.InitArray: { + var ace = new Ast.ArrayCreateExpression(); + ace.Type = operandAsTypeRef; + ComposedType ct = operandAsTypeRef as ComposedType; + var arrayType = (ArrayType) operand; + if (ct != null) + { + // change "new (int[,])[10] to new int[10][,]" + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + ace.Initializer = new ArrayInitializerExpression(); + var first = ace.AdditionalArraySpecifiers.First(); + first.Remove(); + ace.Arguments.AddRange(Enumerable.Repeat(0, first.Dimensions).Select(i => new EmptyExpression())); + } + var newArgs = new List(); + foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse()) + { + int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound; + for (int j = 0; j < args.Count; j += length) + { + var child = new ArrayInitializerExpression(); + child.Elements.AddRange(args.GetRange(j, length)); + newArgs.Add(child); + } + var temp = args; + args = newArgs; + newArgs = temp; + newArgs.Clear(); + } + ace.Initializer.Elements.AddRange(args); + return ace; + } + case ILCode.Ldlen: return arg1.Member("Length"); + case ILCode.Ldelem_I: + case ILCode.Ldelem_I1: + case ILCode.Ldelem_I2: + case ILCode.Ldelem_I4: + case ILCode.Ldelem_I8: + case ILCode.Ldelem_U1: + case ILCode.Ldelem_U2: + case ILCode.Ldelem_U4: + case ILCode.Ldelem_R4: + case ILCode.Ldelem_R8: + case ILCode.Ldelem_Ref: + case ILCode.Ldelem_Any: + return arg1.Indexer(arg2); + case ILCode.Ldelema: + return MakeRef(arg1.Indexer(arg2)); + case ILCode.Stelem_I: + case ILCode.Stelem_I1: + case ILCode.Stelem_I2: + case ILCode.Stelem_I4: + case ILCode.Stelem_I8: + case ILCode.Stelem_R4: + case ILCode.Stelem_R8: + case ILCode.Stelem_Ref: + case ILCode.Stelem_Any: + return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); + case ILCode.CompoundAssignment: + { + CastExpression cast = arg1 as CastExpression; + BinaryOperatorExpression boe = (BinaryOperatorExpression)(cast != null ? cast.Expression : arg1); + var assignment = new Ast.AssignmentExpression { + Left = boe.Left.Detach(), + Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator), + Right = boe.Right.Detach() + }.CopyAnnotationsFrom(boe); + // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because + // the operator cannot be translated back to the expanded form (as the left-hand expression + // would be evaluated twice, and might have side-effects) + if (cast != null) { + cast.Expression = assignment; + return cast; + } else { + return assignment; + } + } + #endregion + #region Comparison + case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case ILCode.Cgt_Un: { + // can also mean Inequality, when used with object references + TypeReference arg1Type = byteCode.Arguments[0].InferredType; + if (arg1Type != null && !arg1Type.IsValueType) + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + else + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + } + case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + #endregion + #region Logical + case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); + case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); + case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); + case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; + case ILCode.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2); + #endregion + #region Branch + case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); + case ILCode.Brtrue: + return new Ast.IfElseStatement() { + Condition = arg1, + TrueStatement = new BlockStatement() { + new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) + } + }; + case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); + case ILCode.LoopContinue: return new Ast.ContinueStatement(); + #endregion + #region Conversions + case ILCode.Conv_I1: + case ILCode.Conv_I2: + case ILCode.Conv_I4: + case ILCode.Conv_I8: + case ILCode.Conv_U1: + case ILCode.Conv_U2: + case ILCode.Conv_U4: + case ILCode.Conv_U8: + case ILCode.Conv_I: + case ILCode.Conv_U: + { + // conversion was handled by Convert() function using the info from type analysis + CastExpression cast = arg1 as CastExpression; + if (cast != null) { + cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); + } + return arg1; + } + case ILCode.Conv_R4: + case ILCode.Conv_R8: + case ILCode.Conv_R_Un: // TODO + return arg1; + case ILCode.Conv_Ovf_I1: + case ILCode.Conv_Ovf_I2: + case ILCode.Conv_Ovf_I4: + case ILCode.Conv_Ovf_I8: + case ILCode.Conv_Ovf_U1: + case ILCode.Conv_Ovf_U2: + case ILCode.Conv_Ovf_U4: + case ILCode.Conv_Ovf_U8: + case ILCode.Conv_Ovf_I1_Un: + case ILCode.Conv_Ovf_I2_Un: + case ILCode.Conv_Ovf_I4_Un: + case ILCode.Conv_Ovf_I8_Un: + case ILCode.Conv_Ovf_U1_Un: + case ILCode.Conv_Ovf_U2_Un: + case ILCode.Conv_Ovf_U4_Un: + case ILCode.Conv_Ovf_U8_Un: + { + // conversion was handled by Convert() function using the info from type analysis + CastExpression cast = arg1 as CastExpression; + if (cast != null) { + cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); + } + return arg1; + } + case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO + case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); + case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); + case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); + case ILCode.Castclass: return arg1.CastTo(operandAsTypeRef); + case ILCode.Unbox_Any: + // unboxing does not require a cast if the argument was an isinst instruction + if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference)) + return arg1; + else + return arg1.CastTo(operandAsTypeRef); + case ILCode.Isinst: + return arg1.CastAs(operandAsTypeRef); + case ILCode.Box: + return arg1; + case ILCode.Unbox: + return MakeRef(arg1.CastTo(operandAsTypeRef)); + #endregion + #region Indirect + case ILCode.Ldind_Ref: + case ILCode.Ldobj: + if (arg1 is DirectionExpression) + return ((DirectionExpression)arg1).Expression.Detach(); + else + return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1); + case ILCode.Stind_Ref: + case ILCode.Stobj: + if (arg1 is DirectionExpression) + return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2); + else + return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2); + #endregion + case ILCode.Arglist: + return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess }; + case ILCode.Break: return InlineAssembly(byteCode, args); + case ILCode.Call: + case ILCode.CallGetter: + case ILCode.CallSetter: + return TransformCall(false, byteCode, args); + case ILCode.Callvirt: + case ILCode.CallvirtGetter: + case ILCode.CallvirtSetter: + return TransformCall(true, byteCode, args); + case ILCode.Ldftn: { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); + } + case ILCode.Ldvirtftn: { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldvirtftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); + } + case ILCode.Calli: return InlineAssembly(byteCode, args); + case ILCode.Ckfinite: return InlineAssembly(byteCode, args); + case ILCode.Constrained: return InlineAssembly(byteCode, args); + case ILCode.Cpblk: return InlineAssembly(byteCode, args); + case ILCode.Cpobj: return InlineAssembly(byteCode, args); + case ILCode.Dup: return arg1; + case ILCode.Endfilter: return InlineAssembly(byteCode, args); + case ILCode.Endfinally: return null; + case ILCode.Initblk: return InlineAssembly(byteCode, args); + case ILCode.Initobj: return InlineAssembly(byteCode, args); + case ILCode.DefaultValue: + return MakeDefaultValue((TypeReference)operand); + case ILCode.Jmp: return InlineAssembly(byteCode, args); + case ILCode.Ldc_I4: + return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); + case ILCode.Ldc_I8: + return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType); + case ILCode.Ldc_R4: + case ILCode.Ldc_R8: + case ILCode.Ldc_Decimal: + return new Ast.PrimitiveExpression(operand); + case ILCode.Ldfld: + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); + case ILCode.Ldsfld: + return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) + .Member(((FieldReference)operand).Name).WithAnnotation(operand); + case ILCode.Stfld: + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); + case ILCode.Stsfld: + return new AssignmentExpression( + AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) + .Member(((FieldReference)operand).Name).WithAnnotation(operand), + arg1); + case ILCode.Ldflda: + if (arg1 is DirectionExpression) + arg1 = ((DirectionExpression)arg1).Expression.Detach(); + return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); + case ILCode.Ldsflda: + return MakeRef( + AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) + .Member(((FieldReference)operand).Name).WithAnnotation(operand)); + case ILCode.Ldloc: { + ILVariable v = (ILVariable)operand; + if (!v.IsParameter) + localVariablesToDefine.Add((ILVariable)operand); + Expression expr; + if (v.IsParameter && v.OriginalParameter.Index < 0) + expr = new ThisReferenceExpression(); + else + expr = new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); + return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr; + } + case ILCode.Ldloca: { + ILVariable v = (ILVariable)operand; + if (v.IsParameter && v.OriginalParameter.Index < 0) + return MakeRef(new ThisReferenceExpression()); + if (!v.IsParameter) + localVariablesToDefine.Add((ILVariable)operand); + return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); + } + case ILCode.Ldnull: return new Ast.NullReferenceExpression(); + case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); + case ILCode.Ldtoken: + if (operand is Cecil.TypeReference) { + return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); + } else { + return InlineAssembly(byteCode, args); + } + case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; + case ILCode.Localloc: + { + PointerType ptrType = byteCode.InferredType as PointerType; + TypeReference type; + if (ptrType != null) { + type = ptrType.ElementType; + } else { + type = typeSystem.Byte; + } + return new StackAllocExpression { + Type = AstBuilder.ConvertType(type), + CountExpression = DivideBySize(arg1, type) + }; + } + case ILCode.Mkrefany: + { + DirectionExpression dir = arg1 as DirectionExpression; + if (dir != null) { + return new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, + Arguments = { dir.Expression.Detach() } + }; + } else { + return InlineAssembly(byteCode, args); + } + } + case ILCode.Refanytype: + return new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.RefType, + Arguments = { arg1 } + }.Member("TypeHandle"); + case ILCode.Refanyval: + return MakeRef( + new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.RefValue, + Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) } + }); + case ILCode.Newobj: { + Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; + if (declaringType is ArrayType) { + ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; + if (ct != null && ct.ArraySpecifiers.Count >= 1) { + var ace = new Ast.ArrayCreateExpression(); + ct.ArraySpecifiers.First().Remove(); + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + ace.Type = ct; + ace.Arguments.AddRange(args); + return ace; + } + } + var oce = new Ast.ObjectCreateExpression(); + if (declaringType.IsAnonymousType()) { + MethodDefinition ctor = ((MethodReference)operand).Resolve(); + if (methodDef != null) { + oce.Initializer = new ArrayInitializerExpression(); + for (int i = 0; i < args.Count; i++) { + oce.Initializer.Elements.Add( + new NamedArgumentExpression { + Identifier = ctor.Parameters[i].Name, + Expression = args[i] + }); + } + } + return oce; + } + oce.Type = AstBuilder.ConvertType(declaringType); + oce.Arguments.AddRange(args); + return oce.WithAnnotation(operand); + } + case ILCode.No: return InlineAssembly(byteCode, args); + case ILCode.Nop: return null; + case ILCode.Pop: return arg1; + case ILCode.Readonly: return InlineAssembly(byteCode, args); + case ILCode.Ret: + if (methodDef.ReturnType.FullName != "System.Void") { + return new Ast.ReturnStatement { Expression = arg1 }; + } else { + return new Ast.ReturnStatement(); + } + case ILCode.Rethrow: return new Ast.ThrowStatement(); + case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; + case ILCode.Stloc: { + ILVariable locVar = (ILVariable)operand; + if (!locVar.IsParameter) + localVariablesToDefine.Add(locVar); + return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); + } + case ILCode.Switch: return InlineAssembly(byteCode, args); + case ILCode.Tail: return InlineAssembly(byteCode, args); + case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; + case ILCode.Unaligned: return InlineAssembly(byteCode, args); + case ILCode.Volatile: return InlineAssembly(byteCode, args); + case ILCode.YieldBreak: + return new Ast.YieldBreakStatement(); + case ILCode.YieldReturn: + return new Ast.YieldStatement { Expression = arg1 }; + case ILCode.InitObject: + case ILCode.InitCollection: + { + ArrayInitializerExpression initializer = new ArrayInitializerExpression(); + for (int i = 1; i < args.Count; i++) { + Match m = objectInitializerPattern.Match(args[i]); + if (m.Success) { + MemberReferenceExpression mre = m.Get("left").Single(); + initializer.Elements.Add( + new NamedArgumentExpression { + Identifier = mre.MemberName, + Expression = m.Get("right").Single().Detach() + }.CopyAnnotationsFrom(mre)); + } else { + m = collectionInitializerPattern.Match(args[i]); + if (m.Success) { + if (m.Get("arg").Count() == 1) { + initializer.Elements.Add(m.Get("arg").Single().Detach()); + } else { + ArrayInitializerExpression argList = new ArrayInitializerExpression(); + foreach (var expr in m.Get("arg")) { + argList.Elements.Add(expr.Detach()); + } + initializer.Elements.Add(argList); + } + } else { + initializer.Elements.Add(args[i]); + } + } + } + ObjectCreateExpression oce = arg1 as ObjectCreateExpression; + DefaultValueExpression dve = arg1 as DefaultValueExpression; + if (oce != null) { + oce.Initializer = initializer; + return oce; + } else if (dve != null) { + oce = new ObjectCreateExpression(dve.Type.Detach()); + oce.CopyAnnotationsFrom(dve); + oce.Initializer = initializer; + return oce; + } else { + return new AssignmentExpression(arg1, initializer); + } + } + case ILCode.InitializedObject: + return new InitializedObjectExpression(); + case ILCode.AddressOf: + return MakeRef(arg1); + default: + throw new Exception("Unknown OpCode: " + byteCode.Code); + } + } + + static readonly AstNode objectInitializerPattern = new AssignmentExpression( + new MemberReferenceExpression { + Target = new InitializedObjectExpression() + }.WithName("left"), + new AnyNode("right") + ); + + static readonly AstNode collectionInitializerPattern = new InvocationExpression { + Target = new MemberReferenceExpression { + Target = new InitializedObjectExpression(), + MemberName = "Add" + }, + Arguments = { new Repeat(new AnyNode("arg")) } + }; + + sealed class InitializedObjectExpression : IdentifierExpression + { + public InitializedObjectExpression() : base("__initialized_object__") {} + + protected override bool DoMatch(AstNode other, Match match) + { + return other is InitializedObjectExpression; + } + } + + /// + /// Divides expr by the size of 'type'. + /// + Expression DivideBySize(Expression expr, TypeReference type) + { + CastExpression cast = expr as CastExpression; + if (cast != null && cast.Type is PrimitiveType && ((PrimitiveType)cast.Type).Keyword == "int") + expr = cast.Expression.Detach(); + + Expression sizeOfExpression; + switch (TypeAnalysis.GetInformationAmount(type)) { + case 1: + case 8: + sizeOfExpression = new PrimitiveExpression(1); + break; + case 16: + sizeOfExpression = new PrimitiveExpression(2); + break; + case 32: + sizeOfExpression = new PrimitiveExpression(4); + break; + case 64: + sizeOfExpression = new PrimitiveExpression(8); + break; + default: + sizeOfExpression = new SizeOfExpression { Type = AstBuilder.ConvertType(type) }; + break; + } + + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null && boe.Operator == BinaryOperatorType.Multiply && sizeOfExpression.IsMatch(boe.Right)) + return boe.Left.Detach(); + + if (sizeOfExpression.IsMatch(expr)) + return new PrimitiveExpression(1); + + return new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, sizeOfExpression); + } + + Expression MakeDefaultValue(TypeReference type) + { + TypeDefinition typeDef = type.Resolve(); + if (typeDef != null) { + if (TypeAnalysis.IsIntegerOrEnum(typeDef)) + return AstBuilder.MakePrimitive(0, typeDef); + else if (!typeDef.IsValueType) + return new NullReferenceExpression(); + switch (typeDef.FullName) { + case "System.Nullable`1": + return new NullReferenceExpression(); + case "System.Single": + return new PrimitiveExpression(0f); + case "System.Double": + return new PrimitiveExpression(0.0); + case "System.Decimal": + return new PrimitiveExpression(0m); + } + } + return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) }; + } + + AstNode TransformCall(bool isVirtual, ILExpression byteCode, List args) + { + Cecil.MethodReference cecilMethod = (MethodReference)byteCode.Operand; + Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); + Ast.Expression target; + List methodArgs = new List(args); + if (cecilMethod.HasThis) { + target = methodArgs[0]; + methodArgs.RemoveAt(0); + + // Unpack any DirectionExpression that is used as target for the call + // (calling methods on value types implicitly passes the first argument by reference) + if (target is DirectionExpression) { + target = ((DirectionExpression)target).Expression; + target.Remove(); // detach from DirectionExpression + } + + if (cecilMethodDef != null) { + // convert null.ToLower() to ((string)null).ToLower() + if (target is NullReferenceExpression) + target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); + + if (cecilMethodDef.DeclaringType.IsInterface) { + TypeReference tr = byteCode.Arguments[0].InferredType; + if (tr != null) { + TypeDefinition td = tr.Resolve(); + if (td != null && !td.IsInterface) { + // Calling an interface method on a non-interface object: + // we need to introduce an explicit cast + target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); + } + } + } + } + } else { + target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) }; + } + if (target is ThisReferenceExpression && !isVirtual) { + // a non-virtual call on "this" might be a "base"-call. + if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) { + // If we're not calling a method in the current class; we must be calling one in the base class. + target = new BaseReferenceExpression(); + } + } + + if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) { + // On value types, the constructor can be called. + // This is equivalent to 'target = new ValueType(args);'. + ObjectCreateExpression oce = new ObjectCreateExpression(); + oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType); + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + oce.Arguments.AddRange(methodArgs); + return new AssignmentExpression(target, oce); + } + + if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { + return target.Indexer(methodArgs); + } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { + return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last()); + } + + // Test whether the method is an accessor: + if (cecilMethodDef != null) { + if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { + foreach (var prop in cecilMethodDef.DeclaringType.Properties) { + if (prop.GetMethod == cecilMethodDef) + return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod); + } + } else if (cecilMethodDef.IsGetter) { // with parameters + PropertyDefinition indexer = GetIndexer(cecilMethodDef); + if (indexer != null) + return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod); + } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { + foreach (var prop in cecilMethodDef.DeclaringType.Properties) { + if (prop.SetMethod == cecilMethodDef) + return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]); + } + } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) { + PropertyDefinition indexer = GetIndexer(cecilMethodDef); + if (indexer != null) + return new AssignmentExpression( + target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod), + methodArgs[methodArgs.Count - 1] + ); + } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { + foreach (var ev in cecilMethodDef.DeclaringType.Events) { + if (ev.AddMethod == cecilMethodDef) { + return new Ast.AssignmentExpression { + Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), + Operator = AssignmentOperatorType.Add, + Right = methodArgs[0] + }; + } + } + } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { + foreach (var ev in cecilMethodDef.DeclaringType.Events) { + if (ev.RemoveMethod == cecilMethodDef) { + return new Ast.AssignmentExpression { + Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), + Operator = AssignmentOperatorType.Subtract, + Right = methodArgs[0] + }; + } + } + } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + return target.Invoke(methodArgs).WithAnnotation(cecilMethod); + } + } + // Default invocation + AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs); + return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); + } + + static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List methodArgs) + { + // Convert 'ref' into 'out' where necessary + for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) { + DirectionExpression dir = methodArgs[i] as DirectionExpression; + ParameterDefinition p = cecilMethod.Parameters[i]; + if (dir != null && p.IsOut && !p.IsIn) + dir.FieldDirection = FieldDirection.Out; + } + } + + static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef) + { + TypeDefinition typeDef = cecilMethodDef.DeclaringType; + string indexerName = null; + foreach (CustomAttribute ca in typeDef.CustomAttributes) { + if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { + indexerName = ca.ConstructorArguments.Single().Value as string; + break; + } + } + if (indexerName == null) + return null; + foreach (PropertyDefinition prop in typeDef.Properties) { + if (prop.Name == indexerName) { + if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef) + return prop; + } + } + return null; + } + + #if DEBUG + static readonly ConcurrentDictionary unhandledOpcodes = new ConcurrentDictionary(); + #endif + + [Conditional("DEBUG")] + public static void ClearUnhandledOpcodes() + { + #if DEBUG + unhandledOpcodes.Clear(); + #endif + } + + [Conditional("DEBUG")] + public static void PrintNumberOfUnhandledOpcodes() + { + #if DEBUG + foreach (var pair in unhandledOpcodes) { + Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value); + } + #endif + } + + static Expression InlineAssembly(ILExpression byteCode, List args) + { + #if DEBUG + unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1); + #endif + // Output the operand of the unknown IL code as well + if (byteCode.Operand != null) { + args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); + } + return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); + } + + static string FormatByteCodeOperand(object operand) + { + if (operand == null) { + return string.Empty; + //} else if (operand is ILExpression) { + // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); + } else if (operand is MethodReference) { + return ((MethodReference)operand).Name + "()"; + } else if (operand is Cecil.TypeReference) { + return ((Cecil.TypeReference)operand).FullName; + } else if (operand is VariableDefinition) { + return ((VariableDefinition)operand).Name; + } else if (operand is ParameterDefinition) { + return ((ParameterDefinition)operand).Name; + } else if (operand is FieldReference) { + return ((FieldReference)operand).Name; + } else if (operand is string) { + return "\"" + operand + "\""; + } else if (operand is int) { + return operand.ToString(); + } else { + return operand.ToString(); + } + } + + static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) + { + GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; + if (g == null) + return null; + if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType())) + return null; + return g.GenericArguments.Select(t => AstBuilder.ConvertType(t)); + } + + static Ast.DirectionExpression MakeRef(Ast.Expression expr) + { + return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; + } + + Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType) + { + if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) { + return expr; + } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) { + return Convert( + new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()), + new PointerType(((ByReferenceType)actualType).ElementType), + reqType); + } else if (actualType is PointerType && reqType is ByReferenceType) { + expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType)); + return new DirectionExpression { + FieldDirection = FieldDirection.Ref, + Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr) + }; + } else if (actualType is PointerType && reqType is PointerType) { + if (actualType.FullName != reqType.FullName) + return expr.CastTo(AstBuilder.ConvertType(reqType)); + else + return expr; + } else { + bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType); + bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType); + + if (TypeAnalysis.IsBoolean(reqType)) { + if (TypeAnalysis.IsBoolean(actualType)) + return expr; + if (actualIsIntegerOrEnum) { + return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType)); + } else { + return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression()); + } + } + if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) { + return new ConditionalExpression { + Condition = expr, + TrueExpression = AstBuilder.MakePrimitive(1, reqType), + FalseExpression = AstBuilder.MakePrimitive(0, reqType) + }; + } + + if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType)) + { + return expr.CastTo(AstBuilder.ConvertType(actualType)); + } + + bool actualIsPrimitiveType = actualIsIntegerOrEnum + || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double; + bool requiredIsPrimitiveType = requiredIsIntegerOrEnum + || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double; + if (actualIsPrimitiveType && requiredIsPrimitiveType) { + return expr.CastTo(AstBuilder.ConvertType(reqType)); + } + return expr; + } + } + } +}