diff --git a/ICSharpCode.Decompiler/CSharp/Analysis/AnnotationNames.cs b/ICSharpCode.Decompiler/CSharp/Analysis/AnnotationNames.cs new file mode 100644 index 000000000..94d19e4b6 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Analysis/AnnotationNames.cs @@ -0,0 +1,48 @@ +// +// Annotations.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class AnnotationNames + { + //Used const instead of readonly to allow values to be used in switch cases. + + public const string AssertionMethodAttribute = "JetBrains.Annotations.AssertionMethodAttribute"; + public const string AssertionConditionAttribute = "JetBrains.Annotations.AssertionConditionAttribute"; + public const string AssertionConditionTypeAttribute = "JetBrains.Annotations.AssertionConditionType"; + + public const string AssertionConditionTypeIsTrue = "JetBrains.Annotations.AssertionConditionType.IS_TRUE"; + public const string AssertionConditionTypeIsFalse = "JetBrains.Annotations.AssertionConditionType.IS_FALSE"; + public const string AssertionConditionTypeIsNull = "JetBrains.Annotations.AssertionConditionType.IS_NULL"; + public const string AssertionConditionTypeIsNotNull = "JetBrains.Annotations.AssertionConditionType.IS_NOT_NULL"; + + public const string NotNullAttribute = "JetBrains.Annotations.NotNullAttribute"; + public const string CanBeNullAttribute = "JetBrains.Annotations.CanBeNullAttribute"; + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Analysis/ControlFlow.cs b/ICSharpCode.Decompiler/CSharp/Analysis/ControlFlow.cs new file mode 100644 index 000000000..4db0c5bbf --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Analysis/ControlFlow.cs @@ -0,0 +1,786 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents a node in the control flow graph of a C# method. + /// + public class ControlFlowNode + { + public readonly Statement PreviousStatement; + public readonly Statement NextStatement; + + public readonly ControlFlowNodeType Type; + + public readonly List Outgoing = new List(); + public readonly List Incoming = new List(); + + public ControlFlowNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + if (previousStatement == null && nextStatement == null) + throw new ArgumentException("previousStatement and nextStatement must not be both null"); + this.PreviousStatement = previousStatement; + this.NextStatement = nextStatement; + this.Type = type; + } + } + + public enum ControlFlowNodeType + { + /// + /// Unknown node type + /// + None, + /// + /// Node in front of a statement + /// + StartNode, + /// + /// Node between two statements + /// + BetweenStatements, + /// + /// Node at the end of a statement list + /// + EndNode, + /// + /// Node representing the position before evaluating the condition of a loop. + /// + LoopCondition + } + + public class ControlFlowEdge + { + public readonly ControlFlowNode From; + public readonly ControlFlowNode To; + public readonly ControlFlowEdgeType Type; + + List jumpOutOfTryFinally; + + public ControlFlowEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) + { + if (from == null) + throw new ArgumentNullException("from"); + if (to == null) + throw new ArgumentNullException("to"); + this.From = from; + this.To = to; + this.Type = type; + } + + internal void AddJumpOutOfTryFinally(TryCatchStatement tryFinally) + { + if (jumpOutOfTryFinally == null) + jumpOutOfTryFinally = new List(); + jumpOutOfTryFinally.Add(tryFinally); + } + + /// + /// Gets whether this control flow edge is leaving any try-finally statements. + /// + public bool IsLeavingTryFinally { + get { return jumpOutOfTryFinally != null; } + } + + /// + /// Gets the try-finally statements that this control flow edge is leaving. + /// + public IEnumerable TryFinallyStatements { + get { return jumpOutOfTryFinally ?? Enumerable.Empty(); } + } + } + + public enum ControlFlowEdgeType + { + /// + /// Regular control flow. + /// + Normal, + /// + /// Conditional control flow (edge taken if condition is true) + /// + ConditionTrue, + /// + /// Conditional control flow (edge taken if condition is false) + /// + ConditionFalse, + /// + /// A jump statement (goto, goto case, break or continue) + /// + Jump + } + + /// + /// Constructs the control flow graph for C# statements. + /// + public class ControlFlowGraphBuilder + { + // Written according to the reachability rules in the C# spec (§8.1 End points and reachability) + + protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ControlFlowNode(previousStatement, nextStatement, type); + } + + protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ControlFlowEdge(from, to, type); + } + + Statement rootStatement; + CSharpTypeResolveContext typeResolveContext; + Func resolver; + List nodes; + Dictionary labels; + List gotoStatements; + CancellationToken cancellationToken; + + internal IList BuildControlFlowGraph(Statement statement, Func resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) + { + NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); + nodeCreationVisitor.builder = this; + try { + this.nodes = new List(); + this.labels = new Dictionary(); + this.gotoStatements = new List(); + this.rootStatement = statement; + this.resolver = resolver; + this.typeResolveContext = typeResolveContext; + this.cancellationToken = cancellationToken; + + ControlFlowNode entryPoint = CreateStartNode(statement); + statement.AcceptVisitor(nodeCreationVisitor, entryPoint); + + // Resolve goto statements: + foreach (ControlFlowNode gotoStmt in gotoStatements) { + string label = ((GotoStatement)gotoStmt.NextStatement).Label; + ControlFlowNode labelNode; + if (labels.TryGetValue(label, out labelNode)) + nodeCreationVisitor.Connect(gotoStmt, labelNode, ControlFlowEdgeType.Jump); + } + + AnnotateLeaveEdgesWithTryFinallyBlocks(); + + return nodes; + } finally { + this.nodes = null; + this.labels = null; + this.gotoStatements = null; + this.rootStatement = null; + this.resolver = null; + this.typeResolveContext = null; + this.cancellationToken = CancellationToken.None; + } + } + + void AnnotateLeaveEdgesWithTryFinallyBlocks() + { + foreach (ControlFlowEdge edge in nodes.SelectMany(n => n.Outgoing)) { + if (edge.Type != ControlFlowEdgeType.Jump) { + // Only jumps are potential candidates for leaving try-finally blocks. + // Note that the regular edges leaving try or catch blocks are already annotated by the visitor. + continue; + } + Statement gotoStatement = edge.From.NextStatement; + Debug.Assert(gotoStatement is GotoStatement || gotoStatement is GotoDefaultStatement || gotoStatement is GotoCaseStatement || gotoStatement is BreakStatement || gotoStatement is ContinueStatement); + Statement targetStatement = edge.To.PreviousStatement ?? edge.To.NextStatement; + if (gotoStatement.Parent == targetStatement.Parent) + continue; + HashSet targetParentTryCatch = new HashSet(targetStatement.Ancestors.OfType()); + for (AstNode node = gotoStatement.Parent; node != null; node = node.Parent) { + TryCatchStatement leftTryCatch = node as TryCatchStatement; + if (leftTryCatch != null) { + if (targetParentTryCatch.Contains(leftTryCatch)) + break; + if (!leftTryCatch.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(leftTryCatch); + } + } + } + } + + #region Create*Node + ControlFlowNode CreateStartNode(Statement statement) + { + if (statement.IsNull) + return null; + ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode); + nodes.Add(node); + return node; + } + + ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true) + { + ControlFlowNode node = CreateNode(null, statement, type); + if (addToNodeList) + nodes.Add(node); + return node; + } + + ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true) + { + Statement nextStatement; + if (statement == rootStatement) { + nextStatement = null; + } else { + // Find the next statement in the same role: + AstNode next = statement; + do { + next = next.NextSibling; + } while (next != null && next.Role != statement.Role); + nextStatement = next as Statement; + } + ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode; + ControlFlowNode node = CreateNode(statement, nextStatement, type); + if (addToNodeList) + nodes.Add(node); + return node; + } + #endregion + + #region Constant evaluation + /// + /// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b"). + /// + public bool EvaluateOnlyPrimitiveConstants { get; set; } + + /// + /// Evaluates an expression. + /// + /// The constant value of the expression; or null if the expression is not a constant. + ResolveResult EvaluateConstant(Expression expr) + { + if (expr.IsNull) + return null; + if (EvaluateOnlyPrimitiveConstants) { + if (!(expr is PrimitiveExpression || expr is NullReferenceExpression)) + return null; + } + return resolver(expr, cancellationToken); + } + + /// + /// Evaluates an expression. + /// + /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. + bool? EvaluateCondition(Expression expr) + { + ResolveResult rr = EvaluateConstant(expr); + if (rr != null && rr.IsCompileTimeConstant) + return rr.ConstantValue as bool?; + else + return null; + } + + bool AreEqualConstants(ResolveResult c1, ResolveResult c2) + { + if (c1 == null || c2 == null || !c1.IsCompileTimeConstant || !c2.IsCompileTimeConstant) + return false; + CSharpResolver r = new CSharpResolver(typeResolveContext); + ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2); + return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true; + } + #endregion + + sealed class NodeCreationVisitor : DepthFirstAstVisitor + { + // 'data' parameter: input control flow node (start of statement being visited) + // Return value: result control flow node (end of statement being visited) + + internal ControlFlowGraphBuilder builder; + Stack breakTargets = new Stack(); + Stack continueTargets = new Stack(); + List gotoCaseOrDefault = new List(); + + internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal) + { + if (from == null || to == null) + return null; + ControlFlowEdge edge = builder.CreateEdge(from, to, type); + from.Outgoing.Add(edge); + to.Incoming.Add(edge); + return edge; + } + + /// + /// Creates an end node for stmt and connects from with the new node. + /// + ControlFlowNode CreateConnectedEndNode(Statement stmt, ControlFlowNode from) + { + ControlFlowNode newNode = builder.CreateEndNode(stmt); + Connect(from, newNode); + return newNode; + } + + protected override ControlFlowNode VisitChildren(AstNode node, ControlFlowNode data) + { + // We have overrides for all possible statements and should visit statements only. + throw new NotSupportedException(); + } + + public override ControlFlowNode VisitBlockStatement(BlockStatement blockStatement, ControlFlowNode data) + { + // C# 4.0 spec: §8.2 Blocks + ControlFlowNode childNode = HandleStatementList(blockStatement.Statements, data); + return CreateConnectedEndNode(blockStatement, childNode); + } + + ControlFlowNode HandleStatementList(AstNodeCollection statements, ControlFlowNode source) + { + ControlFlowNode childNode = null; + foreach (Statement stmt in statements) { + if (childNode == null) { + childNode = builder.CreateStartNode(stmt); + if (source != null) + Connect(source, childNode); + } + Debug.Assert(childNode.NextStatement == stmt); + childNode = stmt.AcceptVisitor(this, childNode); + Debug.Assert(childNode.PreviousStatement == stmt); + } + return childNode ?? source; + } + + public override ControlFlowNode VisitEmptyStatement(EmptyStatement emptyStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(emptyStatement, data); + } + + public override ControlFlowNode VisitLabelStatement(LabelStatement labelStatement, ControlFlowNode data) + { + ControlFlowNode end = CreateConnectedEndNode(labelStatement, data); + builder.labels[labelStatement.Label] = end; + return end; + } + + public override ControlFlowNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(variableDeclarationStatement, data); + } + + public override ControlFlowNode VisitExpressionStatement(ExpressionStatement expressionStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(expressionStatement, data); + } + + public override ControlFlowNode VisitIfElseStatement(IfElseStatement ifElseStatement, ControlFlowNode data) + { + bool? cond = builder.EvaluateCondition(ifElseStatement.Condition); + + ControlFlowNode trueBegin = builder.CreateStartNode(ifElseStatement.TrueStatement); + if (cond != false) + Connect(data, trueBegin, ControlFlowEdgeType.ConditionTrue); + ControlFlowNode trueEnd = ifElseStatement.TrueStatement.AcceptVisitor(this, trueBegin); + + ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement); + if (cond != true) + Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse); + ControlFlowNode falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin); + // (if no else statement exists, both falseBegin and falseEnd will be null) + + ControlFlowNode end = builder.CreateEndNode(ifElseStatement); + Connect(trueEnd, end); + if (falseEnd != null) { + Connect(falseEnd, end); + } else if (cond != true) { + Connect(data, end, ControlFlowEdgeType.ConditionFalse); + } + return end; + } + + public override ControlFlowNode VisitSwitchStatement(SwitchStatement switchStatement, ControlFlowNode data) + { + // First, figure out which switch section will get called (if the expression is constant): + ResolveResult constant = builder.EvaluateConstant(switchStatement.Expression); + SwitchSection defaultSection = null; + SwitchSection sectionMatchedByConstant = null; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (label.Expression.IsNull) { + defaultSection = section; + } else if (constant != null && constant.IsCompileTimeConstant) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(constant, labelConstant)) + sectionMatchedByConstant = section; + } + } + } + if (constant != null && constant.IsCompileTimeConstant && sectionMatchedByConstant == null) + sectionMatchedByConstant = defaultSection; + + int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count; + List sectionStartNodes = new List(); + + ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false); + breakTargets.Push(end); + foreach (SwitchSection section in switchStatement.SwitchSections) { + int sectionStartNodeID = builder.nodes.Count; + if (constant == null || !constant.IsCompileTimeConstant || section == sectionMatchedByConstant) { + HandleStatementList(section.Statements, data); + } else { + // This section is unreachable: pass null to HandleStatementList. + HandleStatementList(section.Statements, null); + } + // Don't bother connecting the ends of the sections: the 'break' statement takes care of that. + + // Store the section start node for 'goto case' statements. + sectionStartNodes.Add(sectionStartNodeID < builder.nodes.Count ? builder.nodes[sectionStartNodeID] : null); + } + breakTargets.Pop(); + if (defaultSection == null && sectionMatchedByConstant == null) { + Connect(data, end); + } + + if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) { + // Resolve 'goto case' statements: + for (int i = gotoCaseOrDefaultInOuterScope; i < gotoCaseOrDefault.Count; i++) { + ControlFlowNode gotoCaseNode = gotoCaseOrDefault[i]; + GotoCaseStatement gotoCaseStatement = gotoCaseNode.NextStatement as GotoCaseStatement; + ResolveResult gotoCaseConstant = null; + if (gotoCaseStatement != null) { + gotoCaseConstant = builder.EvaluateConstant(gotoCaseStatement.LabelExpression); + } + int targetSectionIndex = -1; + int currentSectionIndex = 0; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (gotoCaseStatement != null) { + // goto case + if (!label.Expression.IsNull) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(gotoCaseConstant, labelConstant)) + targetSectionIndex = currentSectionIndex; + } + } else { + // goto default + if (label.Expression.IsNull) + targetSectionIndex = currentSectionIndex; + } + } + currentSectionIndex++; + } + if (targetSectionIndex >= 0 && sectionStartNodes[targetSectionIndex] != null) + Connect(gotoCaseNode, sectionStartNodes[targetSectionIndex], ControlFlowEdgeType.Jump); + else + Connect(gotoCaseNode, end, ControlFlowEdgeType.Jump); + } + gotoCaseOrDefault.RemoveRange(gotoCaseOrDefaultInOuterScope, gotoCaseOrDefault.Count - gotoCaseOrDefaultInOuterScope); + } + + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, ControlFlowNode data) + { + gotoCaseOrDefault.Add(data); + return builder.CreateEndNode(gotoCaseStatement); + } + + public override ControlFlowNode VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, ControlFlowNode data) + { + gotoCaseOrDefault.Add(data); + return builder.CreateEndNode(gotoDefaultStatement); + } + + public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data) + { + // while (cond) { embeddedStmt; } + ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition); + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + Connect(data, conditionNode); + + bool? cond = builder.EvaluateCondition(whileStatement.Condition); + ControlFlowNode bodyStart = builder.CreateStartNode(whileStatement.EmbeddedStatement); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + ControlFlowNode bodyEnd = whileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, conditionNode); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + breakTargets.Pop(); + continueTargets.Pop(); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data) + { + // do { embeddedStmt; } while(cond); + ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false); + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + ControlFlowNode bodyStart = builder.CreateStartNode(doWhileStatement.EmbeddedStatement); + Connect(data, bodyStart); + ControlFlowNode bodyEnd = doWhileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, conditionNode); + + bool? cond = builder.EvaluateCondition(doWhileStatement.Condition); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + breakTargets.Pop(); + continueTargets.Pop(); + builder.nodes.Add(conditionNode); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitForStatement(ForStatement forStatement, ControlFlowNode data) + { + data = HandleStatementList(forStatement.Initializers, data); + // for (initializers ; cond; iterators) { embeddedStmt; } + ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition); + Connect(data, conditionNode); + + int iteratorStartNodeID = builder.nodes.Count; + ControlFlowNode iteratorEnd = HandleStatementList(forStatement.Iterators, null); + ControlFlowNode iteratorStart; + if (iteratorEnd != null) { + iteratorStart = builder.nodes[iteratorStartNodeID]; + Connect(iteratorEnd, conditionNode); + } else { + iteratorStart = conditionNode; + } + + breakTargets.Push(end); + continueTargets.Push(iteratorStart); + + ControlFlowNode bodyStart = builder.CreateStartNode(forStatement.EmbeddedStatement); + ControlFlowNode bodyEnd = forStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, iteratorStart); + + breakTargets.Pop(); + continueTargets.Pop(); + + bool? cond = forStatement.Condition.IsNull ? true : builder.EvaluateCondition(forStatement.Condition); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + builder.nodes.Add(end); + return end; + } + + ControlFlowNode HandleEmbeddedStatement(Statement embeddedStatement, ControlFlowNode source) + { + if (embeddedStatement == null || embeddedStatement.IsNull) + return source; + ControlFlowNode bodyStart = builder.CreateStartNode(embeddedStatement); + if (source != null) + Connect(source, bodyStart); + return embeddedStatement.AcceptVisitor(this, bodyStart); + } + + public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data) + { + // foreach (...) { embeddedStmt } + ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition); + Connect(data, conditionNode); + + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + ControlFlowNode bodyEnd = HandleEmbeddedStatement(foreachStatement.EmbeddedStatement, conditionNode); + Connect(bodyEnd, conditionNode); + + breakTargets.Pop(); + continueTargets.Pop(); + + Connect(conditionNode, end); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitBreakStatement(BreakStatement breakStatement, ControlFlowNode data) + { + if (breakTargets.Count > 0) + Connect(data, breakTargets.Peek(), ControlFlowEdgeType.Jump); + return builder.CreateEndNode(breakStatement); + } + + public override ControlFlowNode VisitContinueStatement(ContinueStatement continueStatement, ControlFlowNode data) + { + if (continueTargets.Count > 0) + Connect(data, continueTargets.Peek(), ControlFlowEdgeType.Jump); + return builder.CreateEndNode(continueStatement); + } + + public override ControlFlowNode VisitGotoStatement(GotoStatement gotoStatement, ControlFlowNode data) + { + builder.gotoStatements.Add(data); + return builder.CreateEndNode(gotoStatement); + } + + public override ControlFlowNode VisitReturnStatement(ReturnStatement returnStatement, ControlFlowNode data) + { + return builder.CreateEndNode(returnStatement); // end not connected with data + } + + public override ControlFlowNode VisitThrowStatement(ThrowStatement throwStatement, ControlFlowNode data) + { + return builder.CreateEndNode(throwStatement); // end not connected with data + } + + public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data) + { + ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false); + var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end); + if (!tryCatchStatement.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(tryCatchStatement); + foreach (CatchClause cc in tryCatchStatement.CatchClauses) { + edge = Connect(HandleEmbeddedStatement(cc.Body, data), end); + if (!tryCatchStatement.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(tryCatchStatement); + } + if (!tryCatchStatement.FinallyBlock.IsNull) { + // Don't connect the end of the try-finally block to anything. + // Consumers of the CFG will have to special-case try-finally. + HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data); + } + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitCheckedStatement(CheckedStatement checkedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(checkedStatement.Body, data); + return CreateConnectedEndNode(checkedStatement, bodyEnd); + } + + public override ControlFlowNode VisitUncheckedStatement(UncheckedStatement uncheckedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(uncheckedStatement.Body, data); + return CreateConnectedEndNode(uncheckedStatement, bodyEnd); + } + + public override ControlFlowNode VisitLockStatement(LockStatement lockStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(lockStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(lockStatement, bodyEnd); + } + + public override ControlFlowNode VisitUsingStatement(UsingStatement usingStatement, ControlFlowNode data) + { + data = HandleEmbeddedStatement(usingStatement.ResourceAcquisition as Statement, data); + ControlFlowNode bodyEnd = HandleEmbeddedStatement(usingStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(usingStatement, bodyEnd); + } + + public override ControlFlowNode VisitYieldReturnStatement(YieldReturnStatement yieldStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(yieldStatement, data); + } + + public override ControlFlowNode VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, ControlFlowNode data) + { + return builder.CreateEndNode(yieldBreakStatement); // end not connected with data + } + + public override ControlFlowNode VisitUnsafeStatement(UnsafeStatement unsafeStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(unsafeStatement.Body, data); + return CreateConnectedEndNode(unsafeStatement, bodyEnd); + } + + public override ControlFlowNode VisitFixedStatement(FixedStatement fixedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(fixedStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(fixedStatement, bodyEnd); + } + } + + /// + /// Debugging helper that exports a control flow graph. + /// + public static GraphVizGraph ExportGraph(IList nodes) + { + GraphVizGraph g = new GraphVizGraph(); + GraphVizNode[] n = new GraphVizNode[nodes.Count]; + Dictionary dict = new Dictionary(); + for (int i = 0; i < n.Length; i++) { + dict.Add(nodes[i], i); + n[i] = new GraphVizNode(i); + string name = "#" + i + " = "; + switch (nodes[i].Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + name += nodes[i].NextStatement.DebugToString(); + break; + case ControlFlowNodeType.EndNode: + name += "End of " + nodes[i].PreviousStatement.DebugToString(); + break; + case ControlFlowNodeType.LoopCondition: + name += "Condition in " + nodes[i].NextStatement.DebugToString(); + break; + default: + name += "?"; + break; + } + n[i].label = name; + g.AddNode(n[i]); + } + for (int i = 0; i < n.Length; i++) { + foreach (ControlFlowEdge edge in nodes[i].Outgoing) { + GraphVizEdge ge = new GraphVizEdge(i, dict[edge.To]); + if (edge.IsLeavingTryFinally) + ge.style = "dashed"; + switch (edge.Type) { + case ControlFlowEdgeType.ConditionTrue: + ge.color = "green"; + break; + case ControlFlowEdgeType.ConditionFalse: + ge.color = "red"; + break; + case ControlFlowEdgeType.Jump: + ge.color = "blue"; + break; + } + g.AddEdge(ge); + } + } + return g; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs b/ICSharpCode.Decompiler/CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs new file mode 100644 index 000000000..a412cba3a --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs @@ -0,0 +1,157 @@ +// +// LovalVariableDeclarationSpace.cs +// +// Author: +// Simon Lindgren +// +// Copyright (c) 2013 Simon Lindgren +// +// 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 ICSharpCode.NRefactory.Utils; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents a declaration space. (§3.3) + /// + public class LocalDeclarationSpace + { + /// + /// Maps from variable name to the declarations in this declaration space. + /// + /// + /// This maps from variable name + /// + MultiDictionary declarations = new MultiDictionary (); + + public LocalDeclarationSpace() + { + Children = new List (); + } + + /// + /// The child declaration spaces. + /// + public IList Children { + get; + private set; + } + + /// + /// The parent declaration space. + /// + /// The parent. + public LocalDeclarationSpace Parent { + get; + private set; + } + + /// + /// The names declared in this declaration space, excluding child spaces. + /// + /// The declared names. + public ICollection DeclaredNames { + get { + return declarations.Keys; + } + } + + /// + /// Get all nodes declaring the name specified in . + /// + /// The declaring nodes. + /// The declaration name. + public IEnumerable GetNameDeclarations(string name) + { + return declarations [name].Concat(Children.SelectMany(child => child.GetNameDeclarations(name))); + } + + /// + /// Adds a child declaration space. + /// + /// The to add. + public void AddChildSpace(LocalDeclarationSpace child) + { + if (child == null) + throw new ArgumentNullException("child"); + if (Children.Contains(child)) + throw new InvalidOperationException("the child was already added"); + + Children.Add(child); + child.Parent = this; + } + + /// + /// Adds a new declaration to the declaration space. + /// + /// The name of the declared variable. + /// A node associated with the declaration. + public void AddDeclaration(string name, AstNode node) + { + if (name == null) + throw new ArgumentNullException("name"); + if (node == null) + throw new ArgumentNullException("node"); + declarations.Add(name, node); + } + + /// + /// Determines if the name exists in the this declaration space. + /// + /// true, if the name specified in is used in this variable declaration space, false otherwise. + /// The name to look for. + /// When true, child declaration spaces are included in the search. + public bool ContainsName(string name, bool includeChildren) + { + if (name == null) + throw new ArgumentNullException("name"); + + if (declarations.Keys.Contains(name)) + return true; + return includeChildren && Children.Any(child => child.ContainsName(name, true)); + } + + /// + /// Determines whether the name specified in is used in surrouding code. + /// + /// true if the name is used, false otherwise. + /// The name to check. + /// + /// Contrary to , this method also checks parent declaration spaces + /// for name conflicts. Typically, this will be the right method to use when determining if a name can be used. + /// + public bool IsNameUsed(string name) + { + if (name == null) + throw new ArgumentNullException("name"); + + return IsNameUsedBySelfOrParent(name) || Children.Any(child => child.ContainsName(name, true)); + } + + bool IsNameUsedBySelfOrParent(string name) + { + if (declarations.Keys.Contains(name)) + return true; + return Parent != null && Parent.IsNameUsedBySelfOrParent(name); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs b/ICSharpCode.Decompiler/CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs new file mode 100644 index 000000000..08389a413 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs @@ -0,0 +1,138 @@ +// +// LocalDeclarationSpaceVisitor.cs +// +// Author: +// Simon Lindgren +// +// Copyright (c) 2013 Simon Lindgren +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + public class LocalDeclarationSpaceVisitor : DepthFirstAstVisitor + { + LocalDeclarationSpace currentDeclarationSpace; + Dictionary nodeDeclarationSpaces = new Dictionary(); + + public LocalDeclarationSpace GetDeclarationSpace(AstNode node) + { + if (node == null) + throw new ArgumentNullException("node"); + while (node != null) { + LocalDeclarationSpace declarationSpace; + if (nodeDeclarationSpaces.TryGetValue(node, out declarationSpace)) + return declarationSpace; + node = node.Parent; + } + return null; + } + + #region Visitor + + void AddDeclaration(string name, AstNode node) + { + if (currentDeclarationSpace != null) + currentDeclarationSpace.AddDeclaration(name, node); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + AddDeclaration(variableInitializer.Name, variableInitializer); + base.VisitVariableInitializer(variableInitializer); + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + AddDeclaration(parameterDeclaration.Name, parameterDeclaration); + base.VisitParameterDeclaration(parameterDeclaration); + } + + void VisitNewDeclarationSpace(AstNode node) + { + var oldDeclarationSpace = currentDeclarationSpace; + currentDeclarationSpace = new LocalDeclarationSpace(); + if (oldDeclarationSpace != null) + oldDeclarationSpace.AddChildSpace(currentDeclarationSpace); + + VisitChildren(node); + + nodeDeclarationSpaces.Add(node, currentDeclarationSpace); + currentDeclarationSpace = oldDeclarationSpace; + } + + #region Declaration space creating nodes + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + VisitNewDeclarationSpace(methodDeclaration); + } + + public override void VisitBlockStatement(BlockStatement blockStatement) + { + VisitNewDeclarationSpace(blockStatement); + } + + public override void VisitSwitchStatement(SwitchStatement switchStatement) + { + VisitNewDeclarationSpace(switchStatement); + } + + public override void VisitForeachStatement(ForeachStatement foreachStatement) + { + AddDeclaration(foreachStatement.VariableName, foreachStatement); + VisitNewDeclarationSpace(foreachStatement); + } + + public override void VisitForStatement(ForStatement forStatement) + { + VisitNewDeclarationSpace(forStatement); + } + + public override void VisitUsingStatement(UsingStatement usingStatement) + { + VisitNewDeclarationSpace(usingStatement); + } + + public override void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + VisitNewDeclarationSpace(lambdaExpression); + } + + public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + VisitNewDeclarationSpace(anonymousMethodExpression); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + AddDeclaration(eventDeclaration.Name, eventDeclaration); + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + VisitNewDeclarationSpace(eventDeclaration); + } + + #endregion + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/ICSharpCode.Decompiler/CSharp/Analysis/DefiniteAssignmentAnalysis.cs new file mode 100644 index 000000000..5c180977d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -0,0 +1,753 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents the definite assignment status of a variable at a specific location. + /// + public enum DefiniteAssignmentStatus + { + /// + /// The variable might be assigned or unassigned. + /// + PotentiallyAssigned, + /// + /// The variable is definitely assigned. + /// + DefinitelyAssigned, + /// + /// The variable is definitely assigned iff the expression results in the value 'true'. + /// + AssignedAfterTrueExpression, + /// + /// The variable is definitely assigned iff the expression results in the value 'false'. + /// + AssignedAfterFalseExpression, + /// + /// The code is unreachable. + /// + CodeUnreachable + } + + /// + /// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment) + /// + public class DefiniteAssignmentAnalysis + { + sealed class DefiniteAssignmentNode : ControlFlowNode + { + public int Index; + public DefiniteAssignmentStatus NodeStatus; + + public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + : base(previousStatement, nextStatement, type) + { + } + } + + sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder + { + protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + return new DefiniteAssignmentNode(previousStatement, nextStatement, type); + } + } + + readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor(); + readonly List allNodes = new List(); + readonly Dictionary beginNodeDict = new Dictionary(); + readonly Dictionary endNodeDict = new Dictionary(); + readonly Dictionary conditionNodeDict = new Dictionary(); + readonly Func resolve; + Dictionary edgeStatus = new Dictionary(); + + string variableName; + List unassignedVariableUses = new List(); + int analyzedRangeStart, analyzedRangeEnd; + CancellationToken analysisCancellationToken; + + Queue nodesWithModifiedInput = new Queue(); + + public DefiniteAssignmentAnalysis(Statement rootStatement, Func resolve, CSharpTypeResolveContext context, CancellationToken cancellationToken) + { + if (rootStatement == null) + throw new ArgumentNullException("rootStatement"); + if (resolve == null) + throw new ArgumentNullException("resolve"); + this.resolve = resolve; + + visitor.analysis = this; + DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder(); + if (context.Compilation.MainAssembly.UnresolvedAssembly is MinimalCorlib) { + cfgBuilder.EvaluateOnlyPrimitiveConstants = true; + } + allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolve, context, cancellationToken).Cast()); + for (int i = 0; i < allNodes.Count; i++) { + DefiniteAssignmentNode node = allNodes[i]; + node.Index = i; // assign numbers to the nodes + if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) { + // Anonymous methods have separate control flow graphs, but we also need to analyze those. + // Iterate backwards so that anonymous methods are inserted in the correct order + for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) { + InsertAnonymousMethods(i + 1, child, cfgBuilder, context, cancellationToken); + } + } + // Now register the node in the dictionaries: + if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) + beginNodeDict.Add(node.NextStatement, node); + if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode) + endNodeDict.Add(node.PreviousStatement, node); + if (node.Type == ControlFlowNodeType.LoopCondition) + conditionNodeDict.Add(node.NextStatement, node); + } + // Verify that we created nodes for all statements: + Debug.Assert(!rootStatement.DescendantsAndSelf.OfType().Except(allNodes.Select(n => n.NextStatement)).Any()); + // Verify that we put all nodes into the dictionaries: + Debug.Assert(rootStatement.DescendantsAndSelf.OfType().All(stmt => beginNodeDict.ContainsKey(stmt))); + Debug.Assert(rootStatement.DescendantsAndSelf.OfType().All(stmt => endNodeDict.ContainsKey(stmt))); + + this.analyzedRangeStart = 0; + this.analyzedRangeEnd = allNodes.Count - 1; + } + + void InsertAnonymousMethods(int insertPos, AstNode node, ControlFlowGraphBuilder cfgBuilder, CSharpTypeResolveContext context, CancellationToken cancellationToken) + { + // Ignore any statements, as those have their own ControlFlowNode and get handled separately + if (node is Statement) + return; + AnonymousMethodExpression ame = node as AnonymousMethodExpression; + if (ame != null) { + allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolve, context, cancellationToken).Cast()); + return; + } + LambdaExpression lambda = node as LambdaExpression; + if (lambda != null && lambda.Body is Statement) { + allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolve, context, cancellationToken).Cast()); + return; + } + // Descend into child expressions + // Iterate backwards so that anonymous methods are inserted in the correct order + for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) { + InsertAnonymousMethods(insertPos, child, cfgBuilder, context, cancellationToken); + } + } + + /// + /// Gets the unassigned usages of the previously analyzed variable. + /// + public IList UnassignedVariableUses { + get { + return unassignedVariableUses.AsReadOnly(); + } + } + + /// + /// Sets the range of statements to be analyzed. + /// This method can be used to restrict the analysis to only a part of the method. + /// Only the control flow paths that are fully contained within the selected part will be analyzed. + /// + /// By default, both 'start' and 'end' are inclusive. + public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true) + { + var dictForStart = startInclusive ? beginNodeDict : endNodeDict; + var dictForEnd = endInclusive ? endNodeDict : beginNodeDict; + Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end)); + int startIndex = dictForStart[start].Index; + int endIndex = dictForEnd[end].Index; + if (startIndex > endIndex) + throw new ArgumentException("The start statement must be lexically preceding the end statement"); + this.analyzedRangeStart = startIndex; + this.analyzedRangeEnd = endIndex; + } + + public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned, CancellationToken cancellationToken = default(CancellationToken)) + { + this.analysisCancellationToken = cancellationToken; + this.variableName = variable; + try { + // Reset the status: + unassignedVariableUses.Clear(); + foreach (DefiniteAssignmentNode node in allNodes) { + node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable; + foreach (ControlFlowEdge edge in node.Outgoing) + edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable; + } + + ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus); + // Iterate as long as the input status of some nodes is changing: + while (nodesWithModifiedInput.Count > 0) { + DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue(); + DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable; + foreach (ControlFlowEdge edge in node.Incoming) { + inputStatus = MergeStatus(inputStatus, edgeStatus[edge]); + } + ChangeNodeStatus(node, inputStatus); + } + } finally { + this.analysisCancellationToken = CancellationToken.None; + this.variableName = null; + } + } + + public DefiniteAssignmentStatus GetStatusBefore(Statement statement) + { + return beginNodeDict[statement].NodeStatus; + } + + public DefiniteAssignmentStatus GetStatusAfter(Statement statement) + { + return endNodeDict[statement].NodeStatus; + } + + public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement) + { + return conditionNodeDict[statement].NodeStatus; + } + + /// + /// Exports the CFG. This method is intended to help debugging issues related to definite assignment. + /// + public GraphVizGraph ExportGraph() + { + GraphVizGraph g = new GraphVizGraph(); + g.Title = "DefiniteAssignment - " + variableName; + for (int i = 0; i < allNodes.Count; i++) { + string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine; + switch (allNodes[i].Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + name += allNodes[i].NextStatement.ToString(); + break; + case ControlFlowNodeType.EndNode: + name += "End of " + allNodes[i].PreviousStatement.ToString(); + break; + case ControlFlowNodeType.LoopCondition: + name += "Condition in " + allNodes[i].NextStatement.ToString(); + break; + default: + name += allNodes[i].Type.ToString(); + break; + } + g.AddNode(new GraphVizNode(i) { label = name }); + foreach (ControlFlowEdge edge in allNodes[i].Outgoing) { + GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index); + if (edgeStatus.Count > 0) + ge.label = edgeStatus[edge].ToString(); + if (edge.IsLeavingTryFinally) + ge.style = "dashed"; + switch (edge.Type) { + case ControlFlowEdgeType.ConditionTrue: + ge.color = "green"; + break; + case ControlFlowEdgeType.ConditionFalse: + ge.color = "red"; + break; + case ControlFlowEdgeType.Jump: + ge.color = "blue"; + break; + } + g.AddEdge(ge); + } + } + return g; + } + + static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b) + { + // The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable. + // The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable. + // The result will be Unreachable if all incoming edges are unreachable. + // Otherwise, the result will be PotentiallyAssigned. + + if (a == b) + return a; + else if (a == DefiniteAssignmentStatus.CodeUnreachable) + return b; + else if (b == DefiniteAssignmentStatus.CodeUnreachable) + return a; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } + + void ChangeNodeStatus (DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus) + { + if (node.NodeStatus == inputStatus) + return; + node.NodeStatus = inputStatus; + DefiniteAssignmentStatus outputStatus; + switch (node.Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + if (node.NextStatement is IfElseStatement) { + // Handle if-else as a condition node + goto case ControlFlowNodeType.LoopCondition; + } + if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) { + // There isn't any way to un-assign variables, so we don't have to check the expression + // if the status already is definitely assigned. + outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + outputStatus = CleanSpecialValues (node.NextStatement.AcceptVisitor (visitor, inputStatus)); + } + break; + case ControlFlowNodeType.EndNode: + outputStatus = inputStatus; + if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole + && (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned)) { + TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent; + // Changing the status on a finally block potentially changes the status of all edges leaving that finally block: + foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) { + if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains (tryFinally)) { + DefiniteAssignmentStatus s = edgeStatus [edge]; + if (s == DefiniteAssignmentStatus.PotentiallyAssigned) { + ChangeEdgeStatus (edge, outputStatus); + } + } + } + } + break; + case ControlFlowNodeType.LoopCondition: + ForeachStatement foreachStmt = node.NextStatement as ForeachStatement; + if (foreachStmt != null) { + outputStatus = CleanSpecialValues (foreachStmt.InExpression.AcceptVisitor (visitor, inputStatus)); + if (foreachStmt.VariableName == this.variableName) + outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; + break; + } else { + Debug.Assert (node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement); + Expression condition = node.NextStatement.GetChildByRole (Roles.Condition); + if (condition.IsNull) + outputStatus = inputStatus; + else + outputStatus = condition.AcceptVisitor(visitor, inputStatus); + foreach (ControlFlowEdge edge in node.Outgoing) { + if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { + ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); + } else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { + ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); + } else { + ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus)); + } + } + return; + } + default: + throw new InvalidOperationException(); + } + foreach (ControlFlowEdge edge in node.Outgoing) { + ChangeEdgeStatus(edge, outputStatus); + } + } + + void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus) + { + DefiniteAssignmentStatus oldStatus = edgeStatus[edge]; + if (oldStatus == newStatus) + return; + // Ensure that status can cannot change back to CodeUnreachable after it once was reachable. + // Also, don't ever use AssignedAfter... for statements. + if (newStatus == DefiniteAssignmentStatus.CodeUnreachable + || newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression + || newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + { + throw new InvalidOperationException(); + } + // Note that the status can change from DefinitelyAssigned + // back to PotentiallyAssigned as unreachable input edges are + // discovered to be reachable. + + edgeStatus[edge] = newStatus; + DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To; + if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) { + // TODO: potential optimization: visit previously unreachable nodes with higher priority + // (e.g. use Deque and enqueue previously unreachable nodes at the front, but + // other nodes at the end) + nodesWithModifiedInput.Enqueue(targetNode); + } + } + + /// + /// Evaluates an expression. + /// + /// The constant value of the expression; or null if the expression is not a constant. + ResolveResult EvaluateConstant(Expression expr) + { + return resolve(expr, analysisCancellationToken); + } + + /// + /// Evaluates an expression. + /// + /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. + bool? EvaluateCondition(Expression expr) + { + ResolveResult rr = EvaluateConstant(expr); + if (rr != null && rr.IsCompileTimeConstant) + return rr.ConstantValue as bool?; + else + return null; + } + + static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status) + { + if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.PotentiallyAssigned; + else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.PotentiallyAssigned; + else + return status; + } + + sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor + { + internal DefiniteAssignmentAnalysis analysis; + + // The general approach for unknown nodes is to pass the status through all child nodes in order + protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data) + { + // the special values are valid as output only, not as input + Debug.Assert(data == CleanSpecialValues(data)); + DefiniteAssignmentStatus status = data; + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + analysis.analysisCancellationToken.ThrowIfCancellationRequested(); + + Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern + status = child.AcceptVisitor(this, status); + status = CleanSpecialValues(status); + } + return status; + } + + #region Statements + // For statements, the visitor only describes the effect of the statement itself; + // we do not consider the effect of any nested statements. + // This is done because the nested statements will be reached using the control flow graph. + + // In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements. + + public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data) + { + return data; + } + + // ExpressionStatement handled by default logic + // VariableDeclarationStatement handled by default logic + + public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data) + { + if (variableInitializer.Initializer.IsNull) { + return data; + } else { + DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data); + if (variableInitializer.Name == analysis.variableName) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else + return status; + } + } + + // IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph + + public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data) + { + return switchStatement.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node + } + + public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node + } + + public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG + } + + // Break/Continue/Goto: handled by default logic + + // ThrowStatement: handled by default logic (just visit the expression) + // ReturnStatement: handled by default logic (just visit the expression) + + public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data) + { + return data; // no special logic when entering the try-catch-finally statement + // TODO: where to put the special logic when exiting the try-finally statement? + } + + public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data) + { + return data; // assignment of the foreach loop variable is done when handling the condition node + } + + public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data) + { + if (usingStatement.ResourceAcquisition is Expression) + return usingStatement.ResourceAcquisition.AcceptVisitor(this, data); + else + return data; // don't handle resource acquisition statements, as those are connected in the control flow graph + } + + public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data) + { + return lockStatement.Expression.AcceptVisitor(this, data); + } + + // Yield statements use the default logic + + public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data) + { + DefiniteAssignmentStatus status = data; + foreach (var variable in fixedStatement.Variables) + status = variable.AcceptVisitor(this, status); + return status; + } + #endregion + + #region Expressions + public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data) + { + if (directionExpression.FieldDirection == FieldDirection.Out) { + return HandleAssignment(directionExpression.Expression, null, data); + } else { + // use default logic for 'ref' + return VisitChildren(directionExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data) + { + if (assignmentExpression.Operator == AssignmentOperatorType.Assign) { + return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data); + } else { + // use default logic for compound assignment operators + return VisitChildren(assignmentExpression, data); + } + } + + DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus) + { + IdentifierExpression ident = left as IdentifierExpression; + if (ident != null && ident.Identifier == analysis.variableName) { + // right==null is special case when handling 'out' expressions + if (right != null) + right.AcceptVisitor(this, initialStatus); + return DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus); + if (right != null) + status = right.AcceptVisitor(this, CleanSpecialValues(status)); + return CleanSpecialValues(status); + } + } + + public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data) + { + // Don't use the default logic here because we don't want to clean up the special values. + return parenthesizedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data) + { + return checkedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data) + { + return uncheckedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data) + { + if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) { + // Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler) + bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left); + if (cond == true) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally + else if (cond == false) + return data; // right operand never gets evaluated + // C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions + DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data); + DefiniteAssignmentStatus beforeRight; + if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned; + else + beforeRight = afterLeft; + DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight); + if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) { + // C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions + bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left); + if (cond == false) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally + else if (cond == true) + return data; // right operand never gets evaluated + DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data); + DefiniteAssignmentStatus beforeRight; + if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned; + else + beforeRight = afterLeft; + DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight); + if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + // C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions + ResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left); + if (crr != null && crr.IsCompileTimeConstant && crr.ConstantValue == null) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); + DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data)); + binaryOperatorExpression.Right.AcceptVisitor(this, status); + return status; + } else { + // use default logic for other operators + return VisitChildren(binaryOperatorExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data) + { + if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) { + // C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions + DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data); + if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else + return status; + } else { + // use default logic for other operators + return VisitChildren(unaryOperatorExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data) + { + // C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions + bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition); + if (cond == true) { + return conditionalExpression.TrueExpression.AcceptVisitor(this, data); + } else if (cond == false) { + return conditionalExpression.FalseExpression.AcceptVisitor(this, data); + } else { + DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data); + + DefiniteAssignmentStatus beforeTrue, beforeFalse; + if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { + beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned; + beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { + beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned; + beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + beforeTrue = afterCondition; + beforeFalse = afterCondition; + } + + DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue); + DefiniteAssignmentStatus afterFalse = conditionalExpression.FalseExpression.AcceptVisitor(this, beforeFalse); + return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse)); + } + } + + public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data) + { + BlockStatement body = anonymousMethodExpression.Body; + analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data); + return data; + } + + public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data) + { + Statement body = lambdaExpression.Body as Statement; + if (body != null) { + analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data); + } else { + lambdaExpression.Body.AcceptVisitor(this, data); + } + return data; + } + + public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data) + { + if (data != DefiniteAssignmentStatus.DefinitelyAssigned + && identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0) + { + analysis.unassignedVariableUses.Add(identifierExpression); + } + return data; + } + #endregion + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/AstNode.cs b/ICSharpCode.Decompiler/CSharp/Ast/AstNode.cs new file mode 100644 index 000000000..213270da6 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/AstNode.cs @@ -0,0 +1,1037 @@ +// +// AstNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode, ICloneable + { + // the Root role must be available when creating the null nodes, so we can't put it in the Roles class + internal static readonly Role RootRole = new Role ("Root"); + + #region Null + public static readonly AstNode Null = new NullAstNode (); + + sealed class NullAstNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator AstNode (PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder (pattern) : null; + } + + sealed class PatternPlaceholder : AstNode, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder (PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder (this, child, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + return child.DoMatch (other, match); + } + + bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection (role, pos, match, backtrackingInfo); + } + } + #endregion + + AstNode parent; + AstNode prevSibling; + AstNode nextSibling; + AstNode firstChild; + AstNode lastChild; + + // Flags, from least significant to most significant bits: + // - Role.RoleIndexBits: role index + // - 1 bit: IsFrozen + protected uint flags = RootRole.Index; + // Derived classes may also use a few bits, + // for example Identifier uses 1 bit for IsVerbatim + + const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; + const uint frozenBit = 1u << Role.RoleIndexBits; + protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1; + + protected AstNode() + { + if (IsNull) + Freeze(); + } + + public bool IsFrozen { + get { return (flags & frozenBit) != 0; } + } + + public void Freeze() + { + if (!IsFrozen) { + for (AstNode child = firstChild; child != null; child = child.nextSibling) + child.Freeze(); + flags |= frozenBit; + } + } + + protected void ThrowIfFrozen() + { + if (IsFrozen) + throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); + } + + public abstract NodeType NodeType { + get; + } + + public virtual bool IsNull { + get { + return false; + } + } + + public virtual TextLocation StartLocation { + get { + var child = firstChild; + if (child == null) + return TextLocation.Empty; + return child.StartLocation; + } + } + + public virtual TextLocation EndLocation { + get { + var child = lastChild; + if (child == null) + return TextLocation.Empty; + return child.EndLocation; + } + } + + public DomRegion Region { + get { + return new DomRegion (StartLocation, EndLocation); + } + } + + /// + /// Gets the region from StartLocation to EndLocation for this node. + /// The file name of the region is set based on the parent SyntaxTree's file name. + /// If this node is not connected to a whole compilation, the file name will be null. + /// + public ICSharpCode.NRefactory.TypeSystem.DomRegion GetRegion() + { + var syntaxTree = (this.Ancestors.LastOrDefault() ?? this) as SyntaxTree; + string fileName = (syntaxTree != null ? syntaxTree.FileName : null); + return new ICSharpCode.NRefactory.TypeSystem.DomRegion(fileName, this.StartLocation, this.EndLocation); + } + + public AstNode Parent { + get { return parent; } + } + + public Role Role { + get { + return Role.GetByIndex(flags & roleIndexMask); + } + set { + if (value == null) + throw new ArgumentNullException("value"); + if (!value.IsValid(this)) + throw new ArgumentException("This node is not valid in the new role."); + ThrowIfFrozen(); + SetRole(value); + } + } + + internal uint RoleIndex { + get { return flags & roleIndexMask; } + } + + void SetRole(Role role) + { + flags = (flags & ~roleIndexMask) | role.Index; + } + + public AstNode NextSibling { + get { return nextSibling; } + } + + public AstNode PrevSibling { + get { return prevSibling; } + } + + public AstNode FirstChild { + get { return firstChild; } + } + + public AstNode LastChild { + get { return lastChild; } + } + + public bool HasChildren { + get { + return firstChild != null; + } + } + + public IEnumerable Children { + get { + AstNode next; + for (AstNode cur = firstChild; cur != null; cur = next) { + Debug.Assert (cur.parent == this); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.nextSibling; + yield return cur; + } + } + } + + /// + /// Gets the ancestors of this node (excluding this node itself) + /// + public IEnumerable Ancestors { + get { + for (AstNode cur = parent; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets the ancestors of this node (including this node itself) + /// + public IEnumerable AncestorsAndSelf { + get { + for (AstNode cur = this; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets all descendants of this node (excluding this node itself) in pre-order. + /// + public IEnumerable Descendants { + get { return GetDescendantsImpl(false); } + } + + /// + /// Gets all descendants of this node (including this node itself) in pre-order. + /// + public IEnumerable DescendantsAndSelf { + get { return GetDescendantsImpl(true); } + } + + static bool IsInsideRegion(DomRegion region, AstNode pos) + { + if (region.IsEmpty) + return true; + var nodeRegion = pos.Region; + return region.IntersectsWith(nodeRegion) || region.OverlapsWith(nodeRegion); + } + + public IEnumerable DescendantNodes (Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodes (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, region, descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, region, descendIntoChildren); + } + + IEnumerable GetDescendantsImpl(bool includeSelf, DomRegion region = new DomRegion (), Func descendIntoChildren = null) + { + if (includeSelf) { + if (IsInsideRegion (region, this)) + yield return this; + if (descendIntoChildren != null && !descendIntoChildren(this)) + yield break; + } + + Stack nextStack = new Stack(); + nextStack.Push(null); + AstNode pos = firstChild; + while (pos != null) { + // Remember next before yielding pos. + // This allows removing/replacing nodes while iterating through the list. + if (pos.nextSibling != null) + nextStack.Push(pos.nextSibling); + if (IsInsideRegion(region, pos)) + yield return pos; + if (pos.firstChild != null && (descendIntoChildren == null || descendIntoChildren(pos))) + pos = pos.firstChild; + else + pos = nextStack.Pop(); + } + } + + /// + /// Gets the first child with the specified role. + /// Returns the role's null object if the child is not found. + /// + public T GetChildByRole(Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + uint roleIndex = role.Index; + for (var cur = firstChild; cur != null; cur = cur.nextSibling) { + if ((cur.flags & roleIndexMask) == roleIndex) + return (T)cur; + } + return role.NullObject; + } + + public T GetParent() where T : AstNode + { + return Ancestors.OfType().FirstOrDefault(); + } + + public AstNode GetParent(Func pred) + { + return Ancestors.FirstOrDefault(pred); + } + + public AstNodeCollection GetChildrenByRole (Role role) where T : AstNode + { + return new AstNodeCollection (this, role); + } + + protected void SetChildByRole (Role role, T newChild) where T : AstNode + { + AstNode oldChild = GetChildByRole (role); + if (oldChild.IsNull) + AddChild (newChild, role); + else + oldChild.ReplaceWith (newChild); + } + + public void AddChild (T child, Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child == this) + throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); + AddChildUnsafe (child, role); + } + + public void AddChildWithExistingRole (AstNode child) + { + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child == this) + throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); + AddChildUnsafe (child, child.Role); + } + + /// + /// Adds a child without performing any safety checks. + /// + internal void AddChildUnsafe (AstNode child, Role role) + { + child.parent = this; + child.SetRole(role); + if (firstChild == null) { + lastChild = firstChild = child; + } else { + lastChild.nextSibling = child; + child.prevSibling = lastChild; + lastChild = child; + } + } + + public void InsertChildBefore (AstNode nextSibling, T child, Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + if (nextSibling == null || nextSibling.IsNull) { + AddChild (child, role); + return; + } + + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); + if (nextSibling.parent != this) + throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling"); + // No need to test for "Cannot add children to null nodes", + // as there isn't any valid nextSibling in null nodes. + InsertChildBeforeUnsafe (nextSibling, child, role); + } + + internal void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role) + { + child.parent = this; + child.SetRole(role); + child.nextSibling = nextSibling; + child.prevSibling = nextSibling.prevSibling; + + if (nextSibling.prevSibling != null) { + Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling); + nextSibling.prevSibling.nextSibling = child; + } else { + Debug.Assert (firstChild == nextSibling); + firstChild = child; + } + nextSibling.prevSibling = child; + } + + public void InsertChildAfter (AstNode prevSibling, T child, Role role) where T : AstNode + { + InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role); + } + + /// + /// Removes this node from its parent. + /// + public void Remove () + { + if (parent != null) { + ThrowIfFrozen(); + if (prevSibling != null) { + Debug.Assert (prevSibling.nextSibling == this); + prevSibling.nextSibling = nextSibling; + } else { + Debug.Assert (parent.firstChild == this); + parent.firstChild = nextSibling; + } + if (nextSibling != null) { + Debug.Assert (nextSibling.prevSibling == this); + nextSibling.prevSibling = prevSibling; + } else { + Debug.Assert (parent.lastChild == this); + parent.lastChild = prevSibling; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + } + + /// + /// Replaces this node with the new node. + /// + public void ReplaceWith (AstNode newNode) + { + if (newNode == null || newNode.IsNull) { + Remove (); + return; + } + if (newNode == this) + return; // nothing to do... + if (parent == null) { + throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + } + ThrowIfFrozen(); + // Because this method doesn't statically check the new node's type with the role, + // we perform a runtime test: + if (!this.Role.IsValid (newNode)) { + throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode"); + } + if (newNode.parent != null) { + // newNode is used within this tree? + if (newNode.Ancestors.Contains (this)) { + // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" + // enable automatic removal + newNode.Remove (); + } else { + throw new ArgumentException ("Node is already used in another tree.", "newNode"); + } + } + if (newNode.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "newNode"); + + newNode.parent = parent; + newNode.SetRole(this.Role); + newNode.prevSibling = prevSibling; + newNode.nextSibling = nextSibling; + + if (prevSibling != null) { + Debug.Assert (prevSibling.nextSibling == this); + prevSibling.nextSibling = newNode; + } else { + Debug.Assert (parent.firstChild == this); + parent.firstChild = newNode; + } + if (nextSibling != null) { + Debug.Assert (nextSibling.prevSibling == this); + nextSibling.prevSibling = newNode; + } else { + Debug.Assert (parent.lastChild == this); + parent.lastChild = newNode; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + + public AstNode ReplaceWith (Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException ("replaceFunction"); + if (parent == null) { + throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + } + AstNode oldParent = parent; + AstNode oldSuccessor = nextSibling; + Role oldRole = this.Role; + Remove (); + AstNode replacement = replaceFunction (this); + if (oldSuccessor != null && oldSuccessor.parent != oldParent) + throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?"); + if (!(replacement == null || replacement.IsNull)) { + if (replacement.parent != null) + throw new InvalidOperationException ("replace function must return the root of a tree"); + if (!oldRole.IsValid (replacement)) { + throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ())); + } + + if (oldSuccessor != null) + oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole); + else + oldParent.AddChildUnsafe (replacement, oldRole); + } + return replacement; + } + + /// + /// Clones the whole subtree starting at this AST node. + /// + /// Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned. + public AstNode Clone () + { + AstNode copy = (AstNode)MemberwiseClone (); + // First, reset the shallow pointer copies + copy.parent = null; + copy.firstChild = null; + copy.lastChild = null; + copy.prevSibling = null; + copy.nextSibling = null; + copy.flags &= ~frozenBit; // unfreeze the copy + + // Then perform a deep copy: + for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) { + copy.AddChildUnsafe (cur.Clone (), cur.Role); + } + + // Finally, clone the annotation, if necessary + copy.CloneAnnotations(); + + return copy; + } + + object ICloneable.Clone() + { + return Clone(); + } + + public abstract void AcceptVisitor (IAstVisitor visitor); + + public abstract T AcceptVisitor (IAstVisitor visitor); + + public abstract S AcceptVisitor (IAstVisitor visitor, T data); + + #region Pattern Matching + protected static bool MatchString (string pattern, string text) + { + return PatternMatching.Pattern.MatchString(pattern, text); + } + + protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match); + + bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match) + { + AstNode o = other as AstNode; + // try matching if other is null, or if other is an AstNode + return (other == null || o != null) && DoMatch (o, match); + } + + bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + AstNode o = pos as AstNode; + return (pos == null || o != null) && DoMatch (o, match); + } + + PatternMatching.INode PatternMatching.INode.NextSibling { + get { return nextSibling; } + } + + PatternMatching.INode PatternMatching.INode.FirstChild { + get { return firstChild; } + } + + #endregion + + public AstNode GetNextNode () + { + if (NextSibling != null) + return NextSibling; + if (Parent != null) + return Parent.GetNextNode (); + return null; + } + + /// + /// Gets the next node which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetNextNode (Func pred) + { + var next = GetNextNode(); + while (next != null && !pred (next)) + next = next.GetNextNode(); + return next; + } + + public AstNode GetPrevNode () + { + if (PrevSibling != null) + return PrevSibling; + if (Parent != null) + return Parent.GetPrevNode (); + return null; + } + + /// + /// Gets the previous node which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetPrevNode (Func pred) + { + var prev = GetPrevNode(); + while (prev != null && !pred (prev)) + prev = prev.GetPrevNode(); + return prev; + } + // filters all non c# nodes (comments, white spaces or pre processor directives) + public AstNode GetCSharpNodeBefore (AstNode node) + { + var n = node.PrevSibling; + while (n != null) { + if (n.Role != Roles.Comment) + return n; + n = n.GetPrevNode (); + } + return null; + } + + /// + /// Gets the next sibling which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetNextSibling (Func pred) + { + var next = NextSibling; + while (next != null && !pred (next)) + next = next.NextSibling; + return next; + } + + /// + /// Gets the next sibling which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetPrevSibling (Func pred) + { + var prev = PrevSibling; + while (prev != null && !pred (prev)) + prev = prev.PrevSibling; + return prev; + } + + #region GetNodeAt + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public AstNode GetNodeAt (int line, int column, Predicate pred = null) + { + return GetNodeAt (new TextLocation (line, column), pred); + } + + /// + /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public AstNode GetNodeAt (TextLocation location, Predicate pred = null) + { + AstNode result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location < child.EndLocation) { + if (pred == null || pred (child)) + result = child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public T GetNodeAt (int line, int column) where T : AstNode + { + return GetNodeAt (new TextLocation (line, column)); + } + + /// + /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public T GetNodeAt (TextLocation location) where T : AstNode + { + T result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location < child.EndLocation) { + if (child is T) + result = (T)child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + #endregion + + #region GetAdjacentNodeAt + /// + /// Gets the node specified by pred at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public AstNode GetAdjacentNodeAt(int line, int column, Predicate pred = null) + { + return GetAdjacentNodeAt (new TextLocation (line, column), pred); + } + + /// + /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public AstNode GetAdjacentNodeAt (TextLocation location, Predicate pred = null) + { + AstNode result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location <= child.EndLocation) { + if (pred == null || pred (child)) + result = child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public T GetAdjacentNodeAt(int line, int column) where T : AstNode + { + return GetAdjacentNodeAt (new TextLocation (line, column)); + } + + /// + /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public T GetAdjacentNodeAt (TextLocation location) where T : AstNode + { + T result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location <= child.EndLocation) { + if (child is T) + result = (T)child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + #endregion + + + /// + /// Gets the node that fully contains the range from startLocation to endLocation. + /// + public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation) + { + for (AstNode child = firstChild; child != null; child = child.nextSibling) { + if (child.StartLocation <= startLocation && endLocation <= child.EndLocation) + return child.GetNodeContaining(startLocation, endLocation); + } + return this; + } + + /// + /// Returns the root nodes of all subtrees that are fully contained in the specified region. + /// + public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn) + { + return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn)); + } + + /// + /// Returns the root nodes of all subtrees that are fully contained between and (inclusive). + /// + public IEnumerable GetNodesBetween (TextLocation start, TextLocation end) + { + AstNode node = this; + while (node != null) { + AstNode next; + if (start <= node.StartLocation && node.EndLocation <= end) { + // Remember next before yielding node. + // This allows iteration to continue when the caller removes/replaces the node. + next = node.GetNextNode(); + yield return node; + } else { + if (node.EndLocation <= start) { + next = node.GetNextNode(); + } else { + next = node.FirstChild; + } + } + + if (next != null && next.StartLocation > end) + yield break; + node = next; + } + } + + /// + /// Gets the node as formatted C# output. + /// + /// + /// Formatting options. + /// + public virtual string ToString (CSharpFormattingOptions formattingOptions) + { + if (IsNull) + return ""; + var w = new StringWriter (); + AcceptVisitor (new CSharpOutputVisitor (w, formattingOptions ?? FormattingOptionsFactory.CreateMono ())); + return w.ToString (); + } + + public sealed override string ToString() + { + return ToString(null); + } + + /// + /// Returns true, if the given coordinates (line, column) are in the node. + /// + /// + /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false. + /// + public bool Contains (int line, int column) + { + return Contains (new TextLocation (line, column)); + } + + /// + /// Returns true, if the given coordinates are in the node. + /// + /// + /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false. + /// + public bool Contains (TextLocation location) + { + return this.StartLocation <= location && location < this.EndLocation; + } + + /// + /// Returns true, if the given coordinates (line, column) are in the node. + /// + /// + /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false. + /// + public bool IsInside (int line, int column) + { + return IsInside (new TextLocation (line, column)); + } + + /// + /// Returns true, if the given coordinates are in the node. + /// + /// + /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false. + /// + public bool IsInside (TextLocation location) + { + return this.StartLocation <= location && location <= this.EndLocation; + } + + public override void AddAnnotation (object annotation) + { + if (this.IsNull) + throw new InvalidOperationException ("Cannot add annotations to the null node"); + base.AddAnnotation (annotation); + } + + internal string DebugToString() + { + if (IsNull) + return "Null"; + string text = ToString(); + text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " "); + if (text.Length > 100) + return text.Substring(0, 97) + "..."; + else + return text; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/AstNodeCollection.cs b/ICSharpCode.Decompiler/CSharp/Ast/AstNodeCollection.cs new file mode 100644 index 000000000..32d08b2e4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/AstNodeCollection.cs @@ -0,0 +1,231 @@ +// Copyright (c) 2010-2013 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents the children of an AstNode that have a specific role. + /// + public class AstNodeCollection : ICollection + #if NET_4_5 + , IReadOnlyCollection + #endif + where T : AstNode + { + readonly AstNode node; + readonly Role role; + + public AstNodeCollection(AstNode node, Role role) + { + if (node == null) + throw new ArgumentNullException("node"); + if (role == null) + throw new ArgumentNullException("role"); + this.node = node; + this.role = role; + } + + public int Count { + get { + int count = 0; + uint roleIndex = role.Index; + for (AstNode cur = node.FirstChild; cur != null; cur = cur.NextSibling) { + if (cur.RoleIndex == roleIndex) + count++; + } + return count; + } + } + + public void Add(T element) + { + node.AddChild(element, role); + } + + public void AddRange(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we add the new children + // Example: collection.AddRange(collection); + if (nodes != null) { + foreach (T node in nodes.ToList()) + Add(node); + } + } + + public void AddRange(T[] nodes) + { + // Fast overload for arrays - we don't need to create a copy + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void ReplaceWith(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we call Clear() + // Example: collection.ReplaceWith(collection); + if (nodes != null) + nodes = nodes.ToList(); + Clear(); + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void MoveTo(ICollection targetCollection) + { + if (targetCollection == null) + throw new ArgumentNullException("targetCollection"); + foreach (T node in this) { + node.Remove(); + targetCollection.Add(node); + } + } + + public bool Contains(T element) + { + return element != null && element.Parent == node && element.RoleIndex == role.Index; + } + + public bool Remove(T element) + { + if (Contains(element)) { + element.Remove(); + return true; + } else { + return false; + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + foreach (T item in this) + array[arrayIndex++] = item; + } + + public void Clear() + { + foreach (T item in this) + item.Remove(); + } + + /// + /// Returns the first element for which the predicate returns true, + /// or the null node (AstNode with IsNull=true) if no such object is found. + /// + public T FirstOrNullObject(Func predicate = null) + { + foreach (T item in this) + if (predicate == null || predicate(item)) + return item; + return role.NullObject; + } + + /// + /// Returns the last element for which the predicate returns true, + /// or the null node (AstNode with IsNull=true) if no such object is found. + /// + public T LastOrNullObject(Func predicate = null) + { + T result = role.NullObject; + foreach (T item in this) + if (predicate == null || predicate(item)) + result = item; + return result; + } + + bool ICollection.IsReadOnly { + get { return false; } + } + + public IEnumerator GetEnumerator() + { + uint roleIndex = role.Index; + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.RoleIndex == roleIndex) + yield return (T)cur; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #region Equals and GetHashCode implementation + public override int GetHashCode() + { + return node.GetHashCode() ^ role.GetHashCode(); + } + + public override bool Equals(object obj) + { + AstNodeCollection other = obj as AstNodeCollection; + if (other == null) + return false; + return this.node == other.node && this.role == other.role; + } + #endregion + + internal bool DoMatch(AstNodeCollection other, Match match) + { + return Pattern.DoMatchCollection(role, node.FirstChild, other.node.FirstChild, match); + } + + public void InsertAfter(T existingItem, T newItem) + { + node.InsertChildAfter(existingItem, newItem, role); + } + + public void InsertBefore(T existingItem, T newItem) + { + node.InsertChildBefore(existingItem, newItem, role); + } + + /// + /// Applies the to all nodes in this collection. + /// + public void AcceptVisitor(IAstVisitor visitor) + { + uint roleIndex = role.Index; + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.RoleIndex == roleIndex) + cur.AcceptVisitor(visitor); + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/AstType.cs b/ICSharpCode.Decompiler/CSharp/Ast/AstType.cs new file mode 100644 index 000000000..80ca72ac9 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/AstType.cs @@ -0,0 +1,288 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A type reference in the C# AST. + /// + public abstract class AstType : AstNode + { + #region Null + public new static readonly AstType Null = new NullAstType (); + + sealed class NullAstType : AstType + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + return SpecialType.UnknownType; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator AstType(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : AstType, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder (this, child, data); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + throw new NotSupportedException(); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { return NodeType.TypeReference; } + } + + public new AstType Clone() + { + return (AstType)base.Clone(); + } + + /// + /// Gets whether this type is a SimpleType "var". + /// + public bool IsVar() + { + SimpleType st = this as SimpleType; + return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; + } + + /// + /// Create an ITypeReference for this AstType. + /// Uses the context (ancestors of this node) to determine the correct . + /// + /// + /// The resulting type reference will read the context information from the + /// : + /// For resolving type parameters, the CurrentTypeDefinition/CurrentMember is used. + /// For resolving simple names, the current namespace and usings from the CurrentUsingScope + /// (on CSharpTypeResolveContext only) is used. + /// + public ITypeReference ToTypeReference(InterningProvider interningProvider = null) + { + return ToTypeReference(GetNameLookupMode(), interningProvider); + } + + /// + /// Create an ITypeReference for this AstType. + /// + /// + /// The resulting type reference will read the context information from the + /// : + /// For resolving type parameters, the CurrentTypeDefinition/CurrentMember is used. + /// For resolving simple names, the current namespace and usings from the CurrentUsingScope + /// (on CSharpTypeResolveContext only) is used. + /// + public abstract ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null); + + /// + /// Gets the name lookup mode from the context (looking at the ancestors of this ). + /// + public NameLookupMode GetNameLookupMode() + { + AstType outermostType = this; + while (outermostType.Parent is AstType) + outermostType = (AstType)outermostType.Parent; + + if (outermostType.Parent is UsingDeclaration || outermostType.Parent is UsingAliasDeclaration) { + return NameLookupMode.TypeInUsingDeclaration; + } else if (outermostType.Role == Roles.BaseType) { + // Use BaseTypeReference for a type's base type, and for a constraint on a type. + // Do not use it for a constraint on a method. + if (outermostType.Parent is TypeDeclaration || (outermostType.Parent is Constraint && outermostType.Parent.Parent is TypeDeclaration)) + return NameLookupMode.BaseTypeReference; + } + return NameLookupMode.Type; + } + + /// + /// Creates a pointer type from this type by nesting it in a . + /// If this type already is a pointer type, this method just increases the PointerRank of the existing pointer type. + /// + public virtual AstType MakePointerType() + { + return new ComposedType { BaseType = this }.MakePointerType(); + } + + /// + /// Creates an array type from this type by nesting it in a . + /// If this type already is an array type, the additional rank is prepended to the existing array specifier list. + /// Thus, new SimpleType("T").MakeArrayType(1).MakeArrayType(2) will result in "T[,][]". + /// + public virtual AstType MakeArrayType(int rank = 1) + { + return new ComposedType { BaseType = this }.MakeArrayType(rank); + } + + /// + /// Creates a nullable type from this type by nesting it in a . + /// + public AstType MakeNullableType() + { + return new ComposedType { BaseType = this, HasNullableSpecifier = true }; + } + + /// + /// Creates a C# 7 ref type from this type by nesting it in a . + /// + public virtual AstType MakeRefType() + { + return new ComposedType { BaseType = this, HasRefSpecifier = true }; + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberReferenceExpression Member(string memberName) + { + return new TypeReferenceExpression { Type = this }.Member(memberName); + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberType MemberType(string memberName, params AstType[] typeArguments) + { + var memberType = new MemberType(this, memberName); + memberType.TypeArguments.AddRange(typeArguments); + return memberType; + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberType MemberType(string memberName, IEnumerable typeArguments) + { + var memberType = new MemberType(this, memberName); + memberType.TypeArguments.AddRange(typeArguments); + return memberType; + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, typeArguments, arguments); + } + + /// + /// Creates a simple AstType from a dotted name. + /// Does not support generics, arrays, etc. - just simple dotted names, + /// e.g. namespace names. + /// + public static AstType Create(string dottedName) + { + string[] parts = dottedName.Split('.'); + AstType type = new SimpleType(parts[0]); + for (int i = 1; i < parts.Length; i++) { + type = new MemberType(type, parts[i]); + } + return type; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/CSharpModifierToken.cs b/ICSharpCode.Decompiler/CSharp/Ast/CSharpModifierToken.cs new file mode 100644 index 000000000..1a46006f2 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/CSharpModifierToken.cs @@ -0,0 +1,218 @@ +// +// CSharpModifierToken.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class CSharpModifierToken : CSharpTokenNode + { + Modifiers modifier; + + public Modifiers Modifier { + get { return modifier; } + set { + ThrowIfFrozen(); + this.modifier = value; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + GetModifierLength (Modifier)); + } + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return GetModifierName (Modifier); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CSharpModifierToken o = other as CSharpModifierToken; + return o != null && this.modifier == o.modifier; + } + + // Not worth using a dictionary for such few elements. + // This table is sorted in the order that modifiers should be output when generating code. + static readonly Modifiers[] allModifiers = { + Modifiers.Public, Modifiers.Protected, Modifiers.Private, Modifiers.Internal, + Modifiers.New, + Modifiers.Unsafe, + Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override, + Modifiers.Readonly, Modifiers.Volatile, + Modifiers.Extern, Modifiers.Partial, Modifiers.Const, + Modifiers.Async, + Modifiers.Any + }; + + public static IEnumerable AllModifiers { + get { return allModifiers; } + } + + public CSharpModifierToken (TextLocation location, Modifiers modifier) : base (location, null) + { + this.Modifier = modifier; + } + + public static string GetModifierName(Modifiers modifier) + { + switch (modifier) { + case Modifiers.Private: + return "private"; + case Modifiers.Internal: + return "internal"; + case Modifiers.Protected: + return "protected"; + case Modifiers.Public: + return "public"; + case Modifiers.Abstract: + return "abstract"; + case Modifiers.Virtual: + return "virtual"; + case Modifiers.Sealed: + return "sealed"; + case Modifiers.Static: + return "static"; + case Modifiers.Override: + return "override"; + case Modifiers.Readonly: + return "readonly"; + case Modifiers.Const: + return "const"; + case Modifiers.New: + return "new"; + case Modifiers.Partial: + return "partial"; + case Modifiers.Extern: + return "extern"; + case Modifiers.Volatile: + return "volatile"; + case Modifiers.Unsafe: + return "unsafe"; + case Modifiers.Async: + return "async"; + case Modifiers.Any: + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return "any"; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + + public static int GetModifierLength(Modifiers modifier) + { + switch (modifier) { + case Modifiers.Private: + return "private".Length; + case Modifiers.Internal: + return "internal".Length; + case Modifiers.Protected: + return "protected".Length; + case Modifiers.Public: + return "public".Length; + case Modifiers.Abstract: + return "abstract".Length; + case Modifiers.Virtual: + return "virtual".Length; + case Modifiers.Sealed: + return "sealed".Length; + case Modifiers.Static: + return "static".Length; + case Modifiers.Override: + return "override".Length; + case Modifiers.Readonly: + return "readonly".Length; + case Modifiers.Const: + return "const".Length; + case Modifiers.New: + return "new".Length; + case Modifiers.Partial: + return "partial".Length; + case Modifiers.Extern: + return "extern".Length; + case Modifiers.Volatile: + return "volatile".Length; + case Modifiers.Unsafe: + return "unsafe".Length; + case Modifiers.Async: + return "async".Length; + case Modifiers.Any: + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return "any".Length; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + + public static Modifiers GetModifierValue(string modifier) + { + switch (modifier) { + case "private": + return Modifiers.Private; + case "internal": + return Modifiers.Internal; + case "protected": + return Modifiers.Protected; + case "public": + return Modifiers.Public; + case "abstract": + return Modifiers.Abstract; + case "virtual": + return Modifiers.Virtual; + case "sealed": + return Modifiers.Sealed; + case "static": + return Modifiers.Static; + case "override": + return Modifiers.Override; + case "readonly": + return Modifiers.Readonly; + case "const": + return Modifiers.Const; + case "new": + return Modifiers.New; + case "partial": + return Modifiers.Partial; + case "extern": + return Modifiers.Extern; + case "volatile": + return Modifiers.Volatile; + case "unsafe": + return Modifiers.Unsafe; + case "async": + return Modifiers.Async; + case "any": + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return Modifiers.Any; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/Ast/CSharpTokenNode.cs b/ICSharpCode.Decompiler/CSharp/Ast/CSharpTokenNode.cs new file mode 100644 index 000000000..713f664b3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/CSharpTokenNode.cs @@ -0,0 +1,131 @@ +// +// TokenNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a token in C#. Note that the type of the token is defined through the TokenRole. + /// + /// + /// In all non null c# token nodes the Role of a CSharpToken must be a TokenRole. + /// + public class CSharpTokenNode : AstNode + { + public static new readonly CSharpTokenNode Null = new NullCSharpTokenNode (); + class NullCSharpTokenNode : CSharpTokenNode + { + public override bool IsNull { + get { + return true; + } + } + + public NullCSharpTokenNode () : base (TextLocation.Empty, null) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Token; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + int TokenLength { + get { + return TokenRole.TokenLengths [(int)(this.flags >> AstNodeFlagsUsedBits)]; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + TokenLength); + } + } + + public CSharpTokenNode (TextLocation location, TokenRole role) + { + this.startLocation = location; + if (role != null) + this.flags |= role.TokenIndex << AstNodeFlagsUsedBits; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return TokenRole.Tokens [(int)(this.flags >> AstNodeFlagsUsedBits)]; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCSharpTokenNode (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCSharpTokenNode (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCSharpTokenNode (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CSharpTokenNode o = other as CSharpTokenNode; + return o != null && !o.IsNull && !(o is CSharpModifierToken); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/CSharpUtil.cs b/ICSharpCode.Decompiler/CSharp/Ast/CSharpUtil.cs new file mode 100644 index 000000000..a2a07ad6e --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/CSharpUtil.cs @@ -0,0 +1,180 @@ +// +// CSharpUtil.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class CSharpUtil + { + /// + /// Inverts a boolean condition. Note: The condition object can be frozen (from AST) it's cloned internally. + /// + /// The condition to invert. + public static Expression InvertCondition(Expression condition) + { + return InvertConditionInternal(condition); + } + + static Expression InvertConditionInternal(Expression condition) + { + if (condition is ParenthesizedExpression) { + return new ParenthesizedExpression(InvertCondition(((ParenthesizedExpression)condition).Expression)); + } + + if (condition is UnaryOperatorExpression) { + var uOp = (UnaryOperatorExpression)condition; + if (uOp.Operator == UnaryOperatorType.Not) { + if (!(uOp.Parent is Expression)) + return GetInnerMostExpression(uOp.Expression).Clone(); + return uOp.Expression.Clone(); + } + return new UnaryOperatorExpression(UnaryOperatorType.Not, uOp.Clone()); + } + + if (condition is BinaryOperatorExpression) { + var bOp = (BinaryOperatorExpression)condition; + + if ((bOp.Operator == BinaryOperatorType.ConditionalAnd) || (bOp.Operator == BinaryOperatorType.ConditionalOr)) { + return new BinaryOperatorExpression(InvertCondition(bOp.Left), NegateConditionOperator(bOp.Operator), InvertCondition(bOp.Right)); + } else if ((bOp.Operator == BinaryOperatorType.Equality) || (bOp.Operator == BinaryOperatorType.InEquality) || (bOp.Operator == BinaryOperatorType.GreaterThan) + || (bOp.Operator == BinaryOperatorType.GreaterThanOrEqual) || (bOp.Operator == BinaryOperatorType.LessThan) || + (bOp.Operator == BinaryOperatorType.LessThanOrEqual)) { + return new BinaryOperatorExpression(bOp.Left.Clone(), NegateRelationalOperator(bOp.Operator), bOp.Right.Clone()); + } else { + var negatedOp = NegateRelationalOperator(bOp.Operator); + if (negatedOp == BinaryOperatorType.Any) + return new UnaryOperatorExpression(UnaryOperatorType.Not, new ParenthesizedExpression(condition.Clone())); + bOp = (BinaryOperatorExpression)bOp.Clone(); + bOp.Operator = negatedOp; + return bOp; + } + } + if (condition is ConditionalExpression) { + var cEx = condition.Clone() as ConditionalExpression; + cEx.Condition = InvertCondition(cEx.Condition); + return cEx; + } + if (condition is PrimitiveExpression) { + var pex = condition as PrimitiveExpression; + if (pex.Value is bool) { + return new PrimitiveExpression(!((bool)pex.Value)); + } + } + + return new UnaryOperatorExpression(UnaryOperatorType.Not, AddParensForUnaryExpressionIfRequired(condition.Clone())); + } + + /// + /// When negating an expression this is required, otherwise you would end up with + /// a or b -> !a or b + /// + internal static Expression AddParensForUnaryExpressionIfRequired(Expression expression) + { + if ((expression is BinaryOperatorExpression) || + (expression is AssignmentExpression) || + (expression is CastExpression) || + (expression is AsExpression) || + (expression is IsExpression) || + (expression is LambdaExpression) || + (expression is ConditionalExpression)) { + return new ParenthesizedExpression(expression); + } + + return expression; + } + + /// + /// Get negation of the specified relational operator + /// + /// + /// negation of the specified relational operator, or BinaryOperatorType.Any if it's not a relational operator + /// + public static BinaryOperatorType NegateRelationalOperator(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.GreaterThan: + return BinaryOperatorType.LessThanOrEqual; + case BinaryOperatorType.GreaterThanOrEqual: + return BinaryOperatorType.LessThan; + case BinaryOperatorType.Equality: + return BinaryOperatorType.InEquality; + case BinaryOperatorType.InEquality: + return BinaryOperatorType.Equality; + case BinaryOperatorType.LessThan: + return BinaryOperatorType.GreaterThanOrEqual; + case BinaryOperatorType.LessThanOrEqual: + return BinaryOperatorType.GreaterThan; + case BinaryOperatorType.ConditionalOr: + return BinaryOperatorType.ConditionalAnd; + case BinaryOperatorType.ConditionalAnd: + return BinaryOperatorType.ConditionalOr; + } + return BinaryOperatorType.Any; + } + + /// + /// Returns true, if the specified operator is a relational operator + /// + public static bool IsRelationalOperator(BinaryOperatorType op) + { + return NegateRelationalOperator(op) != BinaryOperatorType.Any; + } + + /// + /// Get negation of the condition operator + /// + /// + /// negation of the specified condition operator, or BinaryOperatorType.Any if it's not a condition operator + /// + public static BinaryOperatorType NegateConditionOperator(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.ConditionalOr: + return BinaryOperatorType.ConditionalAnd; + case BinaryOperatorType.ConditionalAnd: + return BinaryOperatorType.ConditionalOr; + } + return BinaryOperatorType.Any; + } + + public static bool AreConditionsEqual(Expression cond1, Expression cond2) + { + if (cond1 == null || cond2 == null) + return false; + return GetInnerMostExpression(cond1).IsMatch(GetInnerMostExpression(cond2)); + } + + public static Expression GetInnerMostExpression(Expression target) + { + while (target is ParenthesizedExpression) + target = ((ParenthesizedExpression)target).Expression; + return target; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/ComposedType.cs b/ICSharpCode.Decompiler/CSharp/Ast/ComposedType.cs new file mode 100644 index 000000000..2bbfc5a8a --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/ComposedType.cs @@ -0,0 +1,259 @@ +// +// ComposedType.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ComposedType : AstType + { + public static readonly TokenRole RefRole = new TokenRole("ref"); + public static readonly TokenRole NullableRole = new TokenRole("?"); + public static readonly TokenRole PointerRole = new TokenRole("*"); + public static readonly Role ArraySpecifierRole = new Role("ArraySpecifier"); + + /// + /// Gets/sets whether this type has a 'ref' specifier. + /// This is used for C# 7 ref locals/ref return. + /// Parameters use ParameterDeclaration.ParameterModifier instead. + /// + public bool HasRefSpecifier { + get { + return !GetChildByRole(RefRole).IsNull; + } + set { + SetChildByRole(RefRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); + } + } + + public AstType BaseType { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public bool HasNullableSpecifier { + get { + return !GetChildByRole(NullableRole).IsNull; + } + set { + SetChildByRole(NullableRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); + } + } + + public CSharpTokenNode NullableSpecifierToken { + get { + return GetChildByRole(NullableRole); + } + } + + public int PointerRank { + get { + return GetChildrenByRole(PointerRole).Count; + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException(); + int d = this.PointerRank; + while (d > value) { + GetChildByRole(PointerRole).Remove(); + d--; + } + while (d < value) { + InsertChildBefore(GetChildByRole(PointerRole), new CSharpTokenNode(TextLocation.Empty, PointerRole), PointerRole); + d++; + } + } + } + + public AstNodeCollection ArraySpecifiers { + get { return GetChildrenByRole (ArraySpecifierRole); } + } + + public AstNodeCollection PointerTokens { + get { return GetChildrenByRole (PointerRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitComposedType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitComposedType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitComposedType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ComposedType o = other as ComposedType; + return o != null + && this.HasNullableSpecifier == o.HasNullableSpecifier + && this.PointerRank == o.PointerRank + && this.HasRefSpecifier == o.HasRefSpecifier + && this.BaseType.DoMatch(o.BaseType, match) + && this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + StringBuilder b = new StringBuilder(); + if (this.HasRefSpecifier) + b.Append("ref "); + b.Append(this.BaseType.ToString()); + if (this.HasNullableSpecifier) + b.Append('?'); + b.Append('*', this.PointerRank); + foreach (var arraySpecifier in this.ArraySpecifiers) { + b.Append('['); + b.Append(',', arraySpecifier.Dimensions - 1); + b.Append(']'); + } + return b.ToString(); + } + + public override AstType MakePointerType() + { + if (ArraySpecifiers.Any()) { + return base.MakePointerType(); + } else { + this.PointerRank++; + return this; + } + } + + public override AstType MakeArrayType(int dimensions) + { + InsertChildBefore(this.ArraySpecifiers.FirstOrDefault(), new ArraySpecifier(dimensions), ArraySpecifierRole); + return this; + } + + public override AstType MakeRefType() + { + this.HasRefSpecifier = true; + return this; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + ITypeReference t = this.BaseType.ToTypeReference(lookupMode, interningProvider); + if (this.HasNullableSpecifier) { + t = interningProvider.Intern(NullableType.Create(t)); + } + int pointerRank = this.PointerRank; + for (int i = 0; i < pointerRank; i++) { + t = interningProvider.Intern(new PointerTypeReference(t)); + } + foreach (var a in this.ArraySpecifiers.Reverse()) { + t = interningProvider.Intern(new ArrayTypeReference(t, a.Dimensions)); + } + if (this.HasRefSpecifier) { + t = interningProvider.Intern(new ByReferenceTypeReference(t)); + } + return t; + } + } + + /// + /// [,,,] + /// + public class ArraySpecifier : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public ArraySpecifier() + { + } + + public ArraySpecifier(int dimensions) + { + this.Dimensions = dimensions; + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public int Dimensions { + get { return 1 + GetChildrenByRole(Roles.Comma).Count; } + set { + int d = this.Dimensions; + while (d > value) { + GetChildByRole(Roles.Comma).Remove(); + d--; + } + while (d < value) { + InsertChildBefore(GetChildByRole(Roles.Comma), new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); + d++; + } + } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArraySpecifier (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArraySpecifier (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArraySpecifier(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArraySpecifier o = other as ArraySpecifier; + return o != null && this.Dimensions == o.Dimensions; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return "[" + new string(',', this.Dimensions - 1) + "]"; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Ast/DepthFirstAstVisitor.cs new file mode 100644 index 000000000..d8c678e2b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/DepthFirstAstVisitor.cs @@ -0,0 +1,1849 @@ +// +// IAstVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual void VisitChildren (AstNode node) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + } + + public virtual void VisitNullNode(AstNode nullNode) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + } + + public virtual void VisitSyntaxTree (SyntaxTree syntaxTree) + { + VisitChildren (syntaxTree); + } + + public virtual void VisitComment(Comment comment) + { + VisitChildren(comment); + } + + public virtual void VisitNewLine(NewLineNode newLineNode) + { + VisitChildren(newLineNode); + } + + public virtual void VisitWhitespace(WhitespaceNode whitespaceNode) + { + VisitChildren(whitespaceNode); + } + + public virtual void VisitText(TextNode textNode) + { + VisitChildren(textNode); + } + + public virtual void VisitDocumentationReference (DocumentationReference documentationReference) + { + VisitChildren (documentationReference); + } + + public virtual void VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + VisitChildren (preProcessorDirective); + } + + public virtual void VisitIdentifier (Identifier identifier) + { + VisitChildren (identifier); + } + + public virtual void VisitCSharpTokenNode (CSharpTokenNode token) + { + VisitChildren (token); + } + + public virtual void VisitPrimitiveType (PrimitiveType primitiveType) + { + VisitChildren (primitiveType); + } + + public virtual void VisitComposedType (ComposedType composedType) + { + VisitChildren (composedType); + } + + public virtual void VisitSimpleType(SimpleType simpleType) + { + VisitChildren (simpleType); + } + + public virtual void VisitMemberType(MemberType memberType) + { + VisitChildren (memberType); + } + + public virtual void VisitAttribute (Attribute attribute) + { + VisitChildren (attribute); + } + + public virtual void VisitAttributeSection (AttributeSection attributeSection) + { + VisitChildren (attributeSection); + } + + public virtual void VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration) + { + VisitChildren (delegateDeclaration); + } + + public virtual void VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration) + { + VisitChildren (namespaceDeclaration); + } + + public virtual void VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + VisitChildren (typeDeclaration); + } + + public virtual void VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration) + { + VisitChildren (typeParameterDeclaration); + } + + public virtual void VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration) + { + VisitChildren (enumMemberDeclaration); + } + + public virtual void VisitUsingDeclaration (UsingDeclaration usingDeclaration) + { + VisitChildren (usingDeclaration); + } + + public virtual void VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration) + { + VisitChildren (usingDeclaration); + } + + public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + VisitChildren (externAliasDeclaration); + } + + public virtual void VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration) + { + VisitChildren (constructorDeclaration); + } + + public virtual void VisitConstructorInitializer (ConstructorInitializer constructorInitializer) + { + VisitChildren (constructorInitializer); + } + + public virtual void VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration) + { + VisitChildren (destructorDeclaration); + } + + public virtual void VisitEventDeclaration (EventDeclaration eventDeclaration) + { + VisitChildren (eventDeclaration); + } + + public virtual void VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration) + { + VisitChildren (eventDeclaration); + } + + public virtual void VisitFieldDeclaration (FieldDeclaration fieldDeclaration) + { + VisitChildren (fieldDeclaration); + } + + public virtual void VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration) + { + VisitChildren (fixedFieldDeclaration); + } + + public virtual void VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer) + { + VisitChildren (fixedVariableInitializer); + } + + public virtual void VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration) + { + VisitChildren (indexerDeclaration); + } + + public virtual void VisitMethodDeclaration (MethodDeclaration methodDeclaration) + { + VisitChildren (methodDeclaration); + } + + public virtual void VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration) + { + VisitChildren (operatorDeclaration); + } + + public virtual void VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration) + { + VisitChildren (propertyDeclaration); + } + + public virtual void VisitAccessor (Accessor accessor) + { + VisitChildren (accessor); + } + + public virtual void VisitVariableInitializer (VariableInitializer variableInitializer) + { + VisitChildren (variableInitializer); + } + + public virtual void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) + { + VisitChildren (parameterDeclaration); + } + + public virtual void VisitConstraint (Constraint constraint) + { + VisitChildren (constraint); + } + + public virtual void VisitBlockStatement (BlockStatement blockStatement) + { + VisitChildren (blockStatement); + } + + public virtual void VisitExpressionStatement (ExpressionStatement expressionStatement) + { + VisitChildren (expressionStatement); + } + + public virtual void VisitBreakStatement (BreakStatement breakStatement) + { + VisitChildren (breakStatement); + } + + public virtual void VisitCheckedStatement (CheckedStatement checkedStatement) + { + VisitChildren (checkedStatement); + } + + public virtual void VisitContinueStatement (ContinueStatement continueStatement) + { + VisitChildren (continueStatement); + } + + public virtual void VisitDoWhileStatement (DoWhileStatement doWhileStatement) + { + VisitChildren (doWhileStatement); + } + + public virtual void VisitEmptyStatement (EmptyStatement emptyStatement) + { + VisitChildren (emptyStatement); + } + + public virtual void VisitFixedStatement (FixedStatement fixedStatement) + { + VisitChildren (fixedStatement); + } + + public virtual void VisitForeachStatement (ForeachStatement foreachStatement) + { + VisitChildren (foreachStatement); + } + + public virtual void VisitForStatement (ForStatement forStatement) + { + VisitChildren (forStatement); + } + + public virtual void VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement) + { + VisitChildren (gotoCaseStatement); + } + + public virtual void VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement) + { + VisitChildren (gotoDefaultStatement); + } + + public virtual void VisitGotoStatement (GotoStatement gotoStatement) + { + VisitChildren (gotoStatement); + } + + public virtual void VisitIfElseStatement (IfElseStatement ifElseStatement) + { + VisitChildren (ifElseStatement); + } + + public virtual void VisitLabelStatement (LabelStatement labelStatement) + { + VisitChildren (labelStatement); + } + + public virtual void VisitLockStatement (LockStatement lockStatement) + { + VisitChildren (lockStatement); + } + + public virtual void VisitReturnStatement (ReturnStatement returnStatement) + { + VisitChildren (returnStatement); + } + + public virtual void VisitSwitchStatement (SwitchStatement switchStatement) + { + VisitChildren (switchStatement); + } + + public virtual void VisitSwitchSection (SwitchSection switchSection) + { + VisitChildren (switchSection); + } + + public virtual void VisitCaseLabel (CaseLabel caseLabel) + { + VisitChildren (caseLabel); + } + + public virtual void VisitThrowStatement (ThrowStatement throwStatement) + { + VisitChildren (throwStatement); + } + + public virtual void VisitTryCatchStatement (TryCatchStatement tryCatchStatement) + { + VisitChildren (tryCatchStatement); + } + + public virtual void VisitCatchClause (CatchClause catchClause) + { + VisitChildren (catchClause); + } + + public virtual void VisitUncheckedStatement (UncheckedStatement uncheckedStatement) + { + VisitChildren (uncheckedStatement); + } + + public virtual void VisitUnsafeStatement (UnsafeStatement unsafeStatement) + { + VisitChildren (unsafeStatement); + } + + public virtual void VisitUsingStatement (UsingStatement usingStatement) + { + VisitChildren (usingStatement); + } + + public virtual void VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement) + { + VisitChildren (variableDeclarationStatement); + } + + public virtual void VisitWhileStatement (WhileStatement whileStatement) + { + VisitChildren (whileStatement); + } + + public virtual void VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement) + { + VisitChildren (yieldBreakStatement); + } + + public virtual void VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) + { + VisitChildren (yieldReturnStatement); + } + + public virtual void VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) + { + VisitChildren (anonymousMethodExpression); + } + + public virtual void VisitLambdaExpression (LambdaExpression lambdaExpression) + { + VisitChildren (lambdaExpression); + } + + public virtual void VisitAssignmentExpression (AssignmentExpression assignmentExpression) + { + VisitChildren (assignmentExpression); + } + + public virtual void VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) + { + VisitChildren (baseReferenceExpression); + } + + public virtual void VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) + { + VisitChildren (binaryOperatorExpression); + } + + public virtual void VisitCastExpression (CastExpression castExpression) + { + VisitChildren (castExpression); + } + + public virtual void VisitCheckedExpression (CheckedExpression checkedExpression) + { + VisitChildren (checkedExpression); + } + + public virtual void VisitConditionalExpression (ConditionalExpression conditionalExpression) + { + VisitChildren (conditionalExpression); + } + + public virtual void VisitIdentifierExpression (IdentifierExpression identifierExpression) + { + VisitChildren (identifierExpression); + } + + public virtual void VisitIndexerExpression (IndexerExpression indexerExpression) + { + VisitChildren (indexerExpression); + } + + public virtual void VisitInvocationExpression (InvocationExpression invocationExpression) + { + VisitChildren (invocationExpression); + } + + public virtual void VisitDirectionExpression (DirectionExpression directionExpression) + { + VisitChildren (directionExpression); + } + + public virtual void VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) + { + VisitChildren (memberReferenceExpression); + } + + public virtual void VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) + { + VisitChildren (nullReferenceExpression); + } + + public virtual void VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) + { + VisitChildren (objectCreateExpression); + } + + public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + VisitChildren (anonymousTypeCreateExpression); + } + + public virtual void VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) + { + VisitChildren (arrayCreateExpression); + } + + public virtual void VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) + { + VisitChildren (parenthesizedExpression); + } + + public virtual void VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) + { + VisitChildren (pointerReferenceExpression); + } + + public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + VisitChildren (primitiveExpression); + } + + public virtual void VisitSizeOfExpression (SizeOfExpression sizeOfExpression) + { + VisitChildren (sizeOfExpression); + } + + public virtual void VisitStackAllocExpression (StackAllocExpression stackAllocExpression) + { + VisitChildren (stackAllocExpression); + } + + public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + VisitChildren (thisReferenceExpression); + } + + public virtual void VisitTypeOfExpression (TypeOfExpression typeOfExpression) + { + VisitChildren (typeOfExpression); + } + + public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + VisitChildren (typeReferenceExpression); + } + + public virtual void VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) + { + VisitChildren (unaryOperatorExpression); + } + + public virtual void VisitUncheckedExpression (UncheckedExpression uncheckedExpression) + { + VisitChildren (uncheckedExpression); + } + + public virtual void VisitQueryExpression(QueryExpression queryExpression) + { + VisitChildren (queryExpression); + } + + public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + VisitChildren (queryContinuationClause); + } + + public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) + { + VisitChildren (queryFromClause); + } + + public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) + { + VisitChildren (queryLetClause); + } + + public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + VisitChildren (queryWhereClause); + } + + public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + VisitChildren (queryJoinClause); + } + + public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + VisitChildren (queryOrderClause); + } + + public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) + { + VisitChildren (queryOrdering); + } + + public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + VisitChildren (querySelectClause); + } + + public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + VisitChildren (queryGroupClause); + } + + public virtual void VisitAsExpression (AsExpression asExpression) + { + VisitChildren (asExpression); + } + + public virtual void VisitIsExpression (IsExpression isExpression) + { + VisitChildren (isExpression); + } + + public virtual void VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) + { + VisitChildren (defaultValueExpression); + } + + public virtual void VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) + { + VisitChildren (undocumentedExpression); + } + + public virtual void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + VisitChildren (arrayInitializerExpression); + } + + public virtual void VisitArraySpecifier (ArraySpecifier arraySpecifier) + { + VisitChildren (arraySpecifier); + } + + public virtual void VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) + { + VisitChildren (namedArgumentExpression); + } + + public virtual void VisitNamedExpression (NamedExpression namedExpression) + { + VisitChildren (namedExpression); + } + + public virtual void VisitErrorNode(AstNode errorNode) + { + VisitChildren(errorNode); + } + + public virtual void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + VisitChildren (placeholder); + } + } + + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual T VisitChildren (AstNode node) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + return default (T); + } + + public virtual T VisitNullNode(AstNode nullNode) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + return default (T); + } + + public virtual T VisitSyntaxTree (SyntaxTree unit) + { + return VisitChildren (unit); + } + + public virtual T VisitComment (Comment comment) + { + return VisitChildren (comment); + } + + public virtual T VisitNewLine(NewLineNode newLineNode) + { + return VisitChildren(newLineNode); + } + + public virtual T VisitWhitespace(WhitespaceNode whitespaceNode) + { + return VisitChildren(whitespaceNode); + } + + public virtual T VisitText(TextNode textNode) + { + return VisitChildren(textNode); + } + + public virtual T VisitDocumentationReference (DocumentationReference documentationReference) + { + return VisitChildren (documentationReference); + } + + public virtual T VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + return VisitChildren (preProcessorDirective); + } + + public virtual T VisitIdentifier (Identifier identifier) + { + return VisitChildren (identifier); + } + + public virtual T VisitCSharpTokenNode (CSharpTokenNode token) + { + return VisitChildren (token); + } + + public virtual T VisitPrimitiveType (PrimitiveType primitiveType) + { + return VisitChildren (primitiveType); + } + + public virtual T VisitComposedType (ComposedType composedType) + { + return VisitChildren (composedType); + } + + public virtual T VisitSimpleType(SimpleType simpleType) + { + return VisitChildren (simpleType); + } + + public virtual T VisitMemberType(MemberType memberType) + { + return VisitChildren (memberType); + } + + public virtual T VisitAttribute (Attribute attribute) + { + return VisitChildren (attribute); + } + + public virtual T VisitAttributeSection (AttributeSection attributeSection) + { + return VisitChildren (attributeSection); + } + + public virtual T VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration) + { + return VisitChildren (delegateDeclaration); + } + + public virtual T VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration) + { + return VisitChildren (namespaceDeclaration); + } + + public virtual T VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + return VisitChildren (typeDeclaration); + } + + public virtual T VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration) + { + return VisitChildren (typeParameterDeclaration); + } + + public virtual T VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration) + { + return VisitChildren (enumMemberDeclaration); + } + + public virtual T VisitUsingDeclaration (UsingDeclaration usingDeclaration) + { + return VisitChildren (usingDeclaration); + } + + public virtual T VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration) + { + return VisitChildren (usingDeclaration); + } + + public virtual T VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + return VisitChildren (externAliasDeclaration); + } + + public virtual T VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration) + { + return VisitChildren (constructorDeclaration); + } + + public virtual T VisitConstructorInitializer (ConstructorInitializer constructorInitializer) + { + return VisitChildren (constructorInitializer); + } + + public virtual T VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration) + { + return VisitChildren (destructorDeclaration); + } + + public virtual T VisitEventDeclaration (EventDeclaration eventDeclaration) + { + return VisitChildren (eventDeclaration); + } + + public virtual T VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration) + { + return VisitChildren (eventDeclaration); + } + + public virtual T VisitFieldDeclaration (FieldDeclaration fieldDeclaration) + { + return VisitChildren (fieldDeclaration); + } + + public virtual T VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration) + { + return VisitChildren (fixedFieldDeclaration); + } + + public virtual T VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer) + { + return VisitChildren (fixedVariableInitializer); + } + + public virtual T VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration) + { + return VisitChildren (indexerDeclaration); + } + + public virtual T VisitMethodDeclaration (MethodDeclaration methodDeclaration) + { + return VisitChildren (methodDeclaration); + } + + public virtual T VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration) + { + return VisitChildren (operatorDeclaration); + } + + public virtual T VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration) + { + return VisitChildren (propertyDeclaration); + } + + public virtual T VisitAccessor (Accessor accessor) + { + return VisitChildren (accessor); + } + + public virtual T VisitVariableInitializer (VariableInitializer variableInitializer) + { + return VisitChildren (variableInitializer); + } + + public virtual T VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) + { + return VisitChildren (parameterDeclaration); + } + + public virtual T VisitConstraint (Constraint constraint) + { + return VisitChildren (constraint); + } + + public virtual T VisitBlockStatement (BlockStatement blockStatement) + { + return VisitChildren (blockStatement); + } + + public virtual T VisitExpressionStatement (ExpressionStatement expressionStatement) + { + return VisitChildren (expressionStatement); + } + + public virtual T VisitBreakStatement (BreakStatement breakStatement) + { + return VisitChildren (breakStatement); + } + + public virtual T VisitCheckedStatement (CheckedStatement checkedStatement) + { + return VisitChildren (checkedStatement); + } + + public virtual T VisitContinueStatement (ContinueStatement continueStatement) + { + return VisitChildren (continueStatement); + } + + public virtual T VisitDoWhileStatement (DoWhileStatement doWhileStatement) + { + return VisitChildren (doWhileStatement); + } + + public virtual T VisitEmptyStatement (EmptyStatement emptyStatement) + { + return VisitChildren (emptyStatement); + } + + public virtual T VisitFixedStatement (FixedStatement fixedStatement) + { + return VisitChildren (fixedStatement); + } + + public virtual T VisitForeachStatement (ForeachStatement foreachStatement) + { + return VisitChildren (foreachStatement); + } + + public virtual T VisitForStatement (ForStatement forStatement) + { + return VisitChildren (forStatement); + } + + public virtual T VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement) + { + return VisitChildren (gotoCaseStatement); + } + + public virtual T VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement) + { + return VisitChildren (gotoDefaultStatement); + } + + public virtual T VisitGotoStatement (GotoStatement gotoStatement) + { + return VisitChildren (gotoStatement); + } + + public virtual T VisitIfElseStatement (IfElseStatement ifElseStatement) + { + return VisitChildren (ifElseStatement); + } + + public virtual T VisitLabelStatement (LabelStatement labelStatement) + { + return VisitChildren (labelStatement); + } + + public virtual T VisitLockStatement (LockStatement lockStatement) + { + return VisitChildren (lockStatement); + } + + public virtual T VisitReturnStatement (ReturnStatement returnStatement) + { + return VisitChildren (returnStatement); + } + + public virtual T VisitSwitchStatement (SwitchStatement switchStatement) + { + return VisitChildren (switchStatement); + } + + public virtual T VisitSwitchSection (SwitchSection switchSection) + { + return VisitChildren (switchSection); + } + + public virtual T VisitCaseLabel (CaseLabel caseLabel) + { + return VisitChildren (caseLabel); + } + + public virtual T VisitThrowStatement (ThrowStatement throwStatement) + { + return VisitChildren (throwStatement); + } + + public virtual T VisitTryCatchStatement (TryCatchStatement tryCatchStatement) + { + return VisitChildren (tryCatchStatement); + } + + public virtual T VisitCatchClause (CatchClause catchClause) + { + return VisitChildren (catchClause); + } + + public virtual T VisitUncheckedStatement (UncheckedStatement uncheckedStatement) + { + return VisitChildren (uncheckedStatement); + } + + public virtual T VisitUnsafeStatement (UnsafeStatement unsafeStatement) + { + return VisitChildren (unsafeStatement); + } + + public virtual T VisitUsingStatement (UsingStatement usingStatement) + { + return VisitChildren (usingStatement); + } + + public virtual T VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement) + { + return VisitChildren (variableDeclarationStatement); + } + + public virtual T VisitWhileStatement (WhileStatement whileStatement) + { + return VisitChildren (whileStatement); + } + + public virtual T VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement) + { + return VisitChildren (yieldBreakStatement); + } + + public virtual T VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) + { + return VisitChildren (yieldReturnStatement); + } + + public virtual T VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) + { + return VisitChildren (anonymousMethodExpression); + } + + public virtual T VisitLambdaExpression (LambdaExpression lambdaExpression) + { + return VisitChildren (lambdaExpression); + } + + public virtual T VisitAssignmentExpression (AssignmentExpression assignmentExpression) + { + return VisitChildren (assignmentExpression); + } + + public virtual T VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) + { + return VisitChildren (baseReferenceExpression); + } + + public virtual T VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) + { + return VisitChildren (binaryOperatorExpression); + } + + public virtual T VisitCastExpression (CastExpression castExpression) + { + return VisitChildren (castExpression); + } + + public virtual T VisitCheckedExpression (CheckedExpression checkedExpression) + { + return VisitChildren (checkedExpression); + } + + public virtual T VisitConditionalExpression (ConditionalExpression conditionalExpression) + { + return VisitChildren (conditionalExpression); + } + + public virtual T VisitIdentifierExpression (IdentifierExpression identifierExpression) + { + return VisitChildren (identifierExpression); + } + + public virtual T VisitIndexerExpression (IndexerExpression indexerExpression) + { + return VisitChildren (indexerExpression); + } + + public virtual T VisitInvocationExpression (InvocationExpression invocationExpression) + { + return VisitChildren (invocationExpression); + } + + public virtual T VisitDirectionExpression (DirectionExpression directionExpression) + { + return VisitChildren (directionExpression); + } + + public virtual T VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) + { + return VisitChildren (memberReferenceExpression); + } + + public virtual T VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) + { + return VisitChildren (nullReferenceExpression); + } + + public virtual T VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) + { + return VisitChildren (objectCreateExpression); + } + + public virtual T VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + return VisitChildren (anonymousTypeCreateExpression); + } + + public virtual T VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) + { + return VisitChildren (arrayCreateExpression); + } + + public virtual T VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) + { + return VisitChildren (parenthesizedExpression); + } + + public virtual T VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) + { + return VisitChildren (pointerReferenceExpression); + } + + public virtual T VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + return VisitChildren (primitiveExpression); + } + + public virtual T VisitSizeOfExpression (SizeOfExpression sizeOfExpression) + { + return VisitChildren (sizeOfExpression); + } + + public virtual T VisitStackAllocExpression (StackAllocExpression stackAllocExpression) + { + return VisitChildren (stackAllocExpression); + } + + public virtual T VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + return VisitChildren (thisReferenceExpression); + } + + public virtual T VisitTypeOfExpression (TypeOfExpression typeOfExpression) + { + return VisitChildren (typeOfExpression); + } + + public virtual T VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + return VisitChildren (typeReferenceExpression); + } + + public virtual T VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) + { + return VisitChildren (unaryOperatorExpression); + } + + public virtual T VisitUncheckedExpression (UncheckedExpression uncheckedExpression) + { + return VisitChildren (uncheckedExpression); + } + + public virtual T VisitQueryExpression(QueryExpression queryExpression) + { + return VisitChildren (queryExpression); + } + + public virtual T VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + return VisitChildren (queryContinuationClause); + } + + public virtual T VisitQueryFromClause(QueryFromClause queryFromClause) + { + return VisitChildren (queryFromClause); + } + + public virtual T VisitQueryLetClause(QueryLetClause queryLetClause) + { + return VisitChildren (queryLetClause); + } + + public virtual T VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + return VisitChildren (queryWhereClause); + } + + public virtual T VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + return VisitChildren (queryJoinClause); + } + + public virtual T VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + return VisitChildren (queryOrderClause); + } + + public virtual T VisitQueryOrdering(QueryOrdering queryOrdering) + { + return VisitChildren (queryOrdering); + } + + public virtual T VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + return VisitChildren (querySelectClause); + } + + public virtual T VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + return VisitChildren (queryGroupClause); + } + + public virtual T VisitAsExpression (AsExpression asExpression) + { + return VisitChildren (asExpression); + } + + public virtual T VisitIsExpression (IsExpression isExpression) + { + return VisitChildren (isExpression); + } + + public virtual T VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) + { + return VisitChildren (defaultValueExpression); + } + + public virtual T VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) + { + return VisitChildren (undocumentedExpression); + } + + public virtual T VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + return VisitChildren (arrayInitializerExpression); + } + + public virtual T VisitArraySpecifier (ArraySpecifier arraySpecifier) + { + return VisitChildren (arraySpecifier); + } + + public virtual T VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) + { + return VisitChildren (namedArgumentExpression); + } + + public virtual T VisitNamedExpression (NamedExpression namedExpression) + { + return VisitChildren (namedExpression); + } + + public virtual T VisitErrorNode(AstNode errorNode) + { + return VisitChildren(errorNode); + } + + public virtual T VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + return VisitChildren (placeholder); + } + } + + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual S VisitChildren (AstNode node, T data) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this, data); + } + return default (S); + } + + public virtual S VisitNullNode(AstNode nullNode, T data) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + return default (S); + } + + public virtual S VisitSyntaxTree (SyntaxTree unit, T data) + { + return VisitChildren (unit, data); + } + + public virtual S VisitComment (Comment comment, T data) + { + return VisitChildren (comment, data); + } + + public virtual S VisitNewLine(NewLineNode newLineNode, T data) + { + return VisitChildren(newLineNode, data); + } + + public virtual S VisitWhitespace(WhitespaceNode whitespaceNode, T data) + { + return VisitChildren(whitespaceNode, data); + } + + public virtual S VisitText(TextNode textNode, T data) + { + return VisitChildren(textNode, data); + } + + public virtual S VisitDocumentationReference (DocumentationReference documentationReference, T data) + { + return VisitChildren (documentationReference, data); + } + + public virtual S VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective, T data) + { + return VisitChildren (preProcessorDirective, data); + } + + public virtual S VisitIdentifier (Identifier identifier, T data) + { + return VisitChildren (identifier, data); + } + + public virtual S VisitCSharpTokenNode (CSharpTokenNode token, T data) + { + return VisitChildren (token, data); + } + + public virtual S VisitPrimitiveType (PrimitiveType primitiveType, T data) + { + return VisitChildren (primitiveType, data); + } + + public virtual S VisitComposedType (ComposedType composedType, T data) + { + return VisitChildren (composedType, data); + } + + public virtual S VisitSimpleType(SimpleType simpleType, T data) + { + return VisitChildren (simpleType, data); + } + + public virtual S VisitMemberType(MemberType memberType, T data) + { + return VisitChildren (memberType, data); + } + + public virtual S VisitAttribute (Attribute attribute, T data) + { + return VisitChildren (attribute, data); + } + + public virtual S VisitAttributeSection (AttributeSection attributeSection, T data) + { + return VisitChildren (attributeSection, data); + } + + public virtual S VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration, T data) + { + return VisitChildren (delegateDeclaration, data); + } + + public virtual S VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration, T data) + { + return VisitChildren (namespaceDeclaration, data); + } + + public virtual S VisitTypeDeclaration (TypeDeclaration typeDeclaration, T data) + { + return VisitChildren (typeDeclaration, data); + } + + public virtual S VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration, T data) + { + return VisitChildren (typeParameterDeclaration, data); + } + + public virtual S VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration, T data) + { + return VisitChildren (enumMemberDeclaration, data); + } + + public virtual S VisitUsingDeclaration (UsingDeclaration usingDeclaration, T data) + { + return VisitChildren (usingDeclaration, data); + } + + public virtual S VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration, T data) + { + return VisitChildren (usingDeclaration, data); + } + + public virtual S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data) + { + return VisitChildren (externAliasDeclaration, data); + } + + public virtual S VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration, T data) + { + return VisitChildren (constructorDeclaration, data); + } + + public virtual S VisitConstructorInitializer (ConstructorInitializer constructorInitializer, T data) + { + return VisitChildren (constructorInitializer, data); + } + + public virtual S VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration, T data) + { + return VisitChildren (destructorDeclaration, data); + } + + public virtual S VisitEventDeclaration (EventDeclaration eventDeclaration, T data) + { + return VisitChildren (eventDeclaration, data); + } + + public virtual S VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration, T data) + { + return VisitChildren (eventDeclaration, data); + } + + public virtual S VisitFieldDeclaration (FieldDeclaration fieldDeclaration, T data) + { + return VisitChildren (fieldDeclaration, data); + } + + public virtual S VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration, T data) + { + return VisitChildren (fixedFieldDeclaration, data); + } + + public virtual S VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer, T data) + { + return VisitChildren (fixedVariableInitializer, data); + } + + public virtual S VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration, T data) + { + return VisitChildren (indexerDeclaration, data); + } + + public virtual S VisitMethodDeclaration (MethodDeclaration methodDeclaration, T data) + { + return VisitChildren (methodDeclaration, data); + } + + public virtual S VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration, T data) + { + return VisitChildren (operatorDeclaration, data); + } + + public virtual S VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration, T data) + { + return VisitChildren (propertyDeclaration, data); + } + + public virtual S VisitAccessor (Accessor accessor, T data) + { + return VisitChildren (accessor, data); + } + + public virtual S VisitVariableInitializer (VariableInitializer variableInitializer, T data) + { + return VisitChildren (variableInitializer, data); + } + + public virtual S VisitParameterDeclaration (ParameterDeclaration parameterDeclaration, T data) + { + return VisitChildren (parameterDeclaration, data); + } + + public virtual S VisitConstraint (Constraint constraint, T data) + { + return VisitChildren (constraint, data); + } + + public virtual S VisitBlockStatement (BlockStatement blockStatement, T data) + { + return VisitChildren (blockStatement, data); + } + + public virtual S VisitExpressionStatement (ExpressionStatement expressionStatement, T data) + { + return VisitChildren (expressionStatement, data); + } + + public virtual S VisitBreakStatement (BreakStatement breakStatement, T data) + { + return VisitChildren (breakStatement, data); + } + + public virtual S VisitCheckedStatement (CheckedStatement checkedStatement, T data) + { + return VisitChildren (checkedStatement, data); + } + + public virtual S VisitContinueStatement (ContinueStatement continueStatement, T data) + { + return VisitChildren (continueStatement, data); + } + + public virtual S VisitDoWhileStatement (DoWhileStatement doWhileStatement, T data) + { + return VisitChildren (doWhileStatement, data); + } + + public virtual S VisitEmptyStatement (EmptyStatement emptyStatement, T data) + { + return VisitChildren (emptyStatement, data); + } + + public virtual S VisitFixedStatement (FixedStatement fixedStatement, T data) + { + return VisitChildren (fixedStatement, data); + } + + public virtual S VisitForeachStatement (ForeachStatement foreachStatement, T data) + { + return VisitChildren (foreachStatement, data); + } + + public virtual S VisitForStatement (ForStatement forStatement, T data) + { + return VisitChildren (forStatement, data); + } + + public virtual S VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement, T data) + { + return VisitChildren (gotoCaseStatement, data); + } + + public virtual S VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement, T data) + { + return VisitChildren (gotoDefaultStatement, data); + } + + public virtual S VisitGotoStatement (GotoStatement gotoStatement, T data) + { + return VisitChildren (gotoStatement, data); + } + + public virtual S VisitIfElseStatement (IfElseStatement ifElseStatement, T data) + { + return VisitChildren (ifElseStatement, data); + } + + public virtual S VisitLabelStatement (LabelStatement labelStatement, T data) + { + return VisitChildren (labelStatement, data); + } + + public virtual S VisitLockStatement (LockStatement lockStatement, T data) + { + return VisitChildren (lockStatement, data); + } + + public virtual S VisitReturnStatement (ReturnStatement returnStatement, T data) + { + return VisitChildren (returnStatement, data); + } + + public virtual S VisitSwitchStatement (SwitchStatement switchStatement, T data) + { + return VisitChildren (switchStatement, data); + } + + public virtual S VisitSwitchSection (SwitchSection switchSection, T data) + { + return VisitChildren (switchSection, data); + } + + public virtual S VisitCaseLabel (CaseLabel caseLabel, T data) + { + return VisitChildren (caseLabel, data); + } + + public virtual S VisitThrowStatement (ThrowStatement throwStatement, T data) + { + return VisitChildren (throwStatement, data); + } + + public virtual S VisitTryCatchStatement (TryCatchStatement tryCatchStatement, T data) + { + return VisitChildren (tryCatchStatement, data); + } + + public virtual S VisitCatchClause (CatchClause catchClause, T data) + { + return VisitChildren (catchClause, data); + } + + public virtual S VisitUncheckedStatement (UncheckedStatement uncheckedStatement, T data) + { + return VisitChildren (uncheckedStatement, data); + } + + public virtual S VisitUnsafeStatement (UnsafeStatement unsafeStatement, T data) + { + return VisitChildren (unsafeStatement, data); + } + + public virtual S VisitUsingStatement (UsingStatement usingStatement, T data) + { + return VisitChildren (usingStatement, data); + } + + public virtual S VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement, T data) + { + return VisitChildren (variableDeclarationStatement, data); + } + + public virtual S VisitWhileStatement (WhileStatement whileStatement, T data) + { + return VisitChildren (whileStatement, data); + } + + public virtual S VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement, T data) + { + return VisitChildren (yieldBreakStatement, data); + } + + public virtual S VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement, T data) + { + return VisitChildren (yieldReturnStatement, data); + } + + public virtual S VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression, T data) + { + return VisitChildren (anonymousMethodExpression, data); + } + + public virtual S VisitLambdaExpression (LambdaExpression lambdaExpression, T data) + { + return VisitChildren (lambdaExpression, data); + } + + public virtual S VisitAssignmentExpression (AssignmentExpression assignmentExpression, T data) + { + return VisitChildren (assignmentExpression, data); + } + + public virtual S VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression, T data) + { + return VisitChildren (baseReferenceExpression, data); + } + + public virtual S VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression, T data) + { + return VisitChildren (binaryOperatorExpression, data); + } + + public virtual S VisitCastExpression (CastExpression castExpression, T data) + { + return VisitChildren (castExpression, data); + } + + public virtual S VisitCheckedExpression (CheckedExpression checkedExpression, T data) + { + return VisitChildren (checkedExpression, data); + } + + public virtual S VisitConditionalExpression (ConditionalExpression conditionalExpression, T data) + { + return VisitChildren (conditionalExpression, data); + } + + public virtual S VisitIdentifierExpression (IdentifierExpression identifierExpression, T data) + { + return VisitChildren (identifierExpression, data); + } + + public virtual S VisitIndexerExpression (IndexerExpression indexerExpression, T data) + { + return VisitChildren (indexerExpression, data); + } + + public virtual S VisitInvocationExpression (InvocationExpression invocationExpression, T data) + { + return VisitChildren (invocationExpression, data); + } + + public virtual S VisitDirectionExpression (DirectionExpression directionExpression, T data) + { + return VisitChildren (directionExpression, data); + } + + public virtual S VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression, T data) + { + return VisitChildren (memberReferenceExpression, data); + } + + public virtual S VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression, T data) + { + return VisitChildren (nullReferenceExpression, data); + } + + public virtual S VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression, T data) + { + return VisitChildren (objectCreateExpression, data); + } + + public virtual S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data) + { + return VisitChildren (anonymousTypeCreateExpression, data); + } + + public virtual S VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression, T data) + { + return VisitChildren (arrayCreateExpression, data); + } + + public virtual S VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression, T data) + { + return VisitChildren (parenthesizedExpression, data); + } + + public virtual S VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression, T data) + { + return VisitChildren (pointerReferenceExpression, data); + } + + public virtual S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data) + { + return VisitChildren (primitiveExpression, data); + } + + public virtual S VisitSizeOfExpression (SizeOfExpression sizeOfExpression, T data) + { + return VisitChildren (sizeOfExpression, data); + } + + public virtual S VisitStackAllocExpression (StackAllocExpression stackAllocExpression, T data) + { + return VisitChildren (stackAllocExpression, data); + } + + public virtual S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data) + { + return VisitChildren (thisReferenceExpression, data); + } + + public virtual S VisitTypeOfExpression (TypeOfExpression typeOfExpression, T data) + { + return VisitChildren (typeOfExpression, data); + } + + public virtual S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data) + { + return VisitChildren (typeReferenceExpression, data); + } + + public virtual S VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression, T data) + { + return VisitChildren (unaryOperatorExpression, data); + } + + public virtual S VisitUncheckedExpression (UncheckedExpression uncheckedExpression, T data) + { + return VisitChildren (uncheckedExpression, data); + } + + public virtual S VisitQueryExpression(QueryExpression queryExpression, T data) + { + return VisitChildren (queryExpression, data); + } + + public virtual S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data) + { + return VisitChildren (queryContinuationClause, data); + } + + public virtual S VisitQueryFromClause(QueryFromClause queryFromClause, T data) + { + return VisitChildren (queryFromClause, data); + } + + public virtual S VisitQueryLetClause(QueryLetClause queryLetClause, T data) + { + return VisitChildren (queryLetClause, data); + } + + public virtual S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data) + { + return VisitChildren (queryWhereClause, data); + } + + public virtual S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data) + { + return VisitChildren (queryJoinClause, data); + } + + public virtual S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data) + { + return VisitChildren (queryOrderClause, data); + } + + public virtual S VisitQueryOrdering(QueryOrdering queryOrdering, T data) + { + return VisitChildren (queryOrdering, data); + } + + public virtual S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data) + { + return VisitChildren (querySelectClause, data); + } + + public virtual S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data) + { + return VisitChildren (queryGroupClause, data); + } + + public virtual S VisitAsExpression (AsExpression asExpression, T data) + { + return VisitChildren (asExpression, data); + } + + public virtual S VisitIsExpression (IsExpression isExpression, T data) + { + return VisitChildren (isExpression, data); + } + + public virtual S VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression, T data) + { + return VisitChildren (defaultValueExpression, data); + } + + public virtual S VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression, T data) + { + return VisitChildren (undocumentedExpression, data); + } + + public virtual S VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression, T data) + { + return VisitChildren (arrayInitializerExpression, data); + } + + public virtual S VisitArraySpecifier (ArraySpecifier arraySpecifier, T data) + { + return VisitChildren (arraySpecifier, data); + } + + public virtual S VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression, T data) + { + return VisitChildren (namedArgumentExpression, data); + } + + public virtual S VisitNamedExpression (NamedExpression namedExpression, T data) + { + return VisitChildren (namedExpression, data); + } + + public virtual S VisitErrorNode(AstNode errorNode, T data) + { + return VisitChildren(errorNode, data); + } + + public virtual S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data) + { + return VisitChildren (placeholder, data); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/DocumentationReference.cs b/ICSharpCode.Decompiler/CSharp/Ast/DocumentationReference.cs new file mode 100644 index 000000000..633f921b2 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/DocumentationReference.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a 'cref' reference in XML documentation. + /// + public class DocumentationReference : AstNode + { + public static readonly Role DeclaringTypeRole = new Role("DeclaringType", AstType.Null); + public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", AstType.Null); + + SymbolKind symbolKind; + OperatorType operatorType; + bool hasParameterList; + + /// + /// Gets/Sets the entity type. + /// Possible values are: + /// SymbolKind.Operator for operators, + /// SymbolKind.Indexer for indexers, + /// SymbolKind.TypeDefinition for references to primitive types, + /// and SymbolKind.None for everything else. + /// + public SymbolKind SymbolKind { + get { return symbolKind; } + set { + ThrowIfFrozen(); + symbolKind = value; + } + } + + /// + /// Gets/Sets the operator type. + /// This property is only used when SymbolKind==Operator. + /// + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } + + /// + /// Gets/Sets whether a parameter list was provided. + /// + public bool HasParameterList { + get { return hasParameterList; } + set { + ThrowIfFrozen(); + hasParameterList = value; + } + } + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + /// + /// Gets/Sets the declaring type. + /// + public AstType DeclaringType { + get { return GetChildByRole(DeclaringTypeRole); } + set { SetChildByRole(DeclaringTypeRole, value); } + } + + /// + /// Gets/sets the member name. + /// This property is only used when SymbolKind==None. + /// + public string MemberName { + get { return GetChildByRole(Roles.Identifier).Name; } + set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } + } + + /// + /// Gets/Sets the return type of conversion operators. + /// This property is only used when SymbolKind==Operator and OperatorType is explicit or implicit. + /// + public AstType ConversionOperatorReturnType { + get { return GetChildByRole(ConversionOperatorReturnTypeRole); } + set { SetChildByRole(ConversionOperatorReturnTypeRole, value); } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + DocumentationReference o = other as DocumentationReference; + if (!(o != null && this.SymbolKind == o.SymbolKind && this.HasParameterList == o.HasParameterList)) + return false; + if (this.SymbolKind == SymbolKind.Operator) { + if (this.OperatorType != o.OperatorType) + return false; + if (this.OperatorType == OperatorType.Implicit || this.OperatorType == OperatorType.Explicit) { + if (!this.ConversionOperatorReturnType.DoMatch(o.ConversionOperatorReturnType, match)) + return false; + } + } else if (this.SymbolKind == SymbolKind.None) { + if (!MatchString(this.MemberName, o.MemberName)) + return false; + if (!this.TypeArguments.DoMatch(o.TypeArguments, match)) + return false; + } + return this.Parameters.DoMatch(o.Parameters, match); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDocumentationReference (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDocumentationReference (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitDocumentationReference (this, data); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/ErrorNode.cs b/ICSharpCode.Decompiler/CSharp/Ast/ErrorNode.cs new file mode 100644 index 000000000..36d9ef6e0 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/ErrorNode.cs @@ -0,0 +1,88 @@ +// +// ErrorNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin (http://www.xamarin.com); +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a parsing error in the ast. At the moment it only represents missing closing bracket. + /// This closing bracket is replaced by a node at the highest possible position. + /// (To make GetAstNodeAt (line, col) working). + /// + public class ErrorNode : AstNode + { + static TextLocation maxLoc = new TextLocation (int.MaxValue, int.MaxValue); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override TextLocation StartLocation { + get { + return maxLoc; + } + } + + public override TextLocation EndLocation { + get { + return maxLoc; + } + } + + public ErrorNode () + { + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitErrorNode(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitErrorNode(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitErrorNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as ErrorNode; + return o != null; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return "[ErrorNode]"; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AnonymousMethodExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AnonymousMethodExpression.cs new file mode 100644 index 000000000..e8de95431 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AnonymousMethodExpression.cs @@ -0,0 +1,117 @@ +// +// AnonymousMethodExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [async] delegate(Parameters) {Body} + /// + public class AnonymousMethodExpression : Expression + { + public readonly static TokenRole DelegateKeywordRole = new TokenRole ("delegate"); + public readonly static TokenRole AsyncModifierRole = LambdaExpression.AsyncModifierRole; + + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } + + // used to tell the difference between delegate {} and delegate () {} + bool hasParameterList; + + public bool HasParameterList { + get { return hasParameterList || Parameters.Any(); } + set { ThrowIfFrozen(); hasParameterList = value; } + } + + public CSharpTokenNode DelegateToken { + get { return GetChildByRole (DelegateKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public AnonymousMethodExpression () + { + } + + public AnonymousMethodExpression (BlockStatement body, IEnumerable parameters = null) + { + if (parameters != null) { + hasParameterList = true; + foreach (var parameter in parameters) { + AddChild (parameter, Roles.Parameter); + } + } + AddChild (body, Roles.Body); + } + + public AnonymousMethodExpression (BlockStatement body, params ParameterDeclaration[] parameters) : this (body, (IEnumerable)parameters) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAnonymousMethodExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAnonymousMethodExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAnonymousMethodExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AnonymousMethodExpression o = other as AnonymousMethodExpression; + return o != null && this.IsAsync == o.IsAsync && this.HasParameterList == o.HasParameterList + && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs new file mode 100644 index 000000000..944bf61f5 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs @@ -0,0 +1,91 @@ +// +// AnonymousTypeCreateExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new { [ExpressionList] } + /// + public class AnonymousTypeCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Initializers { + get { return GetChildrenByRole (Roles.Expression); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AnonymousTypeCreateExpression () + { + } + + public AnonymousTypeCreateExpression (IEnumerable initializers) + { + foreach (var ini in initializers) { + AddChild (ini, Roles.Expression); + } + } + + public AnonymousTypeCreateExpression (params Expression[] initializer) : this ((IEnumerable)initializer) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAnonymousTypeCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAnonymousTypeCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAnonymousTypeCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as AnonymousTypeCreateExpression; + return o != null && this.Initializers.DoMatch(o.Initializers, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ArrayCreateExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ArrayCreateExpression.cs new file mode 100644 index 000000000..3720a3fc8 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ArrayCreateExpression.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new Type[Dimensions] + /// + public class ArrayCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + public readonly static Role AdditionalArraySpecifierRole = new Role("AdditionalArraySpecifier"); + public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + /// + /// Gets additional array ranks (those without size info). + /// Empty for "new int[5,1]"; will contain a single element for "new int[5][]". + /// + public AstNodeCollection AdditionalArraySpecifiers { + get { return GetChildrenByRole(AdditionalArraySpecifierRole); } + } + + public ArrayInitializerExpression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArrayCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArrayCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArrayCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArrayCreateExpression o = other as ArrayCreateExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.AdditionalArraySpecifiers.DoMatch(o.AdditionalArraySpecifiers, match) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ArrayInitializerExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ArrayInitializerExpression.cs new file mode 100644 index 000000000..fa3246f92 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ArrayInitializerExpression.cs @@ -0,0 +1,192 @@ +// +// ArrayInitializerExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// { Elements } + /// + public class ArrayInitializerExpression : Expression + { + /// + /// For ease of use purposes in the resolver the ast representation + /// of { a, b, c } is { {a}, {b}, {c} }. + /// If IsSingleElement is true then this array initializer expression is a generated one. + /// That has no meaning in the source code (and contains no brace tokens). + /// + public virtual bool IsSingleElement { + get { + return false; + } + } + + public ArrayInitializerExpression() + { + } + + public ArrayInitializerExpression(IEnumerable elements) + { + this.Elements.AddRange(elements); + } + + public ArrayInitializerExpression(params Expression[] elements) + { + this.Elements.AddRange(elements); + } + + #region Null + public new static readonly ArrayInitializerExpression Null = new NullArrayInitializerExpression (); + + sealed class NullArrayInitializerExpression : ArrayInitializerExpression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Elements { + get { return GetChildrenByRole(Roles.Expression); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArrayInitializerExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArrayInitializerExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArrayInitializerExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArrayInitializerExpression o = other as ArrayInitializerExpression; + return o != null && this.Elements.DoMatch(o.Elements, match); + } + + public static ArrayInitializerExpression CreateSingleElementInitializer () + { + return new SingleArrayInitializerExpression(); + } + /// + /// Single elements in array initializers are represented with this special class. + /// + class SingleArrayInitializerExpression : ArrayInitializerExpression + { + public override bool IsSingleElement { + get { + return true; + } + } + + } + + #region PatternPlaceholder + public static implicit operator ArrayInitializerExpression(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : ArrayInitializerExpression, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AsExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AsExpression.cs new file mode 100644 index 000000000..5a7b5ac5d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AsExpression.cs @@ -0,0 +1,150 @@ +// +// AsExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression as TypeReference + /// + public class AsExpression : Expression + { + public readonly static TokenRole AsKeywordRole = new TokenRole ("as"); + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode AsToken { + get { return GetChildByRole (AsKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public AsExpression () + { + } + + public AsExpression (Expression expression, AstType type) + { + AddChild (expression, Roles.Expression); + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAsExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAsExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAsExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AsExpression o = other as AsExpression; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AssignmentExpression.cs new file mode 100644 index 000000000..95d0cdf28 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/AssignmentExpression.cs @@ -0,0 +1,304 @@ +// +// AssignmentExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq.Expressions; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Left Operator= Right + /// + public class AssignmentExpression : Expression + { + // reuse roles from BinaryOperatorExpression + public readonly static Role LeftRole = BinaryOperatorExpression.LeftRole; + public readonly static Role RightRole = BinaryOperatorExpression.RightRole; + + public readonly static TokenRole AssignRole = new TokenRole ("="); + public readonly static TokenRole AddRole = new TokenRole ("+="); + public readonly static TokenRole SubtractRole = new TokenRole ("-="); + public readonly static TokenRole MultiplyRole = new TokenRole ("*="); + public readonly static TokenRole DivideRole = new TokenRole ("/="); + public readonly static TokenRole ModulusRole = new TokenRole ("%="); + public readonly static TokenRole ShiftLeftRole = new TokenRole ("<<="); + public readonly static TokenRole ShiftRightRole = new TokenRole (">>="); + public readonly static TokenRole BitwiseAndRole = new TokenRole ("&="); + public readonly static TokenRole BitwiseOrRole = new TokenRole ("|="); + public readonly static TokenRole ExclusiveOrRole = new TokenRole ("^="); + + public AssignmentExpression() + { + } + + public AssignmentExpression(Expression left, Expression right) + { + this.Left = left; + this.Right = right; + } + + public AssignmentExpression(Expression left, AssignmentOperatorType op, Expression right) + { + this.Left = left; + this.Operator = op; + this.Right = right; + } + + public AssignmentOperatorType Operator { + get; + set; + } + + public Expression Left { + get { return GetChildByRole (LeftRole); } + set { SetChildByRole(LeftRole, value); } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole(Operator)); } + } + + public Expression Right { + get { return GetChildByRole (RightRole); } + set { SetChildByRole(RightRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAssignmentExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAssignmentExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAssignmentExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AssignmentExpression o = other as AssignmentExpression; + return o != null && (this.Operator == AssignmentOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); + } + + public static TokenRole GetOperatorRole(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.Assign: + return AssignRole; + case AssignmentOperatorType.Add: + return AddRole; + case AssignmentOperatorType.Subtract: + return SubtractRole; + case AssignmentOperatorType.Multiply: + return MultiplyRole; + case AssignmentOperatorType.Divide: + return DivideRole; + case AssignmentOperatorType.Modulus: + return ModulusRole; + case AssignmentOperatorType.ShiftLeft: + return ShiftLeftRole; + case AssignmentOperatorType.ShiftRight: + return ShiftRightRole; + case AssignmentOperatorType.BitwiseAnd: + return BitwiseAndRole; + case AssignmentOperatorType.BitwiseOr: + return BitwiseOrRole; + case AssignmentOperatorType.ExclusiveOr: + return ExclusiveOrRole; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + /// + /// Gets the binary operator for the specified compound assignment operator. + /// Returns null if 'op' is not a compound assignment. + /// + public static BinaryOperatorType? GetCorrespondingBinaryOperator(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.Assign: + return null; + case AssignmentOperatorType.Add: + return BinaryOperatorType.Add; + case AssignmentOperatorType.Subtract: + return BinaryOperatorType.Subtract; + case AssignmentOperatorType.Multiply: + return BinaryOperatorType.Multiply; + case AssignmentOperatorType.Divide: + return BinaryOperatorType.Divide; + case AssignmentOperatorType.Modulus: + return BinaryOperatorType.Modulus; + case AssignmentOperatorType.ShiftLeft: + return BinaryOperatorType.ShiftLeft; + case AssignmentOperatorType.ShiftRight: + return BinaryOperatorType.ShiftRight; + case AssignmentOperatorType.BitwiseAnd: + return BinaryOperatorType.BitwiseAnd; + case AssignmentOperatorType.BitwiseOr: + return BinaryOperatorType.BitwiseOr; + case AssignmentOperatorType.ExclusiveOr: + return BinaryOperatorType.ExclusiveOr; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(AssignmentOperatorType op, bool checkForOverflow) + { + switch (op) { + case AssignmentOperatorType.Assign: + return ExpressionType.Assign; + case AssignmentOperatorType.Add: + return checkForOverflow ? ExpressionType.AddAssignChecked : ExpressionType.AddAssign; + case AssignmentOperatorType.Subtract: + return checkForOverflow ? ExpressionType.SubtractAssignChecked : ExpressionType.SubtractAssign; + case AssignmentOperatorType.Multiply: + return checkForOverflow ? ExpressionType.MultiplyAssignChecked : ExpressionType.MultiplyAssign; + case AssignmentOperatorType.Divide: + return ExpressionType.DivideAssign; + case AssignmentOperatorType.Modulus: + return ExpressionType.ModuloAssign; + case AssignmentOperatorType.ShiftLeft: + return ExpressionType.LeftShiftAssign; + case AssignmentOperatorType.ShiftRight: + return ExpressionType.RightShiftAssign; + case AssignmentOperatorType.BitwiseAnd: + return ExpressionType.AndAssign; + case AssignmentOperatorType.BitwiseOr: + return ExpressionType.OrAssign; + case AssignmentOperatorType.ExclusiveOr: + return ExpressionType.ExclusiveOrAssign; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public enum AssignmentOperatorType + { + /// left = right + Assign, + + /// left += right + Add, + /// left -= right + Subtract, + /// left *= right + Multiply, + /// left /= right + Divide, + /// left %= right + Modulus, + + /// left <<= right + ShiftLeft, + /// left >>= right + ShiftRight, + + /// left &= right + BitwiseAnd, + /// left |= right + BitwiseOr, + /// left ^= right + ExclusiveOr, + + /// Any operator (for pattern matching) + Any + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/BaseReferenceExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/BaseReferenceExpression.cs new file mode 100644 index 000000000..399c36c22 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/BaseReferenceExpression.cs @@ -0,0 +1,71 @@ +// +// BaseReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// base + /// + public class BaseReferenceExpression : Expression + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + "base".Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBaseReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBaseReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBaseReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BaseReferenceExpression o = other as BaseReferenceExpression; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/BinaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/BinaryOperatorExpression.cs new file mode 100644 index 000000000..e4408e184 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/BinaryOperatorExpression.cs @@ -0,0 +1,325 @@ +// +// BinaryOperatorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq.Expressions; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Left Operator Right + /// + public class BinaryOperatorExpression : Expression + { + public readonly static TokenRole BitwiseAndRole = new TokenRole ("&"); + public readonly static TokenRole BitwiseOrRole = new TokenRole ("|"); + public readonly static TokenRole ConditionalAndRole = new TokenRole ("&&"); + public readonly static TokenRole ConditionalOrRole = new TokenRole ("||"); + public readonly static TokenRole ExclusiveOrRole = new TokenRole ("^"); + public readonly static TokenRole GreaterThanRole = new TokenRole (">"); + public readonly static TokenRole GreaterThanOrEqualRole = new TokenRole (">="); + public readonly static TokenRole EqualityRole = new TokenRole ("=="); + public readonly static TokenRole InEqualityRole = new TokenRole ("!="); + public readonly static TokenRole LessThanRole = new TokenRole ("<"); + public readonly static TokenRole LessThanOrEqualRole = new TokenRole ("<="); + public readonly static TokenRole AddRole = new TokenRole ("+"); + public readonly static TokenRole SubtractRole = new TokenRole ("-"); + public readonly static TokenRole MultiplyRole = new TokenRole ("*"); + public readonly static TokenRole DivideRole = new TokenRole ("/"); + public readonly static TokenRole ModulusRole = new TokenRole ("%"); + public readonly static TokenRole ShiftLeftRole = new TokenRole ("<<"); + public readonly static TokenRole ShiftRightRole = new TokenRole (">>"); + public readonly static TokenRole NullCoalescingRole = new TokenRole ("??"); + + public readonly static Role LeftRole = new Role("Left", Expression.Null); + public readonly static Role RightRole = new Role("Right", Expression.Null); + + public BinaryOperatorExpression() + { + } + + public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right) + { + this.Left = left; + this.Operator = op; + this.Right = right; + } + + public BinaryOperatorType Operator { + get; + set; + } + + public Expression Left { + get { return GetChildByRole (LeftRole); } + set { SetChildByRole(LeftRole, value); } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole (Operator)); } + } + + public Expression Right { + get { return GetChildByRole (RightRole); } + set { SetChildByRole (RightRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBinaryOperatorExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBinaryOperatorExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBinaryOperatorExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BinaryOperatorExpression o = other as BinaryOperatorExpression; + return o != null && (this.Operator == BinaryOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); + } + + public static TokenRole GetOperatorRole (BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.BitwiseAnd: + return BitwiseAndRole; + case BinaryOperatorType.BitwiseOr: + return BitwiseOrRole; + case BinaryOperatorType.ConditionalAnd: + return ConditionalAndRole; + case BinaryOperatorType.ConditionalOr: + return ConditionalOrRole; + case BinaryOperatorType.ExclusiveOr: + return ExclusiveOrRole; + case BinaryOperatorType.GreaterThan: + return GreaterThanRole; + case BinaryOperatorType.GreaterThanOrEqual: + return GreaterThanOrEqualRole; + case BinaryOperatorType.Equality: + return EqualityRole; + case BinaryOperatorType.InEquality: + return InEqualityRole; + case BinaryOperatorType.LessThan: + return LessThanRole; + case BinaryOperatorType.LessThanOrEqual: + return LessThanOrEqualRole; + case BinaryOperatorType.Add: + return AddRole; + case BinaryOperatorType.Subtract: + return SubtractRole; + case BinaryOperatorType.Multiply: + return MultiplyRole; + case BinaryOperatorType.Divide: + return DivideRole; + case BinaryOperatorType.Modulus: + return ModulusRole; + case BinaryOperatorType.ShiftLeft: + return ShiftLeftRole; + case BinaryOperatorType.ShiftRight: + return ShiftRightRole; + case BinaryOperatorType.NullCoalescing: + return NullCoalescingRole; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(BinaryOperatorType op, bool checkForOverflow) + { + switch (op) { + case BinaryOperatorType.BitwiseAnd: + return ExpressionType.And; + case BinaryOperatorType.BitwiseOr: + return ExpressionType.Or; + case BinaryOperatorType.ConditionalAnd: + return ExpressionType.AndAlso; + case BinaryOperatorType.ConditionalOr: + return ExpressionType.OrElse; + case BinaryOperatorType.ExclusiveOr: + return ExpressionType.ExclusiveOr; + case BinaryOperatorType.GreaterThan: + return ExpressionType.GreaterThan; + case BinaryOperatorType.GreaterThanOrEqual: + return ExpressionType.GreaterThanOrEqual; + case BinaryOperatorType.Equality: + return ExpressionType.Equal; + case BinaryOperatorType.InEquality: + return ExpressionType.NotEqual; + case BinaryOperatorType.LessThan: + return ExpressionType.LessThan; + case BinaryOperatorType.LessThanOrEqual: + return ExpressionType.LessThanOrEqual; + case BinaryOperatorType.Add: + return checkForOverflow ? ExpressionType.AddChecked : ExpressionType.Add; + case BinaryOperatorType.Subtract: + return checkForOverflow ? ExpressionType.SubtractChecked : ExpressionType.Subtract; + case BinaryOperatorType.Multiply: + return checkForOverflow ? ExpressionType.MultiplyChecked : ExpressionType.Multiply; + case BinaryOperatorType.Divide: + return ExpressionType.Divide; + case BinaryOperatorType.Modulus: + return ExpressionType.Modulo; + case BinaryOperatorType.ShiftLeft: + return ExpressionType.LeftShift; + case BinaryOperatorType.ShiftRight: + return ExpressionType.RightShift; + case BinaryOperatorType.NullCoalescing: + return ExpressionType.Coalesce; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public enum BinaryOperatorType + { + /// + /// Any binary operator (used in pattern matching) + /// + Any, + + // We avoid 'logical or' on purpose, because it's not clear if that refers to the bitwise + // or to the short-circuiting (conditional) operator: + // MCS and old NRefactory used bitwise='|', logical='||' + // but the C# spec uses logical='|', conditional='||' + /// left & right + BitwiseAnd, + /// left | right + BitwiseOr, + /// left && right + ConditionalAnd, + /// left || right + ConditionalOr, + /// left ^ right + ExclusiveOr, + + /// left > right + GreaterThan, + /// left >= right + GreaterThanOrEqual, + /// left == right + Equality, + /// left != right + InEquality, + /// left < right + LessThan, + /// left <= right + LessThanOrEqual, + + /// left + right + Add, + /// left - right + Subtract, + /// left * right + Multiply, + /// left / right + Divide, + /// left % right + Modulus, + + /// left << right + ShiftLeft, + /// left >> right + ShiftRight, + + /// left ?? right + NullCoalescing + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/CastExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/CastExpression.cs new file mode 100644 index 000000000..e771d18fe --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/CastExpression.cs @@ -0,0 +1,152 @@ +// +// CastExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// (CastTo)Expression + /// + public class CastExpression : Expression + { + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CastExpression () + { + } + + public CastExpression (AstType castToType, Expression expression) + { + AddChild (castToType, Roles.Type); + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCastExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCastExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCastExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CastExpression o = other as CastExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Expression.DoMatch(o.Expression, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/CheckedExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/CheckedExpression.cs new file mode 100644 index 000000000..66bdcb54d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/CheckedExpression.cs @@ -0,0 +1,83 @@ +// +// CheckedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// checked(Expression) + /// + public class CheckedExpression : Expression + { + public readonly static TokenRole CheckedKeywordRole = new TokenRole ("checked"); + + public CSharpTokenNode CheckedToken { + get { return GetChildByRole (CheckedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CheckedExpression () + { + } + + public CheckedExpression (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCheckedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCheckedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCheckedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedExpression o = other as CheckedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ConditionalExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ConditionalExpression.cs new file mode 100644 index 000000000..4367a0cce --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ConditionalExpression.cs @@ -0,0 +1,162 @@ +// +// ConditionalExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Condition ? TrueExpression : FalseExpression + /// + public class ConditionalExpression : Expression + { + public readonly static Role ConditionRole = Roles.Condition; + public readonly static TokenRole QuestionMarkRole = new TokenRole("?"); + public readonly static Role TrueRole = new Role("True", Expression.Null); + public readonly static TokenRole ColonRole = Roles.Colon; + public readonly static Role FalseRole = new Role("False", Expression.Null); + + public Expression Condition { + get { return GetChildByRole(ConditionRole); } + set { SetChildByRole(ConditionRole, value); } + } + + public CSharpTokenNode QuestionMarkToken { + get { return GetChildByRole (QuestionMarkRole); } + } + + public Expression TrueExpression { + get { return GetChildByRole(TrueRole); } + set { SetChildByRole(TrueRole, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (ColonRole); } + } + + public Expression FalseExpression { + get { return GetChildByRole(FalseRole); } + set { SetChildByRole(FalseRole, value); } + } + + public ConditionalExpression () + { + } + + public ConditionalExpression (Expression condition, Expression trueExpression, Expression falseExpression) + { + AddChild (condition, ConditionRole); + AddChild (trueExpression, TrueRole); + AddChild (falseExpression, FalseRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConditionalExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConditionalExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConditionalExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConditionalExpression o = other as ConditionalExpression; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueExpression.DoMatch(o.TrueExpression, match) && this.FalseExpression.DoMatch(o.FalseExpression, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/DefaultValueExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/DefaultValueExpression.cs new file mode 100644 index 000000000..0aab343f3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/DefaultValueExpression.cs @@ -0,0 +1,84 @@ +// +// DefaultValueExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// default(Type) + /// + public class DefaultValueExpression : Expression + { + public readonly static TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public CSharpTokenNode DefaultToken { + get { return GetChildByRole (DefaultKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public DefaultValueExpression () + { + } + + public DefaultValueExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDefaultValueExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDefaultValueExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDefaultValueExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DefaultValueExpression o = other as DefaultValueExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/DirectionExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/DirectionExpression.cs new file mode 100644 index 000000000..a17c117bb --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/DirectionExpression.cs @@ -0,0 +1,89 @@ +// +// DirectionExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum FieldDirection + { + None, + Out, + Ref + } + + /// + /// ref Expression + /// + public class DirectionExpression : Expression + { + public readonly static TokenRole RefKeywordRole = new TokenRole ("ref"); + public readonly static TokenRole OutKeywordRole = new TokenRole ("out"); + + public FieldDirection FieldDirection { + get; + set; + } + + public CSharpTokenNode FieldDirectionToken { + get { return FieldDirection == ICSharpCode.NRefactory.CSharp.FieldDirection.Ref ? GetChildByRole (RefKeywordRole) : GetChildByRole (OutKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public DirectionExpression () + { + } + + public DirectionExpression (FieldDirection direction, Expression expression) + { + this.FieldDirection = direction; + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDirectionExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDirectionExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDirectionExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DirectionExpression o = other as DirectionExpression; + return o != null && this.FieldDirection == o.FieldDirection && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ErrorExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ErrorExpression.cs new file mode 100644 index 000000000..6054792e4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ErrorExpression.cs @@ -0,0 +1,129 @@ +// +// ErrorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Obsolete("This class is obsolete. Remove all referencing code.")] + public class EmptyExpression : AstNode + { + #region implemented abstract members of AstNode + + public override void AcceptVisitor(IAstVisitor visitor) + { + throw new NotImplementedException(); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + throw new NotImplementedException(); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + throw new NotImplementedException(); + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + throw new NotImplementedException(); + } + + public override NodeType NodeType { + get { + throw new NotImplementedException(); + } + } + + #endregion + + + } + + public class ErrorExpression : Expression + { + TextLocation location; + + public override TextLocation StartLocation { + get { + return location; + } + } + + public override TextLocation EndLocation { + get { + return location; + } + } + + public string Error { + get; + private set; + } + + public ErrorExpression () + { + } + + public ErrorExpression (TextLocation location) + { + this.location = location; + } + + public ErrorExpression (string error) + { + this.Error = error; + } + + public ErrorExpression (string error, TextLocation location) + { + this.location = location; + this.Error = error; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitErrorNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitErrorNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitErrorNode(this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as ErrorExpression; + return o != null; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/Expression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/Expression.cs new file mode 100644 index 000000000..22962a606 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/Expression.cs @@ -0,0 +1,230 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Base class for expressions. + /// + /// + /// This class is useful even though it doesn't provide any additional functionality: + /// It can be used to communicate more information in APIs, e.g. "this subnode will always be an expression" + /// + public abstract class Expression : AstNode + { + #region Null + public new static readonly Expression Null = new NullExpression (); + + sealed class NullExpression : Expression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator Expression(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : Expression, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Expression; + } + } + + public new Expression Clone() + { + return (Expression)base.Clone(); + } + + public Expression ReplaceWith(Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException("replaceFunction"); + return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); + } + + #region Builder methods + /// + /// Builds an member reference expression using this expression as target. + /// + public virtual MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + /// + /// Builds an indexer expression using this expression as target. + /// + public virtual IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; + } + + /// + /// Builds an indexer expression using this expression as target. + /// + public virtual IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return Invoke(methodName, null, arguments); + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return Invoke(methodName, null, arguments); + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = this; + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; + } + + public virtual CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = this }; + } + + public virtual AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = this }; + } + + public virtual IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = this }; + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IdentifierExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IdentifierExpression.cs new file mode 100644 index 000000000..0ec466c3f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IdentifierExpression.cs @@ -0,0 +1,93 @@ +// +// IdentifierExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class IdentifierExpression : Expression + { + public IdentifierExpression() + { + } + + public IdentifierExpression(string identifier) + { + this.Identifier = identifier; + } + + public IdentifierExpression(string identifier, TextLocation location) + { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (identifier, location)); + } + +// public Identifier IdentifierToken { +// get { return GetChildByRole (Roles.Identifier); } +// } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIdentifierExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIdentifierExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIdentifierExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IdentifierExpression o = other as IdentifierExpression; + return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IndexerExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IndexerExpression.cs new file mode 100644 index 000000000..cbc80c2cf --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IndexerExpression.cs @@ -0,0 +1,92 @@ +// +// IndexerExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target[Arguments] + /// + public class IndexerExpression : Expression + { + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public IndexerExpression () + { + } + + public IndexerExpression (Expression target, IEnumerable arguments) + { + AddChild (target, Roles.TargetExpression); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public IndexerExpression (Expression target, params Expression[] arguments) : this (target, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIndexerExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIndexerExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIndexerExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IndexerExpression o = other as IndexerExpression; + return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/InvocationExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/InvocationExpression.cs new file mode 100644 index 000000000..ad768d541 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/InvocationExpression.cs @@ -0,0 +1,92 @@ +// +// InvocationExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target(Arguments) + /// + public class InvocationExpression : Expression + { + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitInvocationExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitInvocationExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitInvocationExpression (this, data); + } + + public InvocationExpression () + { + } + + public InvocationExpression (Expression target, IEnumerable arguments) + { + AddChild (target, Roles.TargetExpression); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public InvocationExpression (Expression target, params Expression[] arguments) : this (target, (IEnumerable)arguments) + { + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + InvocationExpression o = other as InvocationExpression; + return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IsExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IsExpression.cs new file mode 100644 index 000000000..791ab25d7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/IsExpression.cs @@ -0,0 +1,150 @@ +// +// TypeOfIsExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression is Type + /// + public class IsExpression : Expression + { + public readonly static TokenRole IsKeywordRole = new TokenRole ("is"); + + public Expression Expression { + get { return GetChildByRole(Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode IsToken { + get { return GetChildByRole (IsKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public IsExpression() + { + } + + public IsExpression (Expression expression, AstType type) + { + AddChild (expression, Roles.Expression); + AddChild (type, Roles.Type); + } + + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIsExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIsExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIsExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IsExpression o = other as IsExpression; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/LambdaExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/LambdaExpression.cs new file mode 100644 index 000000000..e85902d84 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/LambdaExpression.cs @@ -0,0 +1,89 @@ +// +// LambdaExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [async] Parameters => Body + /// + public class LambdaExpression : Expression + { + public readonly static TokenRole AsyncModifierRole = new TokenRole ("async"); + public readonly static TokenRole ArrowRole = new TokenRole ("=>"); + public static readonly Role BodyRole = new Role("Body", AstNode.Null); + + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode ArrowToken { + get { return GetChildByRole (ArrowRole); } + } + + public AstNode Body { + get { return GetChildByRole (BodyRole); } + set { SetChildByRole (BodyRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLambdaExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLambdaExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLambdaExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LambdaExpression o = other as LambdaExpression; + return o != null && this.IsAsync == o.IsAsync && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/MemberReferenceExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/MemberReferenceExpression.cs new file mode 100644 index 000000000..334c6a260 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/MemberReferenceExpression.cs @@ -0,0 +1,119 @@ +// +// MemberReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target.MemberName + /// + public class MemberReferenceExpression : Expression + { + public Expression Target { + get { + return GetChildByRole(Roles.TargetExpression); + } + set { + SetChildByRole(Roles.TargetExpression, value); + } + } + + public CSharpTokenNode DotToken { + get { return GetChildByRole (Roles.Dot); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode LChevronToken { + get { return GetChildByRole (Roles.LChevron); } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public CSharpTokenNode RChevronToken { + get { return GetChildByRole (Roles.RChevron); } + } + + public MemberReferenceExpression () + { + } + + public MemberReferenceExpression (Expression target, string memberName, IEnumerable arguments = null) + { + AddChild (target, Roles.TargetExpression); + MemberName = memberName; + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.TypeArgument); + } + } + } + + public MemberReferenceExpression (Expression target, string memberName, params AstType[] arguments) : this (target, memberName, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMemberReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMemberReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMemberReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MemberReferenceExpression o = other as MemberReferenceExpression; + return o != null && this.Target.DoMatch(o.Target, match) && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NamedArgumentExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NamedArgumentExpression.cs new file mode 100644 index 000000000..6b485f015 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NamedArgumentExpression.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a named argument passed to a method or attribute. + /// name: expression + /// + public class NamedArgumentExpression : Expression + { + public NamedArgumentExpression() + { + } + + public NamedArgumentExpression(string name, Expression expression) + { + this.Name = name; + this.Expression = expression; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNamedArgumentExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNamedArgumentExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamedArgumentExpression(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NamedArgumentExpression o = other as NamedArgumentExpression; + return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NamedExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NamedExpression.cs new file mode 100644 index 000000000..92bc993b1 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NamedExpression.cs @@ -0,0 +1,97 @@ +// +// NamedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// name = expression + /// This isn't the same as 'assign' even though it has the same syntax. + /// This expression is used in object initializers and for named attribute arguments [Attr(FieldName = value)]. + /// + public class NamedExpression : Expression + { + public NamedExpression() + { + } + + public NamedExpression (string name, Expression expression) + { + this.Name = name; + this.Expression = expression; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNamedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNamedExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamedExpression(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as NamedExpression; + return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NullReferenceExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NullReferenceExpression.cs new file mode 100644 index 000000000..fbfeb6f91 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/NullReferenceExpression.cs @@ -0,0 +1,83 @@ +// +// NullReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// null + /// + public class NullReferenceExpression : Expression + { + TextLocation location; + public override TextLocation StartLocation { + get { + return location; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.location = value; + } + + public override TextLocation EndLocation { + get { + return new TextLocation (location.Line, location.Column + "null".Length); + } + } + + public NullReferenceExpression () + { + } + + public NullReferenceExpression (TextLocation location) + { + this.location = location; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NullReferenceExpression o = other as NullReferenceExpression; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ObjectCreateExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ObjectCreateExpression.cs new file mode 100644 index 000000000..a9665f8c7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ObjectCreateExpression.cs @@ -0,0 +1,104 @@ +// +// ObjectCreateExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new Type(Arguments) { Initializer } + /// + public class ObjectCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + public readonly static Role InitializerRole = ArrayCreateExpression.InitializerRole; + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public ArrayInitializerExpression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public ObjectCreateExpression () + { + } + + public ObjectCreateExpression (AstType type, IEnumerable arguments = null) + { + AddChild (type, Roles.Type); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public ObjectCreateExpression (AstType type, params Expression[] arguments) : this (type, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitObjectCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitObjectCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitObjectCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ObjectCreateExpression o = other as ObjectCreateExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ParenthesizedExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ParenthesizedExpression.cs new file mode 100644 index 000000000..7dddcb3fb --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ParenthesizedExpression.cs @@ -0,0 +1,98 @@ +// +// ParenthesizedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// ( Expression ) + /// + public class ParenthesizedExpression : Expression + { + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public ParenthesizedExpression() + { + } + + public ParenthesizedExpression(Expression expr) + { + Expression = expr; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitParenthesizedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitParenthesizedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitParenthesizedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ParenthesizedExpression o = other as ParenthesizedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + + /// + /// Gets whether the expression acts like a parenthesized expression, + /// i.e. whether information about the expected type (for lambda type inference) flows + /// into the inner expression. + /// + /// Returns true for ParenthesizedExpression, CheckedExpression or UncheckedExpression; false otherwise. + public static bool ActsAsParenthesizedExpression(AstNode expression) + { + return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; + } + + /// + /// Unpacks the given expression if it is a ParenthesizedExpression, CheckedExpression or UncheckedExpression. + /// + public static Expression UnpackParenthesizedExpression(Expression expr) + { + while (ActsAsParenthesizedExpression(expr)) + expr = expr.GetChildByRole(Roles.Expression); + return expr; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/PointerReferenceExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/PointerReferenceExpression.cs new file mode 100644 index 000000000..35c4a7203 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/PointerReferenceExpression.cs @@ -0,0 +1,90 @@ +// +// PointerReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target->MemberName + /// + public class PointerReferenceExpression : Expression + { + public readonly static TokenRole ArrowRole = new TokenRole ("->"); + + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode ArrowToken { + get { return GetChildByRole (ArrowRole); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPointerReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPointerReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPointerReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PointerReferenceExpression o = other as PointerReferenceExpression; + return o != null && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/PrimitiveExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/PrimitiveExpression.cs new file mode 100644 index 000000000..adfe7c3ba --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/PrimitiveExpression.cs @@ -0,0 +1,162 @@ +// +// PrimitiveExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a literal value. + /// + public class PrimitiveExpression : Expression + { + public static readonly object AnyValue = new object(); + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.startLocation = value; + this.endLocation = null; + } + + string literalValue; + TextLocation? endLocation; + public override TextLocation EndLocation { + get { + if (!endLocation.HasValue) { + endLocation = value is string ? AdvanceLocation (StartLocation, literalValue ?? "") : + new TextLocation (StartLocation.Line, StartLocation.Column + (literalValue ?? "").Length); + } + return endLocation.Value; + } + } + + object value; + + public object Value { + get { return this.value; } + set { + ThrowIfFrozen(); + this.value = value; + literalValue = null; + } + } + + /// Never returns null. + public string LiteralValue { + get { return literalValue ?? ""; } + } + + /// Can be null. + public string UnsafeLiteralValue { + get { return literalValue; } + } + + public void SetValue(object value, string literalValue) + { + if (value == null) + throw new ArgumentNullException(); + ThrowIfFrozen(); + this.value = value; + this.literalValue = literalValue; + } + + public PrimitiveExpression (object value) + { + this.Value = value; + this.literalValue = null; + } + + public PrimitiveExpression (object value, string literalValue) + { + this.Value = value; + this.literalValue = literalValue; + } + + public PrimitiveExpression (object value, TextLocation startLocation, string literalValue) + { + this.Value = value; + this.startLocation = startLocation; + this.literalValue = literalValue; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPrimitiveExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPrimitiveExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPrimitiveExpression (this, data); + } + + unsafe static TextLocation AdvanceLocation(TextLocation startLocation, string str) + { + int line = startLocation.Line; + int col = startLocation.Column; + fixed (char* start = str) { + char* p = start; + char* endPtr = start + str.Length; + while (p < endPtr) { + var nl = NewLine.GetDelimiterLength(*p, () => { + char* nextp = p + 1; + if (nextp < endPtr) + return *nextp; + return '\0'; + }); + if (nl > 0) { + line++; + col = 1; + if (nl == 2) + p++; + } else { + col++; + } + p++; + } + } + return new TextLocation (line, col); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PrimitiveExpression o = other as PrimitiveExpression; + return o != null && (this.Value == AnyValue || object.Equals(this.Value, o.Value)); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/QueryExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/QueryExpression.cs new file mode 100644 index 000000000..b52f50a47 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/QueryExpression.cs @@ -0,0 +1,655 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +namespace ICSharpCode.NRefactory.CSharp +{ + public class QueryExpression : Expression + { + public static readonly Role ClauseRole = new Role("Clause"); + + #region Null + public new static readonly QueryExpression Null = new NullQueryExpression (); + + sealed class NullQueryExpression : QueryExpression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public AstNodeCollection Clauses { + get { return GetChildrenByRole(ClauseRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitQueryExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryExpression o = other as QueryExpression; + return o != null && !o.IsNull && this.Clauses.DoMatch(o.Clauses, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public abstract class QueryClause : AstNode + { + public override NodeType NodeType { + get { return NodeType.QueryClause; } + } + } + + /// + /// Represents a query continuation. + /// "(from .. select ..) into Identifier" or "(from .. group .. by ..) into Identifier" + /// Note that "join .. into .." is not a query continuation! + /// + /// This is always the first(!!) clause in a query expression. + /// The tree for "from a in b select c into d select e" looks like this: + /// new QueryExpression { + /// new QueryContinuationClause { + /// PrecedingQuery = new QueryExpression { + /// new QueryFromClause(a in b), + /// new QuerySelectClause(c) + /// }, + /// Identifier = d + /// }, + /// new QuerySelectClause(e) + /// } + /// + public class QueryContinuationClause : QueryClause + { + public static readonly Role PrecedingQueryRole = new Role("PrecedingQuery", QueryExpression.Null); + public static readonly TokenRole IntoKeywordRole = new TokenRole ("into"); + + public QueryExpression PrecedingQuery { + get { return GetChildByRole(PrecedingQueryRole); } + set { SetChildByRole(PrecedingQueryRole, value); } + } + + public CSharpTokenNode IntoKeyword { + get { return GetChildByRole (IntoKeywordRole); } + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole (Roles.Identifier); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryContinuationClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryContinuationClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryContinuationClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryContinuationClause o = other as QueryContinuationClause; + return o != null && MatchString(this.Identifier, o.Identifier) && this.PrecedingQuery.DoMatch(o.PrecedingQuery, match); + } + } + + public class QueryFromClause : QueryClause + { + public static readonly TokenRole FromKeywordRole = new TokenRole ("from"); + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + + public CSharpTokenNode FromKeyword { + get { return GetChildByRole (FromKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole(Roles.Identifier); } + } + + public CSharpTokenNode InKeyword { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryFromClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryFromClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryFromClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryFromClause o = other as QueryFromClause; + return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.Identifier, o.Identifier) + && this.Expression.DoMatch(o.Expression, match); + } + } + + public class QueryLetClause : QueryClause + { + public readonly static TokenRole LetKeywordRole = new TokenRole ("let"); + + public CSharpTokenNode LetKeyword { + get { return GetChildByRole(LetKeywordRole); } + } + + public string Identifier { + get { + return GetChildByRole(Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole(Roles.Identifier); } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole(Roles.Assign); } + } + + public Expression Expression { + get { return GetChildByRole(Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryLetClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryLetClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryLetClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryLetClause o = other as QueryLetClause; + return o != null && MatchString(this.Identifier, o.Identifier) && this.Expression.DoMatch(o.Expression, match); + } + } + + + public class QueryWhereClause : QueryClause + { + public readonly static TokenRole WhereKeywordRole = new TokenRole ("where"); + + public CSharpTokenNode WhereKeyword { + get { return GetChildByRole (WhereKeywordRole); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryWhereClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryWhereClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryWhereClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryWhereClause o = other as QueryWhereClause; + return o != null && this.Condition.DoMatch(o.Condition, match); + } + } + + /// + /// Represents a join or group join clause. + /// + public class QueryJoinClause : QueryClause + { + public static readonly TokenRole JoinKeywordRole = new TokenRole ("join"); + public static readonly Role TypeRole = Roles.Type; + public static readonly Role JoinIdentifierRole = Roles.Identifier; + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + public static readonly Role InExpressionRole = Roles.Expression; + public static readonly TokenRole OnKeywordRole = new TokenRole ("on"); + public static readonly Role OnExpressionRole = new Role("OnExpression", Expression.Null); + public static readonly TokenRole EqualsKeywordRole = new TokenRole ("equals"); + public static readonly Role EqualsExpressionRole = new Role("EqualsExpression", Expression.Null); + public static readonly TokenRole IntoKeywordRole = new TokenRole ("into"); + public static readonly Role IntoIdentifierRole = new Role("IntoIdentifier", Identifier.Null); + + public bool IsGroupJoin { + get { return !string.IsNullOrEmpty(this.IntoIdentifier); } + } + + public CSharpTokenNode JoinKeyword { + get { return GetChildByRole (JoinKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (TypeRole); } + set { SetChildByRole (TypeRole, value); } + } + + public string JoinIdentifier { + get { + return GetChildByRole(JoinIdentifierRole).Name; + } + set { + SetChildByRole(JoinIdentifierRole, Identifier.Create (value)); + } + } + + public Identifier JoinIdentifierToken { + get { return GetChildByRole(JoinIdentifierRole); } + } + + public CSharpTokenNode InKeyword { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression InExpression { + get { return GetChildByRole (InExpressionRole); } + set { SetChildByRole (InExpressionRole, value); } + } + + public CSharpTokenNode OnKeyword { + get { return GetChildByRole (OnKeywordRole); } + } + + public Expression OnExpression { + get { return GetChildByRole (OnExpressionRole); } + set { SetChildByRole (OnExpressionRole, value); } + } + + public CSharpTokenNode EqualsKeyword { + get { return GetChildByRole (EqualsKeywordRole); } + } + + public Expression EqualsExpression { + get { return GetChildByRole (EqualsExpressionRole); } + set { SetChildByRole (EqualsExpressionRole, value); } + } + + public CSharpTokenNode IntoKeyword { + get { return GetChildByRole (IntoKeywordRole); } + } + + public string IntoIdentifier { + get { + return GetChildByRole (IntoIdentifierRole).Name; + } + set { + SetChildByRole(IntoIdentifierRole, Identifier.Create (value)); + } + } + + public Identifier IntoIdentifierToken { + get { return GetChildByRole(IntoIdentifierRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryJoinClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryJoinClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryJoinClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryJoinClause o = other as QueryJoinClause; + return o != null && this.IsGroupJoin == o.IsGroupJoin + && this.Type.DoMatch(o.Type, match) && MatchString(this.JoinIdentifier, o.JoinIdentifier) + && this.InExpression.DoMatch(o.InExpression, match) && this.OnExpression.DoMatch(o.OnExpression, match) + && this.EqualsExpression.DoMatch(o.EqualsExpression, match) + && MatchString(this.IntoIdentifier, o.IntoIdentifier); + } + } + + public class QueryOrderClause : QueryClause + { + public static readonly TokenRole OrderbyKeywordRole = new TokenRole ("orderby"); + public static readonly Role OrderingRole = new Role("Ordering"); + + public CSharpTokenNode OrderbyToken { + get { return GetChildByRole (OrderbyKeywordRole); } + } + + public AstNodeCollection Orderings { + get { return GetChildrenByRole (OrderingRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryOrderClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryOrderClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryOrderClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryOrderClause o = other as QueryOrderClause; + return o != null && this.Orderings.DoMatch(o.Orderings, match); + } + } + + public class QueryOrdering : AstNode + { + public readonly static TokenRole AscendingKeywordRole = new TokenRole ("ascending"); + public readonly static TokenRole DescendingKeywordRole = new TokenRole ("descending"); + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public QueryOrderingDirection Direction { + get; + set; + } + + public CSharpTokenNode DirectionToken { + get { return Direction == QueryOrderingDirection.Ascending ? GetChildByRole (AscendingKeywordRole) : GetChildByRole (DescendingKeywordRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryOrdering (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryOrdering (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryOrdering (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryOrdering o = other as QueryOrdering; + return o != null && this.Direction == o.Direction && this.Expression.DoMatch(o.Expression, match); + } + } + + public enum QueryOrderingDirection + { + None, + Ascending, + Descending + } + + public class QuerySelectClause : QueryClause + { + public readonly static TokenRole SelectKeywordRole = new TokenRole ("select"); + + public CSharpTokenNode SelectKeyword { + get { return GetChildByRole (SelectKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQuerySelectClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQuerySelectClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQuerySelectClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QuerySelectClause o = other as QuerySelectClause; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } + + public class QueryGroupClause : QueryClause + { + public static readonly TokenRole GroupKeywordRole = new TokenRole ("group"); + public static readonly Role ProjectionRole = new Role("Projection", Expression.Null); + public static readonly TokenRole ByKeywordRole = new TokenRole ("by"); + public static readonly Role KeyRole = new Role("Key", Expression.Null); + + public CSharpTokenNode GroupKeyword { + get { return GetChildByRole (GroupKeywordRole); } + } + + public Expression Projection { + get { return GetChildByRole (ProjectionRole); } + set { SetChildByRole (ProjectionRole, value); } + } + + public CSharpTokenNode ByKeyword { + get { return GetChildByRole (ByKeywordRole); } + } + + public Expression Key { + get { return GetChildByRole (KeyRole); } + set { SetChildByRole (KeyRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryGroupClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryGroupClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryGroupClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryGroupClause o = other as QueryGroupClause; + return o != null && this.Projection.DoMatch(o.Projection, match) && this.Key.DoMatch(o.Key, match); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/SizeOfExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/SizeOfExpression.cs new file mode 100644 index 000000000..8a794960c --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/SizeOfExpression.cs @@ -0,0 +1,83 @@ +// +// SizeOfExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// sizeof(Type) + /// + public class SizeOfExpression : Expression + { + public readonly static TokenRole SizeofKeywordRole = new TokenRole ("sizeof"); + + public CSharpTokenNode SizeOfToken { + get { return GetChildByRole (SizeofKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public SizeOfExpression () + { + } + + public SizeOfExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSizeOfExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSizeOfExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSizeOfExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SizeOfExpression o = other as SizeOfExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/StackAllocExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/StackAllocExpression.cs new file mode 100644 index 000000000..ad9f58c1b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/StackAllocExpression.cs @@ -0,0 +1,79 @@ +// +// StackAllocExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// stackalloc Type[Count] + /// + public class StackAllocExpression : Expression + { + public readonly static TokenRole StackallocKeywordRole = new TokenRole ("stackalloc"); + + public CSharpTokenNode StackAllocToken { + get { return GetChildByRole (StackallocKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public Expression CountExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitStackAllocExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitStackAllocExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitStackAllocExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + StackAllocExpression o = other as StackAllocExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.CountExpression.DoMatch(o.CountExpression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ThisReferenceExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ThisReferenceExpression.cs new file mode 100644 index 000000000..481ef6658 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/ThisReferenceExpression.cs @@ -0,0 +1,71 @@ +// +// ThisReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// this + /// + public class ThisReferenceExpression : Expression + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + "this".Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitThisReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitThisReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitThisReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThisReferenceExpression o = other as ThisReferenceExpression; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/TypeOfExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/TypeOfExpression.cs new file mode 100644 index 000000000..fd2e93ab8 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/TypeOfExpression.cs @@ -0,0 +1,84 @@ +// +// TypeOfExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// typeof(Type) + /// + public class TypeOfExpression : Expression + { + public readonly static TokenRole TypeofKeywordRole = new TokenRole ("typeof"); + + public CSharpTokenNode TypeOfToken { + get { return GetChildByRole (TypeofKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public TypeOfExpression () + { + } + + public TypeOfExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeOfExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeOfExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTypeOfExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeOfExpression o = other as TypeOfExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/TypeReferenceExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/TypeReferenceExpression.cs new file mode 100644 index 000000000..84b2d60dc --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/TypeReferenceExpression.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents an AstType as an expression. + /// This is used when calling a method on a primitive type: "int.Parse()" + /// + public class TypeReferenceExpression : Expression + { + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeReferenceExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTypeReferenceExpression(this, data); + } + + public TypeReferenceExpression () + { + } + + public TypeReferenceExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeReferenceExpression o = other as TypeReferenceExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UnaryOperatorExpression.cs new file mode 100644 index 000000000..878d6132f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UnaryOperatorExpression.cs @@ -0,0 +1,181 @@ +// +// UnaryOperatorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq.Expressions; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Operator Expression + /// + public class UnaryOperatorExpression : Expression + { + public readonly static TokenRole NotRole = new TokenRole ("!"); + public readonly static TokenRole BitNotRole = new TokenRole ("~"); + public readonly static TokenRole MinusRole = new TokenRole ("-"); + public readonly static TokenRole PlusRole = new TokenRole ("+"); + public readonly static TokenRole IncrementRole = new TokenRole ("++"); + public readonly static TokenRole DecrementRole = new TokenRole ("--"); + public readonly static TokenRole DereferenceRole = new TokenRole ("*"); + public readonly static TokenRole AddressOfRole = new TokenRole ("&"); + public readonly static TokenRole AwaitRole = new TokenRole ("await"); + + public UnaryOperatorExpression() + { + } + + public UnaryOperatorExpression(UnaryOperatorType op, Expression expression) + { + this.Operator = op; + this.Expression = expression; + } + + public UnaryOperatorType Operator { + get; + set; + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole (Operator)); } + } + + static Expression NoUnaryExpressionError = new ErrorExpression ("No unary expression"); + public Expression Expression { + get { return GetChildByRole (Roles.Expression) ?? NoUnaryExpressionError; } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUnaryOperatorExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUnaryOperatorExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUnaryOperatorExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnaryOperatorExpression o = other as UnaryOperatorExpression; + return o != null && (this.Operator == UnaryOperatorType.Any || this.Operator == o.Operator) + && this.Expression.DoMatch(o.Expression, match); + } + + public static TokenRole GetOperatorRole(UnaryOperatorType op) + { + switch (op) { + case UnaryOperatorType.Not: + return NotRole; + case UnaryOperatorType.BitNot: + return BitNotRole; + case UnaryOperatorType.Minus: + return MinusRole; + case UnaryOperatorType.Plus: + return PlusRole; + case UnaryOperatorType.Increment: + case UnaryOperatorType.PostIncrement: + return IncrementRole; + case UnaryOperatorType.PostDecrement: + case UnaryOperatorType.Decrement: + return DecrementRole; + case UnaryOperatorType.Dereference: + return DereferenceRole; + case UnaryOperatorType.AddressOf: + return AddressOfRole; + case UnaryOperatorType.Await: + return AwaitRole; + default: + throw new NotSupportedException("Invalid value for UnaryOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(UnaryOperatorType op, bool checkForOverflow) + { + switch (op) { + case UnaryOperatorType.Not: + return ExpressionType.Not; + case UnaryOperatorType.BitNot: + return ExpressionType.OnesComplement; + case UnaryOperatorType.Minus: + return checkForOverflow ? ExpressionType.NegateChecked : ExpressionType.Negate; + case UnaryOperatorType.Plus: + return ExpressionType.UnaryPlus; + case UnaryOperatorType.Increment: + return ExpressionType.PreIncrementAssign; + case UnaryOperatorType.Decrement: + return ExpressionType.PreDecrementAssign; + case UnaryOperatorType.PostIncrement: + return ExpressionType.PostIncrementAssign; + case UnaryOperatorType.PostDecrement: + return ExpressionType.PostDecrementAssign; + case UnaryOperatorType.Dereference: + case UnaryOperatorType.AddressOf: + case UnaryOperatorType.Await: + return ExpressionType.Extension; + default: + throw new NotSupportedException("Invalid value for UnaryOperatorType"); + } + } + } + + public enum UnaryOperatorType + { + /// + /// Any unary operator (used in pattern matching) + /// + Any, + + /// Logical not (!a) + Not, + /// Bitwise not (~a) + BitNot, + /// Unary minus (-a) + Minus, + /// Unary plus (+a) + Plus, + /// Pre increment (++a) + Increment, + /// Pre decrement (--a) + Decrement, + /// Post increment (a++) + PostIncrement, + /// Post decrement (a--) + PostDecrement, + /// Dereferencing (*a) + Dereference, + /// Get address (&a) + AddressOf, + /// C# 5.0 await + Await + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UncheckedExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UncheckedExpression.cs new file mode 100644 index 000000000..5b8686a26 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UncheckedExpression.cs @@ -0,0 +1,83 @@ +// +// UncheckedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unchecked(Expression) + /// + public class UncheckedExpression : Expression + { + public readonly static TokenRole UncheckedKeywordRole = new TokenRole ("unchecked"); + + public CSharpTokenNode UncheckedToken { + get { return GetChildByRole (UncheckedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public UncheckedExpression () + { + } + + public UncheckedExpression (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUncheckedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUncheckedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUncheckedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedExpression o = other as UncheckedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UndocumentedExpression.cs b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UndocumentedExpression.cs new file mode 100644 index 000000000..0efc0d70f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Expressions/UndocumentedExpression.cs @@ -0,0 +1,105 @@ +// +// UndocumentedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum UndocumentedExpressionType + { + ArgListAccess, // __arglist + ArgList, // __arglist (a1, a2, ..., an) + RefValue, // __refvalue (expr , type) + RefType, // __reftype (expr) + MakeRef // __makeref (expr) + } + + /// + /// Represents undocumented expressions. + /// + public class UndocumentedExpression : Expression + { + public readonly static TokenRole ArglistKeywordRole = new TokenRole ("__arglist"); + public readonly static TokenRole RefvalueKeywordRole = new TokenRole ("__refvalue"); + public readonly static TokenRole ReftypeKeywordRole = new TokenRole ("__reftype"); + public readonly static TokenRole MakerefKeywordRole = new TokenRole ("__makeref"); + + public UndocumentedExpressionType UndocumentedExpressionType { + get; set; + } + + public CSharpTokenNode UndocumentedToken { + get { + switch (UndocumentedExpressionType) { + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.ArgListAccess: + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.ArgList: + return GetChildByRole (ArglistKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.RefValue: + return GetChildByRole (RefvalueKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.RefType: + return GetChildByRole (ReftypeKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.MakeRef: + return GetChildByRole (MakerefKeywordRole); + } + return CSharpTokenNode.Null; + } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUndocumentedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUndocumentedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUndocumentedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UndocumentedExpression o = other as UndocumentedExpression; + return o != null && this.UndocumentedExpressionType == o.UndocumentedExpressionType && this.Arguments.DoMatch(o.Arguments, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Attribute.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Attribute.cs new file mode 100644 index 000000000..cc99936e5 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Attribute.cs @@ -0,0 +1,93 @@ +// +// Attribute.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Attribute(Arguments) + /// + public class Attribute : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return base.GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + // HasArgumentList == false: [Empty] + public bool HasArgumentList { + get; + set; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAttribute (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAttribute (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAttribute (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + Attribute o = other as Attribute; + return o != null && this.Type.DoMatch (o.Type, match) && this.Arguments.DoMatch (o.Arguments, match); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + if (IsNull) + return "Null"; + return base.ToString(formattingOptions); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/AttributeSection.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/AttributeSection.cs new file mode 100644 index 000000000..67b04412b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/AttributeSection.cs @@ -0,0 +1,174 @@ +// +// AttributeSection.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [AttributeTarget: Attributes] + /// + public class AttributeSection : AstNode + { + #region PatternPlaceholder + public static implicit operator AttributeSection(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : AttributeSection, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public string AttributeTarget { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier AttributeTargetToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole (Roles.Attribute); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAttributeSection (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAttributeSection (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAttributeSection (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AttributeSection o = other as AttributeSection; + return o != null && MatchString(this.AttributeTarget, o.AttributeTarget) && this.Attributes.DoMatch(o.Attributes, match); + } + + public AttributeSection() + { + } + + public AttributeSection(Attribute attr) + { + this.Attributes.Add(attr); + } + +// public static string GetAttributeTargetName(AttributeTarget attributeTarget) +// { +// switch (attributeTarget) { +// case AttributeTarget.None: +// return null; +// case AttributeTarget.Assembly: +// return "assembly"; +// case AttributeTarget.Module: +// return "module"; +// case AttributeTarget.Type: +// return "type"; +// case AttributeTarget.Param: +// return "param"; +// case AttributeTarget.Field: +// return "field"; +// case AttributeTarget.Return: +// return "return"; +// case AttributeTarget.Method: +// return "method"; +// default: +// throw new NotSupportedException("Invalid value for AttributeTarget"); +// } +// } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Comment.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Comment.cs new file mode 100644 index 000000000..9d53f6e66 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Comment.cs @@ -0,0 +1,140 @@ +// +// Comment.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum CommentType + { + /// + /// "//" comment + /// + SingleLine, + /// + /// "/* */" comment + /// + MultiLine, + /// + /// "///" comment + /// + Documentation, + /// + /// Inactive code (code in non-taken "#if") + /// + InactiveCode, + /// + /// "/** */" comment + /// + MultiLineDocumentation + } + + public class Comment : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + CommentType commentType; + + public CommentType CommentType { + get { return commentType; } + set { ThrowIfFrozen(); commentType = value; } + } + + /// + /// Returns true if the is Documentation or MultiLineDocumentation. + /// + public bool IsDocumentation { + get { + return commentType == CommentType.Documentation || commentType == CommentType.MultiLineDocumentation; + } + } + + bool startsLine; + + public bool StartsLine { + get { return startsLine; } + set { ThrowIfFrozen(); startsLine = value; } + } + + string content; + + public string Content { + get { return content; } + set { ThrowIfFrozen(); content = value; } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public Comment (string content, CommentType type = CommentType.SingleLine) + { + this.CommentType = type; + this.Content = content; + } + + public Comment (CommentType commentType, TextLocation startLocation, TextLocation endLocation) + { + this.CommentType = commentType; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitComment (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitComment (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitComment (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Comment o = other as Comment; + return o != null && this.CommentType == o.CommentType && MatchString(this.Content, o.Content); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Constraint.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Constraint.cs new file mode 100644 index 000000000..c49930427 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/Constraint.cs @@ -0,0 +1,85 @@ +// +// Constraint.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// where TypeParameter : BaseTypes + /// + /// + /// new(), struct and class constraints are represented using a PrimitiveType "new", "struct" or "class" + /// + public class Constraint : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode WhereKeyword { + get { return GetChildByRole (Roles.WhereKeyword); } + } + + public SimpleType TypeParameter { + get { + return GetChildByRole (Roles.ConstraintTypeParameter); + } + set { + SetChildByRole(Roles.ConstraintTypeParameter, value); + } + } + + public AstNodeCollection BaseTypes { + get { + return GetChildrenByRole(Roles.BaseType); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstraint (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstraint (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstraint (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Constraint o = other as Constraint; + return o != null && this.TypeParameter.DoMatch (o.TypeParameter, match) && this.BaseTypes.DoMatch(o.BaseTypes, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/DelegateDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/DelegateDeclaration.cs new file mode 100644 index 000000000..68489bc7f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/DelegateDeclaration.cs @@ -0,0 +1,92 @@ +// +// DelegateDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// delegate ReturnType Name<TypeParameters>(Parameters) where Constraints; + /// + public class DelegateDeclaration : EntityDeclaration + { + public override NodeType NodeType { + get { return NodeType.TypeDeclaration; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.TypeDefinition; } + } + + public CSharpTokenNode DelegateToken { + get { return GetChildByRole(Roles.DelegateKeyword); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole (Roles.Constraint); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDelegateDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDelegateDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDelegateDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DelegateDeclaration o = other as DelegateDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) && this.Parameters.DoMatch(o.Parameters, match) + && this.Constraints.DoMatch(o.Constraints, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs new file mode 100644 index 000000000..9404d417f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs @@ -0,0 +1,91 @@ +// +// ExternAliasDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// extern alias ; + /// + public class ExternAliasDeclaration : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode ExternToken { + get { return GetChildByRole (Roles.ExternKeyword); } + } + + public CSharpTokenNode AliasToken { + get { return GetChildByRole (Roles.AliasKeyword); } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitExternAliasDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitExternAliasDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitExternAliasDeclaration (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as ExternAliasDeclaration; + return o != null && MatchString (this.Name, o.Name); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs new file mode 100644 index 000000000..dbcf0192d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs @@ -0,0 +1,158 @@ +// +// NamespaceDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// namespace Name { Members } + /// + public class NamespaceDeclaration : AstNode + { + public static readonly Role MemberRole = SyntaxTree.MemberRole; + public static readonly Role NamespaceNameRole = new Role("NamespaceName", AstType.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode NamespaceToken { + get { return GetChildByRole(Roles.NamespaceKeyword); } + } + + public AstType NamespaceName { + get { return GetChildByRole(NamespaceNameRole) ?? AstType.Null; } + set { SetChildByRole(NamespaceNameRole, value); } + } + + public string Name { + get { + return UsingDeclaration.ConstructNamespace(NamespaceName); + } + set { + var arr = value.Split('.'); + NamespaceName = ConstructType(arr, arr.Length - 1); + } + } + + static AstType ConstructType(string[] arr, int i) + { + if (i < 0 || i >= arr.Length) + throw new ArgumentOutOfRangeException("i"); + if (i == 0) + return new SimpleType(arr[i]); + return new MemberType(ConstructType(arr, i - 1), arr[i]); + } + + /// + /// Gets the full namespace name (including any parent namespaces) + /// + public string FullName { + get { + NamespaceDeclaration parentNamespace = Parent as NamespaceDeclaration; + if (parentNamespace != null) + return BuildQualifiedName(parentNamespace.FullName, Name); + return Name; + } + } + + public IEnumerable Identifiers { + get { + var result = new Stack(); + AstType type = NamespaceName; + while (type is MemberType) { + var mt = (MemberType)type; + result.Push(mt.MemberName); + type = mt.Target; + } + if (type is SimpleType) + result.Push(((SimpleType)type).Identifier); + return result; + } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole(Roles.LBrace); } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole(MemberRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole(Roles.RBrace); } + } + + public NamespaceDeclaration() + { + } + + public NamespaceDeclaration(string name) + { + this.Name = name; + } + + public static string BuildQualifiedName(string name1, string name2) + { + if (string.IsNullOrEmpty(name1)) + return name2; + if (string.IsNullOrEmpty(name2)) + return name1; + return name1 + "." + name2; + } + + public void AddMember(AstNode child) + { + AddChild(child, MemberRole); + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNamespaceDeclaration(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNamespaceDeclaration(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamespaceDeclaration(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NamespaceDeclaration o = other as NamespaceDeclaration; + return o != null && MatchString(this.Name, o.Name) && this.Members.DoMatch(o.Members, match); + } + } +} ; diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/NewLineNode.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/NewLineNode.cs new file mode 100644 index 000000000..12b004420 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/NewLineNode.cs @@ -0,0 +1,91 @@ +using System; +namespace ICSharpCode.NRefactory.CSharp +{ + + /// + /// A New line node represents a line break in the text. + /// + public sealed class NewLineNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + const uint newLineMask = 0xfu << AstNodeFlagsUsedBits; + static readonly UnicodeNewline[] newLineTypes = { + UnicodeNewline.Unknown, + UnicodeNewline.LF, + UnicodeNewline.CRLF, + UnicodeNewline.CR, + UnicodeNewline.NEL, + UnicodeNewline.VT, + UnicodeNewline.FF, + UnicodeNewline.LS, + UnicodeNewline.PS + }; + + public UnicodeNewline NewLineType { + get { + return newLineTypes[(flags & newLineMask) >> AstNodeFlagsUsedBits]; + } + set { + ThrowIfFrozen(); + int pos = Array.IndexOf(newLineTypes, value); + if (pos < 0) + pos = 0; + flags &= ~newLineMask; // clear old newline type + flags |= (uint)pos << AstNodeFlagsUsedBits; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (startLocation.Line + 1, 1); + } + } + + public NewLineNode() : this (TextLocation.Empty) + { + } + + public NewLineNode(TextLocation startLocation) + { + this.startLocation = startLocation; + } + + public sealed override string ToString(CSharpFormattingOptions formattingOptions) + { + return NewLine.GetString (NewLineType); + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNewLine (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNewLine (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNewLine (this, data); + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + return other is NewLineNode; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/PreProcessorDirective.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/PreProcessorDirective.cs new file mode 100644 index 000000000..631f35e98 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/PreProcessorDirective.cs @@ -0,0 +1,205 @@ +// +// PreProcessorDirective.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum PreProcessorDirectiveType : byte + { + Invalid = 0, + Region = 1, + Endregion = 2, + + If = 3, + Endif = 4, + Elif = 5, + Else = 6, + + Define = 7, + Undef = 8, + Error = 9, + Warning = 10, + Pragma = 11, + Line = 12 + } + + public class LinePreprocessorDirective : PreProcessorDirective + { + public int LineNumber { + get; + set; + } + + public string FileName { + get; + set; + } + + public LinePreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base (PreProcessorDirectiveType.Line, startLocation, endLocation) + { + } + + public LinePreprocessorDirective(string argument = null) : base (PreProcessorDirectiveType.Line, argument) + { + } + } + + public class PragmaWarningPreprocessorDirective : PreProcessorDirective + { + public static readonly Role WarningRole = new Role ("Warning"); + + public static readonly TokenRole PragmaKeywordRole = new TokenRole ("#pragma"); + public static readonly TokenRole WarningKeywordRole = new TokenRole ("warning"); + public static readonly TokenRole DisableKeywordRole = new TokenRole ("disable"); + public static readonly TokenRole RestoreKeywordRole = new TokenRole ("restore"); + + public bool Disable { + get { + return !DisableToken.IsNull; + } + } + + public CSharpTokenNode PragmaToken { + get { return GetChildByRole (PragmaKeywordRole); } + } + + public CSharpTokenNode WarningToken { + get { return GetChildByRole (WarningKeywordRole); } + } + + public CSharpTokenNode DisableToken { + get { return GetChildByRole (DisableKeywordRole); } + } + + public CSharpTokenNode RestoreToken { + get { return GetChildByRole (RestoreKeywordRole); } + } + + public AstNodeCollection Warnings { + get { return GetChildrenByRole(WarningRole); } + } + + public override TextLocation EndLocation { + get { + var child = LastChild; + if (child == null) + return base.EndLocation; + return child.EndLocation; + } + } + + public PragmaWarningPreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base (PreProcessorDirectiveType.Pragma, startLocation, endLocation) + { + } + + public PragmaWarningPreprocessorDirective(string argument = null) : base (PreProcessorDirectiveType.Pragma, argument) + { + } + + public bool IsDefined(int pragmaWarning) + { + return Warnings.Select(w => (int)w.Value).Any(n => n == pragmaWarning); + } + } + + public class PreProcessorDirective : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public PreProcessorDirectiveType Type { + get; + set; + } + + public string Argument { + get; + set; + } + + /// + /// For an '#if' directive, specifies whether the condition evaluated to true. + /// + public bool Take { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public PreProcessorDirective(PreProcessorDirectiveType type, TextLocation startLocation, TextLocation endLocation) + { + this.Type = type; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public PreProcessorDirective(PreProcessorDirectiveType type, string argument = null) + { + this.Type = type; + this.Argument = argument; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPreProcessorDirective (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPreProcessorDirective (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPreProcessorDirective (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PreProcessorDirective o = other as PreProcessorDirective; + return o != null && Type == o.Type && MatchString(Argument, o.Argument); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TextNode.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TextNode.cs new file mode 100644 index 000000000..4c7f9b942 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TextNode.cs @@ -0,0 +1,94 @@ +// +// TextNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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; +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A text node contains text without syntactic or semantic information. + /// (non parseable part of a text) + /// + public class TextNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public string Text { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public TextNode(string text) : this (text, TextLocation.Empty, TextLocation.Empty) + { + } + + public TextNode(string text, TextLocation startLocation, TextLocation endLocation) + { + this.Text = text; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitText (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitText (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitText (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as TextNode; + return o != null && o.Text == Text; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TypeDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TypeDeclaration.cs new file mode 100644 index 000000000..f2dedfa16 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TypeDeclaration.cs @@ -0,0 +1,145 @@ +// +// TypeDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum ClassType + { + Class, + Struct, + Interface, + Enum + } + + /// + /// class Name<TypeParameters> : BaseTypes where Constraints; + /// + public class TypeDeclaration : EntityDeclaration + { + public override NodeType NodeType { + get { return NodeType.TypeDeclaration; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.TypeDefinition; } + } + + ClassType classType; + + public CSharpTokenNode TypeKeyword { + get { + switch (classType) { + case ClassType.Class: + return GetChildByRole(Roles.ClassKeyword); + case ClassType.Struct: + return GetChildByRole(Roles.StructKeyword); + case ClassType.Interface: + return GetChildByRole(Roles.InterfaceKeyword); + case ClassType.Enum: + return GetChildByRole(Roles.EnumKeyword); + default: + return CSharpTokenNode.Null; + } + } + } + + public ClassType ClassType { + get { return classType; } + set { + ThrowIfFrozen(); + classType = value; + } + } + + public CSharpTokenNode LChevronToken { + get { return GetChildByRole (Roles.LChevron); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode RChevronToken { + get { return GetChildByRole (Roles.RChevron); } + } + + + + public CSharpTokenNode ColonToken { + get { + return GetChildByRole(Roles.Colon); + } + } + + public AstNodeCollection BaseTypes { + get { return GetChildrenByRole(Roles.BaseType); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole(Roles.Constraint); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole (Roles.TypeMemberRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTypeDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeDeclaration o = other as TypeDeclaration; + return o != null && this.ClassType == o.ClassType && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.BaseTypes.DoMatch(o.BaseTypes, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Members.DoMatch(o.Members, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs new file mode 100644 index 000000000..c992b629a --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs @@ -0,0 +1,113 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [in|out] Name + /// + /// Represents a type parameter. + /// Note: mirroring the C# syntax, constraints are not part of the type parameter declaration, but belong + /// to the parent type or method. + /// + public class TypeParameterDeclaration : AstNode + { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; + public static readonly TokenRole OutVarianceKeywordRole = new TokenRole ("out"); + public static readonly TokenRole InVarianceKeywordRole = new TokenRole ("in"); + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public AstNodeCollection Attributes { + get { return GetChildrenByRole (AttributeRole); } + } + + VarianceModifier variance; + + public VarianceModifier Variance { + get { return variance; } + set { ThrowIfFrozen(); variance = value; } + } + + public CSharpTokenNode VarianceToken { + get { + switch (Variance) { + case VarianceModifier.Covariant: + return GetChildByRole(OutVarianceKeywordRole); + case VarianceModifier.Contravariant: + return GetChildByRole(InVarianceKeywordRole); + default: + return CSharpTokenNode.Null; + } + } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public TypeParameterDeclaration () + { + } + + public TypeParameterDeclaration (string name) + { + Name = name; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeParameterDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeParameterDeclaration (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTypeParameterDeclaration(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeParameterDeclaration o = other as TypeParameterDeclaration; + return o != null && this.Variance == o.Variance && MatchString(this.Name, o.Name) && this.Attributes.DoMatch(o.Attributes, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs new file mode 100644 index 000000000..9924132d3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs @@ -0,0 +1,107 @@ +// +// UsingAliasDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using Alias = Import; + /// + public class UsingAliasDeclaration : AstNode + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role AliasRole = new Role("Alias", Identifier.Null); + public static readonly Role ImportRole = UsingDeclaration.ImportRole; + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public string Alias { + get { + return GetChildByRole (AliasRole).Name; + } + set { + SetChildByRole(AliasRole, Identifier.Create (value)); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public AstType Import { + get { return GetChildByRole (ImportRole); } + set { SetChildByRole (ImportRole, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public UsingAliasDeclaration () + { + } + + public UsingAliasDeclaration (string alias, string nameSpace) + { + AddChild (Identifier.Create (alias), AliasRole); + AddChild (new SimpleType (nameSpace), ImportRole); + } + + public UsingAliasDeclaration (string alias, AstType import) + { + AddChild (Identifier.Create (alias), AliasRole); + AddChild (import, ImportRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingAliasDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingAliasDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingAliasDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingAliasDeclaration o = other as UsingAliasDeclaration; + return o != null && MatchString(this.Alias, o.Alias) && this.Import.DoMatch(o.Import, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/UsingDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/UsingDeclaration.cs new file mode 100644 index 000000000..9e0c35a89 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/UsingDeclaration.cs @@ -0,0 +1,122 @@ +// +// UsingDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq; +using System.Text; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using Import; + /// + public class UsingDeclaration : AstNode + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role ImportRole = new Role("Import", AstType.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public AstType Import { + get { return GetChildByRole (ImportRole); } + set { SetChildByRole (ImportRole, value); } + } + + public string Namespace { + get { return ConstructNamespace (Import); } + } + + internal static string ConstructNamespace (AstType type) + { + var stack = new Stack(); + while (type is MemberType) { + var mt = (MemberType)type; + stack.Push(mt.MemberName); + type = mt.Target; + if (mt.IsDoubleColon) { + stack.Push("::"); + } else { + stack.Push("."); + } + } + if (type is SimpleType) + stack.Push(((SimpleType)type).Identifier); + + var result = new StringBuilder(); + while (stack.Count > 0) + result.Append(stack.Pop()); + return result.ToString(); + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public UsingDeclaration () + { + } + + public UsingDeclaration (string nameSpace) + { + AddChild (AstType.Create (nameSpace), ImportRole); + } + + public UsingDeclaration (AstType import) + { + AddChild (import, ImportRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingDeclaration o = other as UsingDeclaration; + return o != null && this.Import.DoMatch(o.Import, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/WhitespaceNode.cs b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/WhitespaceNode.cs new file mode 100644 index 000000000..a03588aa1 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/GeneralScope/WhitespaceNode.cs @@ -0,0 +1,91 @@ +// +// WhitespaceNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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; +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A Whitespace node contains only whitespaces. + /// + public class WhitespaceNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public string WhiteSpaceText { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (startLocation.Line, startLocation.Column + WhiteSpaceText.Length); + } + } + + public WhitespaceNode(string whiteSpaceText) : this (whiteSpaceText, TextLocation.Empty) + { + } + + public WhitespaceNode(string whiteSpaceText, TextLocation startLocation) + { + this.WhiteSpaceText = whiteSpaceText; + this.startLocation = startLocation; + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitWhitespace (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitWhitespace (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitWhitespace (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as WhitespaceNode; + return o != null && o.WhiteSpaceText == WhiteSpaceText; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/IAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Ast/IAstVisitor.cs new file mode 100644 index 000000000..4aadddbdc --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/IAstVisitor.cs @@ -0,0 +1,418 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// AST visitor. + /// + public interface IAstVisitor + { + void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); + void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); + void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); + void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); + void VisitAsExpression(AsExpression asExpression); + void VisitAssignmentExpression(AssignmentExpression assignmentExpression); + void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); + void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); + void VisitCastExpression(CastExpression castExpression); + void VisitCheckedExpression(CheckedExpression checkedExpression); + void VisitConditionalExpression(ConditionalExpression conditionalExpression); + void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); + void VisitDirectionExpression(DirectionExpression directionExpression); + void VisitIdentifierExpression(IdentifierExpression identifierExpression); + void VisitIndexerExpression(IndexerExpression indexerExpression); + void VisitInvocationExpression(InvocationExpression invocationExpression); + void VisitIsExpression(IsExpression isExpression); + void VisitLambdaExpression(LambdaExpression lambdaExpression); + void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); + void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); + void VisitNamedExpression(NamedExpression namedExpression); + void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); + void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); + void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); + void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); + void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); + void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); + void VisitSizeOfExpression(SizeOfExpression sizeOfExpression); + void VisitStackAllocExpression(StackAllocExpression stackAllocExpression); + void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); + void VisitTypeOfExpression(TypeOfExpression typeOfExpression); + void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); + void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); + void VisitUncheckedExpression(UncheckedExpression uncheckedExpression); + + void VisitQueryExpression(QueryExpression queryExpression); + void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); + void VisitQueryFromClause(QueryFromClause queryFromClause); + void VisitQueryLetClause(QueryLetClause queryLetClause); + void VisitQueryWhereClause(QueryWhereClause queryWhereClause); + void VisitQueryJoinClause(QueryJoinClause queryJoinClause); + void VisitQueryOrderClause(QueryOrderClause queryOrderClause); + void VisitQueryOrdering(QueryOrdering queryOrdering); + void VisitQuerySelectClause(QuerySelectClause querySelectClause); + void VisitQueryGroupClause(QueryGroupClause queryGroupClause); + + void VisitAttribute(Attribute attribute); + void VisitAttributeSection(AttributeSection attributeSection); + void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); + void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); + void VisitTypeDeclaration(TypeDeclaration typeDeclaration); + void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); + void VisitUsingDeclaration(UsingDeclaration usingDeclaration); + void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); + + void VisitBlockStatement(BlockStatement blockStatement); + void VisitBreakStatement(BreakStatement breakStatement); + void VisitCheckedStatement(CheckedStatement checkedStatement); + void VisitContinueStatement(ContinueStatement continueStatement); + void VisitDoWhileStatement(DoWhileStatement doWhileStatement); + void VisitEmptyStatement(EmptyStatement emptyStatement); + void VisitExpressionStatement(ExpressionStatement expressionStatement); + void VisitFixedStatement(FixedStatement fixedStatement); + void VisitForeachStatement(ForeachStatement foreachStatement); + void VisitForStatement(ForStatement forStatement); + void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); + void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); + void VisitGotoStatement(GotoStatement gotoStatement); + void VisitIfElseStatement(IfElseStatement ifElseStatement); + void VisitLabelStatement(LabelStatement labelStatement); + void VisitLockStatement(LockStatement lockStatement); + void VisitReturnStatement(ReturnStatement returnStatement); + void VisitSwitchStatement(SwitchStatement switchStatement); + void VisitSwitchSection(SwitchSection switchSection); + void VisitCaseLabel(CaseLabel caseLabel); + void VisitThrowStatement(ThrowStatement throwStatement); + void VisitTryCatchStatement(TryCatchStatement tryCatchStatement); + void VisitCatchClause(CatchClause catchClause); + void VisitUncheckedStatement(UncheckedStatement uncheckedStatement); + void VisitUnsafeStatement(UnsafeStatement unsafeStatement); + void VisitUsingStatement(UsingStatement usingStatement); + void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + void VisitWhileStatement(WhileStatement whileStatement); + void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); + void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); + + void VisitAccessor(Accessor accessor); + void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); + void VisitConstructorInitializer(ConstructorInitializer constructorInitializer); + void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); + void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); + void VisitEventDeclaration(EventDeclaration eventDeclaration); + void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); + void VisitFieldDeclaration(FieldDeclaration fieldDeclaration); + void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); + void VisitMethodDeclaration(MethodDeclaration methodDeclaration); + void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); + void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); + void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); + void VisitVariableInitializer(VariableInitializer variableInitializer); + void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); + void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); + + void VisitSyntaxTree(SyntaxTree syntaxTree); + void VisitSimpleType(SimpleType simpleType); + void VisitMemberType(MemberType memberType); + void VisitComposedType(ComposedType composedType); + void VisitArraySpecifier(ArraySpecifier arraySpecifier); + void VisitPrimitiveType(PrimitiveType primitiveType); + + void VisitComment(Comment comment); + void VisitNewLine(NewLineNode newLineNode); + void VisitWhitespace(WhitespaceNode whitespaceNode); + void VisitText(TextNode textNode); + void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); + void VisitDocumentationReference(DocumentationReference documentationReference); + + void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); + void VisitConstraint(Constraint constraint); + void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); + void VisitIdentifier(Identifier identifier); + + void VisitNullNode(AstNode nullNode); + void VisitErrorNode(AstNode errorNode); + void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); + } + + /// + /// AST visitor. + /// + public interface IAstVisitor + { + S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); + S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); + S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); + S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); + S VisitAsExpression(AsExpression asExpression); + S VisitAssignmentExpression(AssignmentExpression assignmentExpression); + S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); + S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); + S VisitCastExpression(CastExpression castExpression); + S VisitCheckedExpression(CheckedExpression checkedExpression); + S VisitConditionalExpression(ConditionalExpression conditionalExpression); + S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); + S VisitDirectionExpression(DirectionExpression directionExpression); + S VisitIdentifierExpression(IdentifierExpression identifierExpression); + S VisitIndexerExpression(IndexerExpression indexerExpression); + S VisitInvocationExpression(InvocationExpression invocationExpression); + S VisitIsExpression(IsExpression isExpression); + S VisitLambdaExpression(LambdaExpression lambdaExpression); + S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); + S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); + S VisitNamedExpression(NamedExpression namedExpression); + S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); + S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); + S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); + S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); + S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); + S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); + S VisitSizeOfExpression(SizeOfExpression sizeOfExpression); + S VisitStackAllocExpression(StackAllocExpression stackAllocExpression); + S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); + S VisitTypeOfExpression(TypeOfExpression typeOfExpression); + S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); + S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); + S VisitUncheckedExpression(UncheckedExpression uncheckedExpression); + + S VisitQueryExpression(QueryExpression queryExpression); + S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); + S VisitQueryFromClause(QueryFromClause queryFromClause); + S VisitQueryLetClause(QueryLetClause queryLetClause); + S VisitQueryWhereClause(QueryWhereClause queryWhereClause); + S VisitQueryJoinClause(QueryJoinClause queryJoinClause); + S VisitQueryOrderClause(QueryOrderClause queryOrderClause); + S VisitQueryOrdering(QueryOrdering queryOrdering); + S VisitQuerySelectClause(QuerySelectClause querySelectClause); + S VisitQueryGroupClause(QueryGroupClause queryGroupClause); + + S VisitAttribute(Attribute attribute); + S VisitAttributeSection(AttributeSection attributeSection); + S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); + S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); + S VisitTypeDeclaration(TypeDeclaration typeDeclaration); + S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); + S VisitUsingDeclaration(UsingDeclaration usingDeclaration); + S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); + + S VisitBlockStatement(BlockStatement blockStatement); + S VisitBreakStatement(BreakStatement breakStatement); + S VisitCheckedStatement(CheckedStatement checkedStatement); + S VisitContinueStatement(ContinueStatement continueStatement); + S VisitDoWhileStatement(DoWhileStatement doWhileStatement); + S VisitEmptyStatement(EmptyStatement emptyStatement); + S VisitExpressionStatement(ExpressionStatement expressionStatement); + S VisitFixedStatement(FixedStatement fixedStatement); + S VisitForeachStatement(ForeachStatement foreachStatement); + S VisitForStatement(ForStatement forStatement); + S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); + S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); + S VisitGotoStatement(GotoStatement gotoStatement); + S VisitIfElseStatement(IfElseStatement ifElseStatement); + S VisitLabelStatement(LabelStatement labelStatement); + S VisitLockStatement(LockStatement lockStatement); + S VisitReturnStatement(ReturnStatement returnStatement); + S VisitSwitchStatement(SwitchStatement switchStatement); + S VisitSwitchSection(SwitchSection switchSection); + S VisitCaseLabel(CaseLabel caseLabel); + S VisitThrowStatement(ThrowStatement throwStatement); + S VisitTryCatchStatement(TryCatchStatement tryCatchStatement); + S VisitCatchClause(CatchClause catchClause); + S VisitUncheckedStatement(UncheckedStatement uncheckedStatement); + S VisitUnsafeStatement(UnsafeStatement unsafeStatement); + S VisitUsingStatement(UsingStatement usingStatement); + S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + S VisitWhileStatement(WhileStatement whileStatement); + S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); + S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); + + S VisitAccessor(Accessor accessor); + S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); + S VisitConstructorInitializer(ConstructorInitializer constructorInitializer); + S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); + S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); + S VisitEventDeclaration(EventDeclaration eventDeclaration); + S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); + S VisitFieldDeclaration(FieldDeclaration fieldDeclaration); + S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); + S VisitMethodDeclaration(MethodDeclaration methodDeclaration); + S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); + S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); + S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); + S VisitVariableInitializer(VariableInitializer variableInitializer); + S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); + S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); + + S VisitSyntaxTree(SyntaxTree syntaxTree); + S VisitSimpleType(SimpleType simpleType); + S VisitMemberType(MemberType memberType); + S VisitComposedType(ComposedType composedType); + S VisitArraySpecifier(ArraySpecifier arraySpecifier); + S VisitPrimitiveType(PrimitiveType primitiveType); + + S VisitComment(Comment comment); + S VisitWhitespace(WhitespaceNode whitespaceNode); + S VisitText(TextNode textNode); + S VisitNewLine(NewLineNode newLineNode); + S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); + S VisitDocumentationReference(DocumentationReference documentationReference); + + S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); + S VisitConstraint(Constraint constraint); + S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); + S VisitIdentifier(Identifier identifier); + + S VisitNullNode(AstNode nullNode); + S VisitErrorNode(AstNode errorNode); + S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); + } + + /// + /// AST visitor. + /// + public interface IAstVisitor + { + S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, T data); + S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, T data); + S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, T data); + S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, T data); + S VisitAsExpression(AsExpression asExpression, T data); + S VisitAssignmentExpression(AssignmentExpression assignmentExpression, T data); + S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, T data); + S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, T data); + S VisitCastExpression(CastExpression castExpression, T data); + S VisitCheckedExpression(CheckedExpression checkedExpression, T data); + S VisitConditionalExpression(ConditionalExpression conditionalExpression, T data); + S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, T data); + S VisitDirectionExpression(DirectionExpression directionExpression, T data); + S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data); + S VisitIndexerExpression(IndexerExpression indexerExpression, T data); + S VisitInvocationExpression(InvocationExpression invocationExpression, T data); + S VisitIsExpression(IsExpression isExpression, T data); + S VisitLambdaExpression(LambdaExpression lambdaExpression, T data); + S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, T data); + S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, T data); + S VisitNamedExpression(NamedExpression namedExpression, T data); + S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, T data); + S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, T data); + S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data); + S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, T data); + S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, T data); + S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data); + S VisitSizeOfExpression(SizeOfExpression sizeOfExpression, T data); + S VisitStackAllocExpression(StackAllocExpression stackAllocExpression, T data); + S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data); + S VisitTypeOfExpression(TypeOfExpression typeOfExpression, T data); + S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data); + S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, T data); + S VisitUncheckedExpression(UncheckedExpression uncheckedExpression, T data); + + S VisitQueryExpression(QueryExpression queryExpression, T data); + S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data); + S VisitQueryFromClause(QueryFromClause queryFromClause, T data); + S VisitQueryLetClause(QueryLetClause queryLetClause, T data); + S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data); + S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data); + S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data); + S VisitQueryOrdering(QueryOrdering queryOrdering, T data); + S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data); + S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data); + + S VisitAttribute(Attribute attribute, T data); + S VisitAttributeSection(AttributeSection attributeSection, T data); + S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, T data); + S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, T data); + S VisitTypeDeclaration(TypeDeclaration typeDeclaration, T data); + S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration, T data); + S VisitUsingDeclaration(UsingDeclaration usingDeclaration, T data); + S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data); + + S VisitBlockStatement(BlockStatement blockStatement, T data); + S VisitBreakStatement(BreakStatement breakStatement, T data); + S VisitCheckedStatement(CheckedStatement checkedStatement, T data); + S VisitContinueStatement(ContinueStatement continueStatement, T data); + S VisitDoWhileStatement(DoWhileStatement doWhileStatement, T data); + S VisitEmptyStatement(EmptyStatement emptyStatement, T data); + S VisitExpressionStatement(ExpressionStatement expressionStatement, T data); + S VisitFixedStatement(FixedStatement fixedStatement, T data); + S VisitForeachStatement(ForeachStatement foreachStatement, T data); + S VisitForStatement(ForStatement forStatement, T data); + S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, T data); + S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, T data); + S VisitGotoStatement(GotoStatement gotoStatement, T data); + S VisitIfElseStatement(IfElseStatement ifElseStatement, T data); + S VisitLabelStatement(LabelStatement labelStatement, T data); + S VisitLockStatement(LockStatement lockStatement, T data); + S VisitReturnStatement(ReturnStatement returnStatement, T data); + S VisitSwitchStatement(SwitchStatement switchStatement, T data); + S VisitSwitchSection(SwitchSection switchSection, T data); + S VisitCaseLabel(CaseLabel caseLabel, T data); + S VisitThrowStatement(ThrowStatement throwStatement, T data); + S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data); + S VisitCatchClause(CatchClause catchClause, T data); + S VisitUncheckedStatement(UncheckedStatement uncheckedStatement, T data); + S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data); + S VisitUsingStatement(UsingStatement usingStatement, T data); + S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data); + S VisitWhileStatement(WhileStatement whileStatement, T data); + S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data); + S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data); + + S VisitAccessor(Accessor accessor, T data); + S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, T data); + S VisitConstructorInitializer(ConstructorInitializer constructorInitializer, T data); + S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, T data); + S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, T data); + S VisitEventDeclaration(EventDeclaration eventDeclaration, T data); + S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration, T data); + S VisitFieldDeclaration(FieldDeclaration fieldDeclaration, T data); + S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, T data); + S VisitMethodDeclaration(MethodDeclaration methodDeclaration, T data); + S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, T data); + S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, T data); + S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, T data); + S VisitVariableInitializer(VariableInitializer variableInitializer, T data); + S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration, T data); + S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer, T data); + + S VisitSyntaxTree(SyntaxTree syntaxTree, T data); + S VisitSimpleType(SimpleType simpleType, T data); + S VisitMemberType(MemberType memberType, T data); + S VisitComposedType(ComposedType composedType, T data); + S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data); + S VisitPrimitiveType(PrimitiveType primitiveType, T data); + + S VisitComment(Comment comment, T data); + S VisitNewLine(NewLineNode newLineNode, T data); + S VisitWhitespace(WhitespaceNode whitespaceNode, T data); + S VisitText(TextNode textNode, T data); + S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective, T data); + S VisitDocumentationReference(DocumentationReference documentationReference, T data); + + S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, T data); + S VisitConstraint(Constraint constraint, T data); + S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data); + S VisitIdentifier(Identifier identifier, T data); + + S VisitNullNode(AstNode nullNode, T data); + S VisitErrorNode(AstNode errorNode, T data); + S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data); + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Identifier.cs b/ICSharpCode.Decompiler/CSharp/Ast/Identifier.cs new file mode 100644 index 000000000..cf403afbd --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Identifier.cs @@ -0,0 +1,173 @@ +// +// Identifier.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class Identifier : AstNode + { + public new static readonly Identifier Null = new NullIdentifier (); + sealed class NullIdentifier : Identifier + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Token; + } + } + + string name; + public string Name { + get { return this.name; } + set { + if (value == null) + throw new ArgumentNullException("value"); + ThrowIfFrozen(); + this.name = value; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.startLocation = value; + } + + const uint verbatimBit = 1u << AstNodeFlagsUsedBits; + + public bool IsVerbatim { + get { + return (flags & verbatimBit) != 0; + } + set { + ThrowIfFrozen(); + if (value) + flags |= verbatimBit; + else + flags &= ~verbatimBit; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + (IsVerbatim ? 1 : 0)); + } + } + + Identifier () + { + this.name = string.Empty; + } + + protected Identifier (string name, TextLocation location) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + this.startLocation = location; + } + + public static Identifier Create (string name) + { + return Create (name, TextLocation.Empty); + } + + public static Identifier Create (string name, TextLocation location) + { + if (string.IsNullOrEmpty(name)) + return Identifier.Null; + if (name[0] == '@') + return new Identifier (name.Substring (1), new TextLocation (location.Line, location.Column + 1)) { IsVerbatim = true }; + else + return new Identifier (name, location); + } + + public static Identifier Create (string name, TextLocation location, bool isVerbatim) + { + if (string.IsNullOrEmpty (name)) + return Identifier.Null; + + if (isVerbatim) + return new Identifier (name, location) { IsVerbatim = true }; + return new Identifier (name, location); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIdentifier (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIdentifier (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIdentifier (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Identifier o = other as Identifier; + return o != null && !o.IsNull && MatchString(this.Name, o.Name); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/Ast/IdentifierExpressionBackreference.cs b/ICSharpCode.Decompiler/CSharp/Ast/IdentifierExpressionBackreference.cs new file mode 100644 index 000000000..7bfacd990 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/IdentifierExpressionBackreference.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2010-2013 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.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition. + /// + public class IdentifierExpressionBackreference : Pattern + { + readonly string referencedGroupName; + + public string ReferencedGroupName { + get { return referencedGroupName; } + } + + public IdentifierExpressionBackreference(string referencedGroupName) + { + if (referencedGroupName == null) + throw new ArgumentNullException("referencedGroupName"); + this.referencedGroupName = referencedGroupName; + } + + public override bool DoMatch(INode other, Match match) + { + CSharp.IdentifierExpression ident = other as CSharp.IdentifierExpression; + if (ident == null || ident.TypeArguments.Any()) + return false; + CSharp.AstNode referenced = (CSharp.AstNode)match.Get(referencedGroupName).Last(); + if (referenced == null) + return false; + return ident.Identifier == referenced.GetChildByRole(CSharp.Roles.Identifier).Name; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/MemberType.cs b/ICSharpCode.Decompiler/CSharp/Ast/MemberType.cs new file mode 100644 index 000000000..2045d00ed --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/MemberType.cs @@ -0,0 +1,150 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class MemberType : AstType + { + public static readonly Role TargetRole = new Role("Target", AstType.Null); + + bool isDoubleColon; + + public bool IsDoubleColon { + get { return isDoubleColon; } + set { + ThrowIfFrozen(); + isDoubleColon = value; + } + } + + public AstType Target { + get { return GetChildByRole(TargetRole); } + set { SetChildByRole(TargetRole, value); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public MemberType () + { + } + + public MemberType (AstType target, string memberName) + { + this.Target = target; + this.MemberName = memberName; + } + + public MemberType (AstType target, string memberName, IEnumerable typeArguments) + { + this.Target = target; + this.MemberName = memberName; + foreach (var arg in typeArguments) { + AddChild (arg, Roles.TypeArgument); + } + } + + public MemberType (AstType target, string memberName, params AstType[] typeArguments) : this (target, memberName, (IEnumerable)typeArguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMemberType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMemberType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMemberType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MemberType o = other as MemberType; + return o != null && this.IsDoubleColon == o.IsDoubleColon + && MatchString(this.MemberName, o.MemberName) && this.Target.DoMatch(o.Target, match) + && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + + TypeOrNamespaceReference t; + if (this.IsDoubleColon) { + SimpleType st = this.Target as SimpleType; + if (st != null) { + t = interningProvider.Intern(new AliasNamespaceReference(interningProvider.Intern(st.Identifier))); + } else { + t = null; + } + } else { + t = this.Target.ToTypeReference(lookupMode, interningProvider) as TypeOrNamespaceReference; + } + if (t == null) + return SpecialType.UnknownType; + var typeArguments = new List(); + foreach (var ta in this.TypeArguments) { + typeArguments.Add(ta.ToTypeReference(lookupMode, interningProvider)); + } + string memberName = interningProvider.Intern(this.MemberName); + return interningProvider.Intern(new MemberTypeOrNamespaceReference(t, memberName, interningProvider.InternList(typeArguments), lookupMode)); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Modifiers.cs b/ICSharpCode.Decompiler/CSharp/Ast/Modifiers.cs new file mode 100644 index 000000000..eb320495c --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Modifiers.cs @@ -0,0 +1,65 @@ +// +// Modifiers.cs +// +// Author: +// Mike Krüger +// +// Copyright (C) 2008 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Flags] + public enum Modifiers + { + None = 0, + + Private = 0x0001, + Internal = 0x0002, + Protected = 0x0004, + Public = 0x0008, + + Abstract = 0x0010, + Virtual = 0x0020, + Sealed = 0x0040, + Static = 0x0080, + Override = 0x0100, + Readonly = 0x0200, + Const = 0x0400, + New = 0x0800, + Partial = 0x1000, + + Extern = 0x2000, + Volatile = 0x4000, + Unsafe = 0x8000, + Async = 0x10000, + + VisibilityMask = Private | Internal | Protected | Public, + + /// + /// Special value used to match any modifiers during pattern matching. + /// + Any = unchecked((int)0x80000000) + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/NodeType.cs b/ICSharpCode.Decompiler/CSharp/Ast/NodeType.cs new file mode 100644 index 000000000..cf96a60c8 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/NodeType.cs @@ -0,0 +1,56 @@ +// +// NodeType.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum NodeType + { + Unknown, + /// + /// AstType + /// + TypeReference, + /// + /// Type or delegate declaration + /// + TypeDeclaration, + Member, + Statement, + Expression, + Token, + QueryClause, + /// + /// Comment or whitespace or pre-processor directive + /// + Whitespace, + /// + /// Placeholder for a pattern + /// + Pattern + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/ObservableAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Ast/ObservableAstVisitor.cs new file mode 100644 index 000000000..898d51864 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/ObservableAstVisitor.cs @@ -0,0 +1,857 @@ +// +// ObservableAstVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ObservableAstVisitor : IAstVisitor + { + void Visit(Action enter, Action leave, T node) where T : AstNode + { + if (enter != null) + enter(node); + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces children. + next = child.NextSibling; + child.AcceptVisitor (this); + } + if (leave != null) + leave(node); + } + + void IAstVisitor.VisitNullNode(AstNode nullNode) + { + } + + void IAstVisitor.VisitErrorNode(AstNode nullNode) + { + } + + public event Action EnterSyntaxTree, LeaveSyntaxTree; + + void IAstVisitor.VisitSyntaxTree(SyntaxTree unit) + { + Visit(EnterSyntaxTree, LeaveSyntaxTree, unit); + } + + public event Action EnterComment, LeaveComment; + + void IAstVisitor.VisitComment(Comment comment) + { + Visit(EnterComment, LeaveComment, comment); + } + + public event Action EnterNewLine, LeaveNewLine; + + void IAstVisitor.VisitNewLine(NewLineNode newLineNode) + { + Visit(EnterNewLine, LeaveNewLine, newLineNode); + } + + public event Action EnterWhitespace, LeaveWhitespace; + + void IAstVisitor.VisitWhitespace(WhitespaceNode whitespace) + { + Visit(EnterWhitespace, LeaveWhitespace, whitespace); + } + + public event Action EnterText, LeaveText; + + void IAstVisitor.VisitText(TextNode textNode) + { + Visit(EnterText, LeaveText, textNode); + } + + public event Action EnterPreProcessorDirective, LeavePreProcessorDirective; + void IAstVisitor.VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + Visit(EnterPreProcessorDirective, LeavePreProcessorDirective, preProcessorDirective); + } + + public event Action EnterDocumentationReference, LeaveDocumentationReference; + + void IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) + { + Visit(EnterDocumentationReference, LeaveDocumentationReference, documentationReference); + } + + public event Action EnterIdentifier, LeaveIdentifier; + + void IAstVisitor.VisitIdentifier(Identifier identifier) + { + Visit(EnterIdentifier, LeaveIdentifier, identifier); + } + + public event Action EnterCSharpTokenNode, LeaveCSharpTokenNode; + + void IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode token) + { + Visit(EnterCSharpTokenNode, LeaveCSharpTokenNode, token); + } + + public event Action EnterPrimitiveType, LeavePrimitiveType; + + void IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType) + { + Visit(EnterPrimitiveType, LeavePrimitiveType, primitiveType); + } + + public event Action EnterComposedType, LeaveComposedType; + + void IAstVisitor.VisitComposedType(ComposedType composedType) + { + Visit(EnterComposedType, LeaveComposedType, composedType); + } + + public event Action EnterSimpleType, LeaveSimpleType; + + void IAstVisitor.VisitSimpleType(SimpleType simpleType) + { + Visit(EnterSimpleType, LeaveSimpleType, simpleType); + } + + public event Action EnterMemberType, LeaveMemberType; + + void IAstVisitor.VisitMemberType(MemberType memberType) + { + Visit(EnterMemberType, LeaveMemberType, memberType); + } + + public event Action EnterAttribute, LeaveAttribute; + + void IAstVisitor.VisitAttribute(Attribute attribute) + { + Visit(EnterAttribute, LeaveAttribute, attribute); + } + + public event Action EnterAttributeSection, LeaveAttributeSection; + + void IAstVisitor.VisitAttributeSection(AttributeSection attributeSection) + { + Visit(EnterAttributeSection, LeaveAttributeSection, attributeSection); + } + + public event Action EnterDelegateDeclaration, LeaveDelegateDeclaration; + + void IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + Visit(EnterDelegateDeclaration, LeaveDelegateDeclaration, delegateDeclaration); + } + + public event Action EnterNamespaceDeclaration, LeaveNamespaceDeclaration; + + void IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + Visit(EnterNamespaceDeclaration, LeaveNamespaceDeclaration, namespaceDeclaration); + } + + public event Action EnterTypeDeclaration, LeaveTypeDeclaration; + + void IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + Visit(EnterTypeDeclaration, LeaveTypeDeclaration, typeDeclaration); + } + + public event Action EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration; + + void IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + Visit(EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration, typeParameterDeclaration); + } + + public event Action EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration; + + void IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + Visit(EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration, enumMemberDeclaration); + } + + public event Action EnterUsingDeclaration, LeaveUsingDeclaration; + + void IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + Visit(EnterUsingDeclaration, LeaveUsingDeclaration, usingDeclaration); + } + + public event Action EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration; + + void IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) + { + Visit(EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration, usingDeclaration); + } + + public event Action EnterExternAliasDeclaration, LeaveExternAliasDeclaration; + + void IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + Visit(EnterExternAliasDeclaration, LeaveExternAliasDeclaration, externAliasDeclaration); + } + + public event Action EnterConstructorDeclaration, LeaveConstructorDeclaration; + + void IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + Visit(EnterConstructorDeclaration, LeaveConstructorDeclaration, constructorDeclaration); + } + + public event Action EnterConstructorInitializer, LeaveConstructorInitializer; + + void IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + Visit(EnterConstructorInitializer, LeaveConstructorInitializer, constructorInitializer); + } + + public event Action EnterDestructorDeclaration, LeaveDestructorDeclaration; + + void IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + Visit(EnterDestructorDeclaration, LeaveDestructorDeclaration, destructorDeclaration); + } + + public event Action EnterEventDeclaration, LeaveEventDeclaration; + + void IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) + { + Visit(EnterEventDeclaration, LeaveEventDeclaration, eventDeclaration); + } + + public event Action EnterCustomEventDeclaration, LeaveCustomEventDeclaration; + + void IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + Visit(EnterCustomEventDeclaration, LeaveCustomEventDeclaration, eventDeclaration); + } + + public event Action EnterFieldDeclaration, LeaveFieldDeclaration; + + void IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + Visit(EnterFieldDeclaration, LeaveFieldDeclaration, fieldDeclaration); + } + + public event Action EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration; + + void IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + Visit(EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration, fixedFieldDeclaration); + } + + public event Action EnterFixedVariableInitializer, LeaveFixedVariableInitializer; + + void IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + Visit(EnterFixedVariableInitializer, LeaveFixedVariableInitializer, fixedVariableInitializer); + } + + public event Action EnterIndexerDeclaration, LeaveIndexerDeclaration; + + void IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + Visit(EnterIndexerDeclaration, LeaveIndexerDeclaration, indexerDeclaration); + } + + public event Action EnterMethodDeclaration, LeaveMethodDeclaration; + + void IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + Visit(EnterMethodDeclaration, LeaveMethodDeclaration, methodDeclaration); + } + + public event Action EnterOperatorDeclaration, LeaveOperatorDeclaration; + + void IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + Visit(EnterOperatorDeclaration, LeaveOperatorDeclaration, operatorDeclaration); + } + + public event Action EnterPropertyDeclaration, LeavePropertyDeclaration; + + void IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + Visit(EnterPropertyDeclaration, LeavePropertyDeclaration, propertyDeclaration); + } + + public event Action EnterAccessor, LeaveAccessor; + + void IAstVisitor.VisitAccessor(Accessor accessor) + { + Visit(EnterAccessor, LeaveAccessor, accessor); + } + + public event Action EnterVariableInitializer, LeaveVariableInitializer; + + void IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer) + { + Visit(EnterVariableInitializer, LeaveVariableInitializer, variableInitializer); + } + + public event Action EnterParameterDeclaration, LeaveParameterDeclaration; + + void IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + Visit(EnterParameterDeclaration, LeaveParameterDeclaration, parameterDeclaration); + } + + public event Action EnterConstraint, LeaveConstraint; + + void IAstVisitor.VisitConstraint(Constraint constraint) + { + Visit(EnterConstraint, LeaveConstraint, constraint); + } + + public event Action EnterBlockStatement, LeaveBlockStatement; + + void IAstVisitor.VisitBlockStatement(BlockStatement blockStatement) + { + Visit(EnterBlockStatement, LeaveBlockStatement, blockStatement); + } + + public event Action EnterExpressionStatement, LeaveExpressionStatement; + + void IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) + { + Visit(EnterExpressionStatement, LeaveExpressionStatement, expressionStatement); + } + + public event Action EnterBreakStatement, LeaveBreakStatement; + + void IAstVisitor.VisitBreakStatement(BreakStatement breakStatement) + { + Visit(EnterBreakStatement, LeaveBreakStatement, breakStatement); + } + + public event Action EnterCheckedStatement, LeaveCheckedStatement; + + void IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement) + { + Visit(EnterCheckedStatement, LeaveCheckedStatement, checkedStatement); + } + + public event Action EnterContinueStatement, LeaveContinueStatement; + + void IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement) + { + Visit(EnterContinueStatement, LeaveContinueStatement, continueStatement); + } + + public event Action EnterDoWhileStatement, LeaveDoWhileStatement; + + void IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + Visit(EnterDoWhileStatement, LeaveDoWhileStatement, doWhileStatement); + } + + public event Action EnterEmptyStatement, LeaveEmptyStatement; + + void IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) + { + Visit(EnterEmptyStatement, LeaveEmptyStatement, emptyStatement); + } + + public event Action EnterFixedStatement, LeaveFixedStatement; + + void IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) + { + Visit(EnterFixedStatement, LeaveFixedStatement, fixedStatement); + } + + public event Action EnterForeachStatement, LeaveForeachStatement; + + void IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement) + { + Visit(EnterForeachStatement, LeaveForeachStatement, foreachStatement); + } + + public event Action EnterForStatement, LeaveForStatement; + + void IAstVisitor.VisitForStatement(ForStatement forStatement) + { + Visit(EnterForStatement, LeaveForStatement, forStatement); + } + + public event Action EnterGotoCaseStatement, LeaveGotoCaseStatement; + + void IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + Visit(EnterGotoCaseStatement, LeaveGotoCaseStatement, gotoCaseStatement); + } + + public event Action EnterGotoDefaultStatement, LeaveGotoDefaultStatement; + + void IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + Visit(EnterGotoDefaultStatement, LeaveGotoDefaultStatement, gotoDefaultStatement); + } + + public event Action EnterGotoStatement, LeaveGotoStatement; + + void IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement) + { + Visit(EnterGotoStatement, LeaveGotoStatement, gotoStatement); + } + + public event Action EnterIfElseStatement, LeaveIfElseStatement; + + void IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement) + { + Visit(EnterIfElseStatement, LeaveIfElseStatement, ifElseStatement); + } + + public event Action EnterLabelStatement, LeaveLabelStatement; + + void IAstVisitor.VisitLabelStatement(LabelStatement labelStatement) + { + Visit(EnterLabelStatement, LeaveLabelStatement, labelStatement); + } + + public event Action EnterLockStatement, LeaveLockStatement; + + void IAstVisitor.VisitLockStatement(LockStatement lockStatement) + { + Visit(EnterLockStatement, LeaveLockStatement, lockStatement); + } + + public event Action EnterReturnStatement, LeaveReturnStatement; + + void IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement) + { + Visit(EnterReturnStatement, LeaveReturnStatement, returnStatement); + } + + public event Action EnterSwitchStatement, LeaveSwitchStatement; + + void IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement) + { + Visit(EnterSwitchStatement, LeaveSwitchStatement, switchStatement); + } + + public event Action EnterSwitchSection, LeaveSwitchSection; + + void IAstVisitor.VisitSwitchSection(SwitchSection switchSection) + { + Visit(EnterSwitchSection, LeaveSwitchSection, switchSection); + } + + public event Action EnterCaseLabel, LeaveCaseLabel; + + void IAstVisitor.VisitCaseLabel(CaseLabel caseLabel) + { + Visit(EnterCaseLabel, LeaveCaseLabel, caseLabel); + } + + public event Action EnterThrowStatement, LeaveThrowStatement; + + void IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement) + { + Visit(EnterThrowStatement, LeaveThrowStatement, throwStatement); + } + + public event Action EnterTryCatchStatement, LeaveTryCatchStatement; + + void IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + Visit(EnterTryCatchStatement, LeaveTryCatchStatement, tryCatchStatement); + } + + public event Action EnterCatchClause, LeaveCatchClause; + + void IAstVisitor.VisitCatchClause(CatchClause catchClause) + { + Visit(EnterCatchClause, LeaveCatchClause, catchClause); + } + + public event Action EnterUncheckedStatement, LeaveUncheckedStatement; + + void IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + Visit(EnterUncheckedStatement, LeaveUncheckedStatement, uncheckedStatement); + } + + public event Action EnterUnsafeStatement, LeaveUnsafeStatement; + + void IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + Visit(EnterUnsafeStatement, LeaveUnsafeStatement, unsafeStatement); + } + + public event Action EnterUsingStatement, LeaveUsingStatement; + + void IAstVisitor.VisitUsingStatement(UsingStatement usingStatement) + { + Visit(EnterUsingStatement, LeaveUsingStatement, usingStatement); + } + + public event Action EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement; + + void IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + Visit(EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement, variableDeclarationStatement); + } + + public event Action EnterWhileStatement, LeaveWhileStatement; + + void IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) + { + Visit(EnterWhileStatement, LeaveWhileStatement, whileStatement); + } + + public event Action EnterYieldBreakStatement, LeaveYieldBreakStatement; + + void IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + Visit(EnterYieldBreakStatement, LeaveYieldBreakStatement, yieldBreakStatement); + } + + public event Action EnterYieldReturnStatement, LeaveYieldReturnStatement; + + void IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + Visit(EnterYieldReturnStatement, LeaveYieldReturnStatement, yieldStatement); + } + + public event Action EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression; + + void IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + Visit(EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression, anonymousMethodExpression); + } + + public event Action EnterLambdaExpression, LeaveLambdaExpression; + + void IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression) + { + Visit(EnterLambdaExpression, LeaveLambdaExpression, lambdaExpression); + } + + public event Action EnterAssignmentExpression, LeaveAssignmentExpression; + + void IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + Visit(EnterAssignmentExpression, LeaveAssignmentExpression, assignmentExpression); + } + + public event Action EnterBaseReferenceExpression, LeaveBaseReferenceExpression; + + void IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + Visit(EnterBaseReferenceExpression, LeaveBaseReferenceExpression, baseReferenceExpression); + } + + public event Action EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression; + + void IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + Visit(EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression, binaryOperatorExpression); + } + + public event Action EnterCastExpression, LeaveCastExpression; + + void IAstVisitor.VisitCastExpression(CastExpression castExpression) + { + Visit(EnterCastExpression, LeaveCastExpression, castExpression); + } + + public event Action EnterCheckedExpression, LeaveCheckedExpression; + + void IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression) + { + Visit(EnterCheckedExpression, LeaveCheckedExpression, checkedExpression); + } + + public event Action EnterConditionalExpression, LeaveConditionalExpression; + + void IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + Visit(EnterConditionalExpression, LeaveConditionalExpression, conditionalExpression); + } + + public event Action EnterIdentifierExpression, LeaveIdentifierExpression; + + void IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + Visit(EnterIdentifierExpression, LeaveIdentifierExpression, identifierExpression); + } + + public event Action EnterIndexerExpression, LeaveIndexerExpression; + + void IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression) + { + Visit(EnterIndexerExpression, LeaveIndexerExpression, indexerExpression); + } + + public event Action EnterInvocationExpression, LeaveInvocationExpression; + + void IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression) + { + Visit(EnterInvocationExpression, LeaveInvocationExpression, invocationExpression); + } + + public event Action EnterDirectionExpression, LeaveDirectionExpression; + + void IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression) + { + Visit(EnterDirectionExpression, LeaveDirectionExpression, directionExpression); + } + + public event Action EnterMemberReferenceExpression, LeaveMemberReferenceExpression; + + void IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + Visit(EnterMemberReferenceExpression, LeaveMemberReferenceExpression, memberReferenceExpression); + } + + public event Action EnterNullReferenceExpression, LeaveNullReferenceExpression; + + void IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + Visit(EnterNullReferenceExpression, LeaveNullReferenceExpression, nullReferenceExpression); + } + + public event Action EnterObjectCreateExpression, LeaveObjectCreateExpression; + + void IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + Visit(EnterObjectCreateExpression, LeaveObjectCreateExpression, objectCreateExpression); + } + + public event Action EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression; + + void IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + Visit(EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression, anonymousTypeCreateExpression); + } + + public event Action EnterArrayCreateExpression, LeaveArrayCreateExpression; + + void IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arraySCreateExpression) + { + Visit(EnterArrayCreateExpression, LeaveArrayCreateExpression, arraySCreateExpression); + } + + public event Action EnterParenthesizedExpression, LeaveParenthesizedExpression; + + void IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + Visit(EnterParenthesizedExpression, LeaveParenthesizedExpression, parenthesizedExpression); + } + + public event Action EnterPointerReferenceExpression, LeavePointerReferenceExpression; + + void IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + Visit(EnterPointerReferenceExpression, LeavePointerReferenceExpression, pointerReferenceExpression); + } + + public event Action EnterPrimitiveExpression, LeavePrimitiveExpression; + + void IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + Visit(EnterPrimitiveExpression, LeavePrimitiveExpression, primitiveExpression); + } + + public event Action EnterSizeOfExpression, LeaveSizeOfExpression; + + void IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + Visit(EnterSizeOfExpression, LeaveSizeOfExpression, sizeOfExpression); + } + + public event Action EnterStackAllocExpression, LeaveStackAllocExpression; + + void IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + Visit(EnterStackAllocExpression, LeaveStackAllocExpression, stackAllocExpression); + } + + public event Action EnterThisReferenceExpression, LeaveThisReferenceExpression; + + void IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + Visit(EnterThisReferenceExpression, LeaveThisReferenceExpression, thisReferenceExpression); + } + + public event Action EnterTypeOfExpression, LeaveTypeOfExpression; + + void IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + Visit(EnterTypeOfExpression, LeaveTypeOfExpression, typeOfExpression); + } + + public event Action EnterTypeReferenceExpression, LeaveTypeReferenceExpression; + + void IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + Visit(EnterTypeReferenceExpression, LeaveTypeReferenceExpression, typeReferenceExpression); + } + + public event Action EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression; + + void IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + Visit(EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression, unaryOperatorExpression); + } + + public event Action EnterUncheckedExpression, LeaveUncheckedExpression; + + void IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + Visit(EnterUncheckedExpression, LeaveUncheckedExpression, uncheckedExpression); + } + + public event Action EnterQueryExpression, LeaveQueryExpression; + + void IAstVisitor.VisitQueryExpression(QueryExpression queryExpression) + { + Visit(EnterQueryExpression, LeaveQueryExpression, queryExpression); + } + + public event Action EnterQueryContinuationClause, LeaveQueryContinuationClause; + + void IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + Visit(EnterQueryContinuationClause, LeaveQueryContinuationClause, queryContinuationClause); + } + + public event Action EnterQueryFromClause, LeaveQueryFromClause; + + void IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause) + { + Visit(EnterQueryFromClause, LeaveQueryFromClause, queryFromClause); + } + + public event Action EnterQueryLetClause, LeaveQueryLetClause; + + void IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause) + { + Visit(EnterQueryLetClause, LeaveQueryLetClause, queryLetClause); + } + + public event Action EnterQueryWhereClause, LeaveQueryWhereClause; + + void IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + Visit(EnterQueryWhereClause, LeaveQueryWhereClause, queryWhereClause); + } + + public event Action EnterQueryJoinClause, LeaveQueryJoinClause; + + void IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + Visit(EnterQueryJoinClause, LeaveQueryJoinClause, queryJoinClause); + } + + public event Action EnterQueryOrderClause, LeaveQueryOrderClause; + + void IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + Visit(EnterQueryOrderClause, LeaveQueryOrderClause, queryOrderClause); + } + + public event Action EnterQueryOrdering, LeaveQueryOrdering; + + void IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering) + { + Visit(EnterQueryOrdering, LeaveQueryOrdering, queryOrdering); + } + + public event Action EnterQuerySelectClause, LeaveQuerySelectClause; + + void IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + Visit(EnterQuerySelectClause, LeaveQuerySelectClause, querySelectClause); + } + + public event Action EnterQueryGroupClause, LeaveQueryGroupClause; + + void IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + Visit(EnterQueryGroupClause, LeaveQueryGroupClause, queryGroupClause); + } + + public event Action EnterAsExpression, LeaveAsExpression; + + void IAstVisitor.VisitAsExpression(AsExpression asExpression) + { + Visit(EnterAsExpression, LeaveAsExpression, asExpression); + } + + public event Action EnterIsExpression, LeaveIsExpression; + + void IAstVisitor.VisitIsExpression(IsExpression isExpression) + { + Visit(EnterIsExpression, LeaveIsExpression, isExpression); + } + + public event Action EnterDefaultValueExpression, LeaveDefaultValueExpression; + + void IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + Visit(EnterDefaultValueExpression, LeaveDefaultValueExpression, defaultValueExpression); + } + + public event Action EnterUndocumentedExpression, LeaveUndocumentedExpression; + + void IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + Visit(EnterUndocumentedExpression, LeaveUndocumentedExpression, undocumentedExpression); + } + + public event Action EnterArrayInitializerExpression, LeaveArrayInitializerExpression; + + void IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + Visit(EnterArrayInitializerExpression, LeaveArrayInitializerExpression, arrayInitializerExpression); + } + + public event Action EnterArraySpecifier, LeaveArraySpecifier; + + void IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + Visit(EnterArraySpecifier, LeaveArraySpecifier, arraySpecifier); + } + + public event Action EnterNamedArgumentExpression, LeaveNamedArgumentExpression; + + void IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + Visit(EnterNamedArgumentExpression, LeaveNamedArgumentExpression, namedArgumentExpression); + } + + public event Action EnterNamedExpression, LeaveNamedExpression; + + void IAstVisitor.VisitNamedExpression(NamedExpression namedExpression) + { + Visit(EnterNamedExpression, LeaveNamedExpression, namedExpression); + } + + void IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/PrimitiveType.cs b/ICSharpCode.Decompiler/CSharp/Ast/PrimitiveType.cs new file mode 100644 index 000000000..e3761bc82 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/PrimitiveType.cs @@ -0,0 +1,166 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class PrimitiveType : AstType + { + TextLocation location; + string keyword = string.Empty; + + public string Keyword { + get { return keyword; } + set { + if (value == null) + throw new ArgumentNullException(); + ThrowIfFrozen(); + keyword = value; + } + } + + public KnownTypeCode KnownTypeCode { + get { return GetTypeCodeForPrimitiveType(this.Keyword); } + } + + public PrimitiveType() + { + } + + public PrimitiveType(string keyword) + { + this.Keyword = keyword; + } + + public PrimitiveType(string keyword, TextLocation location) + { + this.Keyword = keyword; + this.location = location; + } + + public override TextLocation StartLocation { + get { + return location; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.location = value; + } + + public override TextLocation EndLocation { + get { + return new TextLocation (location.Line, location.Column + keyword.Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPrimitiveType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPrimitiveType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPrimitiveType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PrimitiveType o = other as PrimitiveType; + return o != null && MatchString(this.Keyword, o.Keyword); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return Keyword; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + KnownTypeCode typeCode = GetTypeCodeForPrimitiveType(this.Keyword); + if (typeCode == KnownTypeCode.None) { + if (this.Keyword == "__arglist") + return SpecialType.ArgList; + return new UnknownType(null, this.Keyword); + } else { + return KnownTypeReference.Get(typeCode); + } + } + + public static KnownTypeCode GetTypeCodeForPrimitiveType(string keyword) + { + switch (keyword) { + case "string": + return KnownTypeCode.String; + case "int": + return KnownTypeCode.Int32; + case "uint": + return KnownTypeCode.UInt32; + case "object": + return KnownTypeCode.Object; + case "bool": + return KnownTypeCode.Boolean; + case "sbyte": + return KnownTypeCode.SByte; + case "byte": + return KnownTypeCode.Byte; + case "short": + return KnownTypeCode.Int16; + case "ushort": + return KnownTypeCode.UInt16; + case "long": + return KnownTypeCode.Int64; + case "ulong": + return KnownTypeCode.UInt64; + case "float": + return KnownTypeCode.Single; + case "double": + return KnownTypeCode.Double; + case "decimal": + return KnownTypeCode.Decimal; + case "char": + return KnownTypeCode.Char; + case "void": + return KnownTypeCode.Void; + default: + return KnownTypeCode.None; + } + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Roles.cs b/ICSharpCode.Decompiler/CSharp/Ast/Roles.cs new file mode 100644 index 000000000..a7408c91d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Roles.cs @@ -0,0 +1,96 @@ +// +// Roles.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class Roles + { + public static readonly Role Root = AstNode.RootRole; + + // some pre defined constants for common roles + public static readonly Role Identifier = new Role ("Identifier", CSharp.Identifier.Null); + public static readonly Role Body = new Role ("Body", CSharp.BlockStatement.Null); + public static readonly Role Parameter = new Role ("Parameter"); + public static readonly Role Argument = new Role ("Argument", CSharp.Expression.Null); + public static readonly Role Type = new Role ("Type", CSharp.AstType.Null); + public static readonly Role Expression = new Role ("Expression", CSharp.Expression.Null); + public static readonly Role TargetExpression = new Role ("Target", CSharp.Expression.Null); + public readonly static Role Condition = new Role ("Condition", CSharp.Expression.Null); + public static readonly Role TypeParameter = new Role ("TypeParameter"); + public static readonly Role TypeArgument = new Role ("TypeArgument", CSharp.AstType.Null); + public readonly static Role Constraint = new Role ("Constraint"); + public static readonly Role Variable = new Role ("Variable", VariableInitializer.Null); + public static readonly Role EmbeddedStatement = new Role ("EmbeddedStatement", CSharp.Statement.Null); + public readonly static Role TypeMemberRole = new Role ("TypeMember"); + + + // public static readonly TokenRole Keyword = new TokenRole ("Keyword", CSharpTokenNode.Null); +// public static readonly TokenRole InKeyword = new TokenRole ("InKeyword", CSharpTokenNode.Null); + + // some pre defined constants for most used punctuation + public static readonly TokenRole LPar = new TokenRole ("("); + public static readonly TokenRole RPar = new TokenRole (")"); + public static readonly TokenRole LBracket = new TokenRole ("["); + public static readonly TokenRole RBracket = new TokenRole ("]"); + public static readonly TokenRole LBrace = new TokenRole ("{"); + public static readonly TokenRole RBrace = new TokenRole ("}"); + public static readonly TokenRole LChevron = new TokenRole ("<"); + public static readonly TokenRole RChevron = new TokenRole (">"); + public static readonly TokenRole Comma = new TokenRole (","); + public static readonly TokenRole Dot = new TokenRole ("."); + public static readonly TokenRole Semicolon = new TokenRole (";"); + public static readonly TokenRole Assign = new TokenRole ("="); + public static readonly TokenRole Colon = new TokenRole (":"); + public static readonly TokenRole DoubleColon = new TokenRole ("::"); + public static readonly Role Comment = new Role ("Comment"); + public static readonly Role NewLine = new Role ("NewLine"); + public static readonly Role Whitespace = new Role ("Whitespace"); + public static readonly Role Text = new Role ("Text"); + public static readonly Role PreProcessorDirective = new Role ("PreProcessorDirective"); + public static readonly Role Error = new Role ("Error"); + + public readonly static Role BaseType = new Role ("BaseType", AstType.Null); + + public static readonly Role Attribute = new Role ("Attribute"); + public static readonly Role AttributeTargetRole = new Role ("AttributeTarget", CSharpTokenNode.Null); + + public readonly static TokenRole WhereKeyword = new TokenRole ("where"); + public readonly static Role ConstraintTypeParameter = new Role ("TypeParameter", SimpleType.Null); + public readonly static TokenRole DelegateKeyword = new TokenRole ("delegate"); + public static readonly TokenRole ExternKeyword = new TokenRole ("extern"); + public static readonly TokenRole AliasKeyword = new TokenRole ("alias"); + public static readonly TokenRole NamespaceKeyword = new TokenRole ("namespace"); + + public static readonly TokenRole EnumKeyword = new TokenRole ("enum"); + public static readonly TokenRole InterfaceKeyword = new TokenRole ("interface"); + public static readonly TokenRole StructKeyword = new TokenRole ("struct"); + public static readonly TokenRole ClassKeyword = new TokenRole ("class"); + + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/SimpleType.cs b/ICSharpCode.Decompiler/CSharp/Ast/SimpleType.cs new file mode 100644 index 000000000..529a62fbe --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/SimpleType.cs @@ -0,0 +1,169 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class SimpleType : AstType + { + #region Null + public new static readonly SimpleType Null = new NullSimpleType (); + + sealed class NullSimpleType : SimpleType + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + return SpecialType.UnknownType; + } + } + #endregion + + public SimpleType() + { + } + + public SimpleType(string identifier) + { + this.Identifier = identifier; + } + + public SimpleType (Identifier identifier) + { + this.IdentifierToken = identifier; + } + + public SimpleType(string identifier, TextLocation location) + { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (identifier, location)); + } + + public SimpleType (string identifier, IEnumerable typeArguments) + { + this.Identifier = identifier; + foreach (var arg in typeArguments) { + AddChild (arg, Roles.TypeArgument); + } + } + + public SimpleType (string identifier, params AstType[] typeArguments) : this (identifier, (IEnumerable)typeArguments) + { + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSimpleType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSimpleType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSimpleType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SimpleType o = other as SimpleType; + return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + var typeArguments = new List(); + foreach (var ta in this.TypeArguments) { + typeArguments.Add(ta.ToTypeReference(lookupMode, interningProvider)); + } + string identifier = interningProvider.Intern(this.Identifier); + if (typeArguments.Count == 0 && string.IsNullOrEmpty(identifier)) { + // empty SimpleType is used for typeof(List<>). + return SpecialType.UnboundTypeArgument; + } + var t = new SimpleTypeOrNamespaceReference(identifier, interningProvider.InternList(typeArguments), lookupMode); + return interningProvider.Intern(t); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/BlockStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/BlockStatement.cs new file mode 100644 index 000000000..24b9cd106 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/BlockStatement.cs @@ -0,0 +1,164 @@ +// +// BlockStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// { Statements } + /// + public class BlockStatement : Statement, IEnumerable + { + public static readonly Role StatementRole = new Role("Statement", Statement.Null); + + #region Null + public static readonly new BlockStatement Null = new NullBlockStatement (); + sealed class NullBlockStatement : BlockStatement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator BlockStatement(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : BlockStatement, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Statements { + get { return GetChildrenByRole (StatementRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBlockStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBlockStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBlockStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BlockStatement o = other as BlockStatement; + return o != null && !o.IsNull && this.Statements.DoMatch(o.Statements, match); + } + + public void Add(Statement statement) + { + AddChild(statement, StatementRole); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.Statements.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.Statements.GetEnumerator(); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/BreakStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/BreakStatement.cs new file mode 100644 index 000000000..4bb4e39ef --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/BreakStatement.cs @@ -0,0 +1,65 @@ +// +// BreakStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// break; + /// + public class BreakStatement : Statement + { + public static readonly TokenRole BreakKeywordRole = new TokenRole ("break"); + + public CSharpTokenNode BreakToken { + get { return GetChildByRole (BreakKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBreakStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBreakStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBreakStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BreakStatement o = other as BreakStatement; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/CheckedStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/CheckedStatement.cs new file mode 100644 index 000000000..803067aff --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/CheckedStatement.cs @@ -0,0 +1,75 @@ +// +// CheckedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// checked BodyBlock + /// + public class CheckedStatement : Statement + { + public static readonly TokenRole CheckedKeywordRole = new TokenRole ("checked"); + + public CSharpTokenNode CheckedToken { + get { return GetChildByRole (CheckedKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public CheckedStatement () + { + } + + public CheckedStatement (BlockStatement body) + { + AddChild (body, Roles.Body); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCheckedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCheckedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCheckedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedStatement o = other as CheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/ContinueStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ContinueStatement.cs new file mode 100644 index 000000000..aac1690b2 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ContinueStatement.cs @@ -0,0 +1,65 @@ +// +// ContinueStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// continue; + /// + public class ContinueStatement : Statement + { + public static readonly TokenRole ContinueKeywordRole = new TokenRole ("continue"); + + public CSharpTokenNode ContinueToken { + get { return GetChildByRole (ContinueKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitContinueStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitContinueStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitContinueStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ContinueStatement o = other as ContinueStatement; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/DoWhileStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/DoWhileStatement.cs new file mode 100644 index 000000000..280ca7cea --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/DoWhileStatement.cs @@ -0,0 +1,99 @@ +// +// DoWhileStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "do EmbeddedStatement while(Condition);" + /// + public class DoWhileStatement : Statement + { + public static readonly TokenRole DoKeywordRole = new TokenRole ("do"); + public static readonly TokenRole WhileKeywordRole = new TokenRole ("while"); + + public CSharpTokenNode DoToken { + get { return GetChildByRole (DoKeywordRole); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public CSharpTokenNode WhileToken { + get { return GetChildByRole (WhileKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDoWhileStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDoWhileStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDoWhileStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DoWhileStatement o = other as DoWhileStatement; + return o != null && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match) && this.Condition.DoMatch(o.Condition, match); + } + + public DoWhileStatement() + { + } + + public DoWhileStatement(Expression condition, Statement embeddedStatement) + { + this.Condition = condition; + this.EmbeddedStatement = embeddedStatement; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/EmptyStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/EmptyStatement.cs new file mode 100644 index 000000000..deaa3a9c4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/EmptyStatement.cs @@ -0,0 +1,72 @@ +// +// EmptyStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// ; + /// + public class EmptyStatement : Statement + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + 1); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEmptyStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEmptyStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEmptyStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EmptyStatement o = other as EmptyStatement; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/ExpressionStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ExpressionStatement.cs new file mode 100644 index 000000000..1fdc4ddc4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ExpressionStatement.cs @@ -0,0 +1,73 @@ +// +// ExpressionStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression; + /// + public class ExpressionStatement : Statement + { + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitExpressionStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitExpressionStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitExpressionStatement (this, data); + } + + public ExpressionStatement() + { + } + + public ExpressionStatement(Expression expression) + { + this.Expression = expression; + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ExpressionStatement o = other as ExpressionStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/FixedStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/FixedStatement.cs new file mode 100644 index 000000000..d44366504 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/FixedStatement.cs @@ -0,0 +1,85 @@ +// +// FixedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// fixed (Type Variables) EmbeddedStatement + /// + public class FixedStatement : Statement + { + public static readonly TokenRole FixedKeywordRole = new TokenRole ("fixed"); + + public CSharpTokenNode FixedToken { + get { return GetChildByRole (FixedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FixedStatement o = other as FixedStatement; + return o != null && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/ForStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ForStatement.cs new file mode 100644 index 000000000..d369536d0 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ForStatement.cs @@ -0,0 +1,97 @@ +// +// ForStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// for (Initializers; Condition; Iterators) EmbeddedStatement + /// + public class ForStatement : Statement + { + public static readonly TokenRole ForKeywordRole = new TokenRole ("for"); + public readonly static Role InitializerRole = new Role("Initializer", Statement.Null); + public readonly static Role IteratorRole = new Role("Iterator", Statement.Null); + + public CSharpTokenNode ForToken { + get { return GetChildByRole (ForKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + /// + /// Gets the list of initializer statements. + /// Note: this contains multiple statements for "for (a = 2, b = 1; a > b; a--)", but contains + /// only a single statement for "for (int a = 2, b = 1; a > b; a--)" (a single VariableDeclarationStatement with two variables) + /// + public AstNodeCollection Initializers { + get { return GetChildrenByRole (InitializerRole); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public AstNodeCollection Iterators { + get { return GetChildrenByRole (IteratorRole); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitForStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitForStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitForStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForStatement o = other as ForStatement; + return o != null && this.Initializers.DoMatch(o.Initializers, match) && this.Condition.DoMatch(o.Condition, match) + && this.Iterators.DoMatch(o.Iterators, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/ForeachStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ForeachStatement.cs new file mode 100644 index 000000000..b3a9c5f78 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ForeachStatement.cs @@ -0,0 +1,108 @@ +// +// ForeachStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// foreach (Type VariableName in InExpression) EmbeddedStatement + /// + public class ForeachStatement : Statement + { + public static readonly TokenRole ForeachKeywordRole = new TokenRole ("foreach"); + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + + public CSharpTokenNode ForeachToken { + get { return GetChildByRole (ForeachKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType VariableType { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string VariableName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier VariableNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode InToken { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression InExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitForeachStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitForeachStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitForeachStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForeachStatement o = other as ForeachStatement; + return o != null && this.VariableType.DoMatch(o.VariableType, match) && MatchString(this.VariableName, o.VariableName) + && this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/GotoStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/GotoStatement.cs new file mode 100644 index 000000000..7aff7a82f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/GotoStatement.cs @@ -0,0 +1,178 @@ +// +// GotoStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "goto Label;" + /// + public class GotoStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + + public GotoStatement () + { + } + + public GotoStatement (string label) + { + this.Label = label; + } + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public string Label { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + if (string.IsNullOrEmpty(value)) + SetChildByRole(Roles.Identifier, null); + else + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoStatement o = other as GotoStatement; + return o != null && MatchString(this.Label, o.Label); + } + } + + /// + /// or "goto case LabelExpression;" + /// + public class GotoCaseStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + public static readonly TokenRole CaseKeywordRole = new TokenRole ("case"); + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public CSharpTokenNode CaseToken { + get { return GetChildByRole (CaseKeywordRole); } + } + + /// + /// Used for "goto case LabelExpression;" + /// + public Expression LabelExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoCaseStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoCaseStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoCaseStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoCaseStatement o = other as GotoCaseStatement; + return o != null && this.LabelExpression.DoMatch(o.LabelExpression, match); + } + } + + /// + /// or "goto default;" + /// + public class GotoDefaultStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + public static readonly TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public CSharpTokenNode DefaultToken { + get { return GetChildByRole (DefaultKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoDefaultStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoDefaultStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoDefaultStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoDefaultStatement o = other as GotoDefaultStatement; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/IfElseStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/IfElseStatement.cs new file mode 100644 index 000000000..70ece3fd5 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/IfElseStatement.cs @@ -0,0 +1,103 @@ +// +// IfElseStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// if (Condition) TrueStatement else FalseStatement + /// + public class IfElseStatement : Statement + { + public readonly static TokenRole IfKeywordRole = new TokenRole ("if"); + public readonly static Role ConditionRole = Roles.Condition; + public readonly static Role TrueRole = new Role("True", Statement.Null); + public readonly static TokenRole ElseKeywordRole = new TokenRole ("else"); + public readonly static Role FalseRole = new Role("False", Statement.Null); + + public CSharpTokenNode IfToken { + get { return GetChildByRole (IfKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (ConditionRole); } + set { SetChildByRole (ConditionRole, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement TrueStatement { + get { return GetChildByRole (TrueRole); } + set { SetChildByRole (TrueRole, value); } + } + + public CSharpTokenNode ElseToken { + get { return GetChildByRole (ElseKeywordRole); } + } + + public Statement FalseStatement { + get { return GetChildByRole (FalseRole); } + set { SetChildByRole (FalseRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIfElseStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIfElseStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIfElseStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IfElseStatement o = other as IfElseStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueStatement.DoMatch(o.TrueStatement, match) && this.FalseStatement.DoMatch(o.FalseStatement, match); + } + + public IfElseStatement() + { + } + + public IfElseStatement(Expression condition, Statement trueStatement, Statement falseStatement = null) + { + this.Condition = condition; + this.TrueStatement = trueStatement; + this.FalseStatement = falseStatement; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/LabelStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/LabelStatement.cs new file mode 100644 index 000000000..43d22cea7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/LabelStatement.cs @@ -0,0 +1,73 @@ +// +// LabelStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Label: + /// + public class LabelStatement : Statement + { + public string Label { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier LabelToken { + get { return GetChildByRole (Roles.Identifier); } + set { SetChildByRole (Roles.Identifier, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLabelStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLabelStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLabelStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LabelStatement o = other as LabelStatement; + return o != null && MatchString(this.Label, o.Label); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/LockStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/LockStatement.cs new file mode 100644 index 000000000..e59f99308 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/LockStatement.cs @@ -0,0 +1,79 @@ +// +// LockStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// lock (Expression) EmbeddedStatement; + /// + public class LockStatement : Statement + { + public static readonly TokenRole LockKeywordRole = new TokenRole ("lock"); + + public CSharpTokenNode LockToken { + get { return GetChildByRole (LockKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLockStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLockStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLockStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LockStatement o = other as LockStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/ReturnStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ReturnStatement.cs new file mode 100644 index 000000000..0970bce43 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ReturnStatement.cs @@ -0,0 +1,79 @@ +// +// ReturnStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// return Expression; + /// + public class ReturnStatement : Statement + { + public static readonly TokenRole ReturnKeywordRole = new TokenRole ("return"); + + public CSharpTokenNode ReturnToken { + get { return GetChildByRole (ReturnKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public ReturnStatement () + { + } + + public ReturnStatement (Expression returnExpression) + { + AddChild (returnExpression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitReturnStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitReturnStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitReturnStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ReturnStatement o = other as ReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/Statement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/Statement.cs new file mode 100644 index 000000000..24d3ede92 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/Statement.cs @@ -0,0 +1,132 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Base class for statements. + /// + /// + /// This class is useful even though it doesn't provide any additional functionality: + /// It can be used to communicate more information in APIs, e.g. "this subnode will always be a statement" + /// + public abstract class Statement : AstNode + { + #region Null + public new static readonly Statement Null = new NullStatement (); + + sealed class NullStatement : Statement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator Statement(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : Statement, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public new Statement Clone() + { + return (Statement)base.Clone(); + } + + public Statement ReplaceWith(Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException("replaceFunction"); + return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); + } + + public override NodeType NodeType { + get { return NodeType.Statement; } + } + + public static implicit operator Statement (Expression type) + { + return new ExpressionStatement(type); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/SwitchStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/SwitchStatement.cs new file mode 100644 index 000000000..fa8e80cd5 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/SwitchStatement.cs @@ -0,0 +1,230 @@ +// +// SwitchStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// switch (Expression) { SwitchSections } + /// + public class SwitchStatement : Statement + { + public static readonly TokenRole SwitchKeywordRole = new TokenRole ("switch"); + public static readonly Role SwitchSectionRole = new Role("SwitchSection"); + + public CSharpTokenNode SwitchToken { + get { return GetChildByRole (SwitchKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection SwitchSections { + get { return GetChildrenByRole (SwitchSectionRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSwitchStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSwitchStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSwitchStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchStatement o = other as SwitchStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match); + } + } + + public class SwitchSection : AstNode + { + #region PatternPlaceholder + public static implicit operator SwitchSection(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : SwitchSection, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public static readonly Role CaseLabelRole = new Role("CaseLabel"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstNodeCollection CaseLabels { + get { return GetChildrenByRole (CaseLabelRole); } + } + + public AstNodeCollection Statements { + get { return GetChildrenByRole (Roles.EmbeddedStatement); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSwitchSection (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSwitchSection (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSwitchSection (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchSection o = other as SwitchSection; + return o != null && this.CaseLabels.DoMatch(o.CaseLabels, match) && this.Statements.DoMatch(o.Statements, match); + } + } + + public class CaseLabel : AstNode + { + public static readonly TokenRole CaseKeywordRole = new TokenRole ("case"); + public static readonly TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + /// + /// Gets or sets the expression. The expression can be null - if the expression is null, it's the default switch section. + /// + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public CaseLabel () + { + } + + public CaseLabel (Expression expression) + { + this.Expression = expression; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCaseLabel (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCaseLabel (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCaseLabel (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CaseLabel o = other as CaseLabel; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/ThrowStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ThrowStatement.cs new file mode 100644 index 000000000..98e27d1e7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/ThrowStatement.cs @@ -0,0 +1,79 @@ +// +// ThrowStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// throw Expression; + /// + public class ThrowStatement : Statement + { + public static readonly TokenRole ThrowKeywordRole = new TokenRole ("throw"); + + public CSharpTokenNode ThrowToken { + get { return GetChildByRole (ThrowKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public ThrowStatement () + { + } + + public ThrowStatement (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitThrowStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitThrowStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitThrowStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThrowStatement o = other as ThrowStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/TryCatchStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/TryCatchStatement.cs new file mode 100644 index 000000000..b93a168a4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/TryCatchStatement.cs @@ -0,0 +1,252 @@ +// +// TryCatchStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// try TryBlock CatchClauses finally FinallyBlock + /// + public class TryCatchStatement : Statement + { + public static readonly TokenRole TryKeywordRole = new TokenRole ("try"); + public static readonly Role TryBlockRole = new Role("TryBlock", BlockStatement.Null); + public static readonly Role CatchClauseRole = new Role("CatchClause", CatchClause.Null); + public static readonly TokenRole FinallyKeywordRole = new TokenRole ("finally"); + public static readonly Role FinallyBlockRole = new Role("FinallyBlock", BlockStatement.Null); + + public CSharpTokenNode TryToken { + get { return GetChildByRole (TryKeywordRole); } + } + + public BlockStatement TryBlock { + get { return GetChildByRole (TryBlockRole); } + set { SetChildByRole (TryBlockRole, value); } + } + + public AstNodeCollection CatchClauses { + get { return GetChildrenByRole (CatchClauseRole); } + } + + public CSharpTokenNode FinallyToken { + get { return GetChildByRole (FinallyKeywordRole); } + } + + public BlockStatement FinallyBlock { + get { return GetChildByRole (FinallyBlockRole); } + set { SetChildByRole (FinallyBlockRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTryCatchStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTryCatchStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTryCatchStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TryCatchStatement o = other as TryCatchStatement; + return o != null && this.TryBlock.DoMatch(o.TryBlock, match) && this.CatchClauses.DoMatch(o.CatchClauses, match) && this.FinallyBlock.DoMatch(o.FinallyBlock, match); + } + } + + /// + /// catch (Type VariableName) { Body } + /// + public class CatchClause : AstNode + { + public static readonly TokenRole CatchKeywordRole = new TokenRole ("catch"); + public static readonly TokenRole WhenKeywordRole = new TokenRole ("when"); + public static readonly Role ConditionRole = Roles.Condition; + + #region Null + public new static readonly CatchClause Null = new NullCatchClause (); + + sealed class NullCatchClause : CatchClause + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator CatchClause(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : CatchClause, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode CatchToken { + get { return GetChildByRole (CatchKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string VariableName { + get { return GetChildByRole (Roles.Identifier).Name; } + set { + if (string.IsNullOrEmpty(value)) + SetChildByRole (Roles.Identifier, null); + else + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier VariableNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode WhenToken { + get { return GetChildByRole (WhenKeywordRole); } + } + + public Expression Condition { + get { return GetChildByRole(ConditionRole); } + set { SetChildByRole(ConditionRole, value); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCatchClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCatchClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCatchClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CatchClause o = other as CatchClause; + return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.VariableName, o.VariableName) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/UncheckedStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/UncheckedStatement.cs new file mode 100644 index 000000000..765cd9ab3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/UncheckedStatement.cs @@ -0,0 +1,75 @@ +// +// UncheckedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unchecked BodyBlock + /// + public class UncheckedStatement : Statement + { + public static readonly TokenRole UncheckedKeywordRole = new TokenRole ("unchecked"); + + public CSharpTokenNode UncheckedToken { + get { return GetChildByRole (UncheckedKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public UncheckedStatement () + { + } + + public UncheckedStatement (BlockStatement body) + { + AddChild (body, Roles.Body); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUncheckedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUncheckedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUncheckedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedStatement o = other as UncheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/UnsafeStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/UnsafeStatement.cs new file mode 100644 index 000000000..fa6421ae6 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/UnsafeStatement.cs @@ -0,0 +1,66 @@ +// +// UnsafeStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unsafe { Body } + /// + public class UnsafeStatement : Statement + { + public static readonly TokenRole UnsafeKeywordRole = new TokenRole ("unsafe"); + + public CSharpTokenNode UnsafeToken { + get { return GetChildByRole (UnsafeKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUnsafeStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUnsafeStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUnsafeStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnsafeStatement o = other as UnsafeStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/UsingStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/UsingStatement.cs new file mode 100644 index 000000000..c87304675 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/UsingStatement.cs @@ -0,0 +1,83 @@ +// +// UsingStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using (ResourceAcquisition) EmbeddedStatement + /// + public class UsingStatement : Statement + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", AstNode.Null); + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + /// + /// Either a VariableDeclarationStatement, or an Expression. + /// + public AstNode ResourceAcquisition { + get { return GetChildByRole (ResourceAcquisitionRole); } + set { SetChildByRole (ResourceAcquisitionRole, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingStatement o = other as UsingStatement; + return o != null && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/VariableDeclarationStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/VariableDeclarationStatement.cs new file mode 100644 index 000000000..32c141d96 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/VariableDeclarationStatement.cs @@ -0,0 +1,90 @@ +// +// VariableDeclarationStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class VariableDeclarationStatement : Statement + { + public static readonly Role ModifierRole = EntityDeclaration.ModifierRole; + + public VariableDeclarationStatement() + { + } + + public VariableDeclarationStatement(AstType type, string name, Expression initializer = null) + { + this.Type = type; + this.Variables.Add(new VariableInitializer(name, initializer)); + } + + public Modifiers Modifiers { + get { return EntityDeclaration.GetModifiers(this); } + set { EntityDeclaration.SetModifiers(this, value); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public VariableInitializer GetVariable (string name) + { + return Variables.FirstOrNullObject (vi => vi.Name == name); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitVariableDeclarationStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitVariableDeclarationStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitVariableDeclarationStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + VariableDeclarationStatement o = other as VariableDeclarationStatement; + return o != null && this.Modifiers == o.Modifiers && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/WhileStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/WhileStatement.cs new file mode 100644 index 000000000..e38daa144 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/WhileStatement.cs @@ -0,0 +1,89 @@ +// +// WhileStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "while (Condition) EmbeddedStatement" + /// + public class WhileStatement : Statement + { + public static readonly TokenRole WhileKeywordRole = new TokenRole ("while"); + + public CSharpTokenNode WhileToken { + get { return GetChildByRole (WhileKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitWhileStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitWhileStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitWhileStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + WhileStatement o = other as WhileStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + + public WhileStatement() + { + } + + public WhileStatement(Expression condition, Statement embeddedStatement) + { + this.Condition = condition; + this.EmbeddedStatement = embeddedStatement; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/YieldBreakStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/YieldBreakStatement.cs new file mode 100644 index 000000000..ea5cac4a6 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/YieldBreakStatement.cs @@ -0,0 +1,70 @@ +// +// YieldBreakStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// yield break; + /// + public class YieldBreakStatement : Statement + { + public static readonly TokenRole YieldKeywordRole = new TokenRole ("yield"); + public static readonly TokenRole BreakKeywordRole = new TokenRole ("break"); + + public CSharpTokenNode YieldToken { + get { return GetChildByRole (YieldKeywordRole); } + } + + public CSharpTokenNode BreakToken { + get { return GetChildByRole (BreakKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitYieldBreakStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitYieldBreakStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitYieldBreakStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldBreakStatement o = other as YieldBreakStatement; + return o != null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/Statements/YieldReturnStatement.cs b/ICSharpCode.Decompiler/CSharp/Ast/Statements/YieldReturnStatement.cs new file mode 100644 index 000000000..6539bf0c0 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/Statements/YieldReturnStatement.cs @@ -0,0 +1,75 @@ +// +// YieldStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// yield return Expression; + /// + public class YieldReturnStatement : Statement + { + public static readonly TokenRole YieldKeywordRole = new TokenRole ("yield"); + public static readonly TokenRole ReturnKeywordRole = new TokenRole ("return"); + + public CSharpTokenNode YieldToken { + get { return GetChildByRole (YieldKeywordRole); } + } + + public CSharpTokenNode ReturnToken { + get { return GetChildByRole (ReturnKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitYieldReturnStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitYieldReturnStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitYieldReturnStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldReturnStatement o = other as YieldReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/SyntaxExtensions.cs b/ICSharpCode.Decompiler/CSharp/Ast/SyntaxExtensions.cs new file mode 100644 index 000000000..bac28bb76 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/SyntaxExtensions.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2013 Daniel Grunwald +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Extension methods for the syntax tree. + /// + public static class SyntaxExtensions + { + public static bool IsComparisonOperator(this OperatorType operatorType) + { + switch (operatorType) { + case OperatorType.Equality: + case OperatorType.Inequality: + case OperatorType.GreaterThan: + case OperatorType.LessThan: + case OperatorType.GreaterThanOrEqual: + case OperatorType.LessThanOrEqual: + return true; + default: + return false; + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/SyntaxTree.cs b/ICSharpCode.Decompiler/CSharp/Ast/SyntaxTree.cs new file mode 100644 index 000000000..fccf906fc --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/SyntaxTree.cs @@ -0,0 +1,149 @@ +// +// SyntaxTree.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using System.IO; +using ICSharpCode.NRefactory.Editor; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class SyntaxTree : AstNode + { + public static readonly Role MemberRole = new Role("Member", AstNode.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + string fileName; + + /// + /// Gets/Sets the file name of this syntax tree. + /// + public string FileName { + get { return fileName; } + set { + ThrowIfFrozen(); + fileName = value; + } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole(MemberRole); } + } + + IList conditionalSymbols = null; + + List errors = new List (); + + public List Errors { + get { return errors; } + } + + + /// + /// Gets the conditional symbols used to parse the source file. Note that this list contains + /// the conditional symbols at the start of the first token in the file - including the ones defined + /// in the source file. + /// + public IList ConditionalSymbols { + get { + return conditionalSymbols ?? EmptyList.Instance; + } + internal set { + conditionalSymbols = value; + } + } + + /// + /// Gets the expression that was on top of the parse stack. + /// This is the only way to get an expression that isn't part of a statment. + /// (eg. when an error follows an expression). + /// + /// This is used for code completion to 'get the expression before a token - like ., <, ('. + /// + public AstNode TopExpression { + get; + internal set; + } + + public SyntaxTree () + { + } + + /// + /// Gets all defined types in this syntax tree. + /// + /// + /// A list containing or nodes. + /// + public IEnumerable GetTypes(bool includeInnerTypes = false) + { + Stack nodeStack = new Stack (); + nodeStack.Push(this); + while (nodeStack.Count > 0) { + var curNode = nodeStack.Pop(); + if (curNode is TypeDeclaration || curNode is DelegateDeclaration) { + yield return (EntityDeclaration)curNode; + } + foreach (var child in curNode.Children) { + if (!(child is Statement || child is Expression) && + (child.Role != Roles.TypeMemberRole || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) + nodeStack.Push (child); + } + } + } + + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SyntaxTree o = other as SyntaxTree; + return o != null && this.Members.DoMatch(o.Members, match); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSyntaxTree (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSyntaxTree (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSyntaxTree (this, data); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TokenRole.cs b/ICSharpCode.Decompiler/CSharp/Ast/TokenRole.cs new file mode 100644 index 000000000..8c9c7392a --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TokenRole.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A specific role only used for C# tokens + /// + public sealed class TokenRole : Role + { + internal readonly static List Tokens = new List (); + internal readonly static List TokenLengths = new List (); + internal readonly uint TokenIndex; + + static TokenRole () + { + // null token + Tokens.Add (""); + TokenLengths.Add (0); + } + + /// + /// Gets the token as string. Note that the token Name and Token value may differ. + /// + public string Token { + get; + private set; + } + + /// + /// Gets the char length of the token. + /// + public int Length { + get; + private set; + } + + + public TokenRole(string token) : base (token, CSharpTokenNode.Null) + { + this.Token = token; + this.Length = token.Length; + + bool found = false; + for (int i = 0; i < Tokens.Count; i++) { + var existingToken = Tokens [i]; + if (existingToken == token) { + TokenIndex = (uint)i; + found = true; + break; + } + } + if (!found) { + TokenIndex = (uint)Tokens.Count; + Tokens.Add (token); + TokenLengths.Add (this.Length); + } + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/Accessor.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/Accessor.cs new file mode 100644 index 000000000..8bd18c477 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/Accessor.cs @@ -0,0 +1,117 @@ +// +// PropertyDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// get/set/add/remove + /// + public class Accessor : EntityDeclaration + { + public static readonly new Accessor Null = new NullAccessor (); + sealed class NullAccessor : Accessor + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Method; } + } + + /// + /// Gets the 'get'/'set'/'add'/'remove' keyword + /// + public CSharpTokenNode Keyword { + get { + for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) { + if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole + || child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole) + { + return (CSharpTokenNode)child; + } + } + return CSharpTokenNode.Null; + } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAccessor (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAccessor (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitAccessor (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Accessor o = other as Accessor; + return o != null && !o.IsNull && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs new file mode 100644 index 000000000..23a973a5c --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs @@ -0,0 +1,190 @@ +// +// ConstructorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ConstructorDeclaration : EntityDeclaration + { + public static readonly Role InitializerRole = new Role("Initializer", ConstructorInitializer.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Constructor; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public ConstructorInitializer Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole( InitializerRole, value); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstructorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstructorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstructorDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConstructorDeclaration o = other as ConstructorDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) && this.Parameters.DoMatch(o.Parameters, match) + && this.Initializer.DoMatch(o.Initializer, match) && this.Body.DoMatch(o.Body, match); + } + } + + public enum ConstructorInitializerType { + Any, + Base, + This + } + + public class ConstructorInitializer : AstNode + { + public static readonly TokenRole BaseKeywordRole = new TokenRole ("base"); + public static readonly TokenRole ThisKeywordRole = new TokenRole ("this"); + + public static readonly new ConstructorInitializer Null = new NullConstructorInitializer (); + class NullConstructorInitializer : ConstructorInitializer + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public ConstructorInitializerType ConstructorInitializerType { + get; + set; + } + + public CSharpTokenNode Keyword { + get { + if (ConstructorInitializerType == ConstructorInitializerType.Base) + return GetChildByRole(BaseKeywordRole); + else + return GetChildByRole(ThisKeywordRole); + } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstructorInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstructorInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstructorInitializer (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConstructorInitializer o = other as ConstructorInitializer; + return o != null && !o.IsNull + && (this.ConstructorInitializerType == ConstructorInitializerType.Any || this.ConstructorInitializerType == o.ConstructorInitializerType) + && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/DestructorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/DestructorDeclaration.cs new file mode 100644 index 000000000..0609e5dc6 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/DestructorDeclaration.cs @@ -0,0 +1,76 @@ +// +// DestructorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class DestructorDeclaration : EntityDeclaration + { + public static readonly TokenRole TildeRole = new TokenRole ("~"); + + public CSharpTokenNode TildeToken { + get { return GetChildByRole (TildeRole); } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Destructor; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDestructorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDestructorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDestructorDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DestructorDeclaration o = other as DestructorDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EntityDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EntityDeclaration.cs new file mode 100644 index 000000000..c02ff21b6 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EntityDeclaration.cs @@ -0,0 +1,117 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class EntityDeclaration : AstNode + { + public static readonly Role AttributeRole = new Role("Attribute"); + public static readonly Role UnattachedAttributeRole = new Role("UnattachedAttribute"); + public static readonly Role ModifierRole = new Role("Modifier"); + public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); + + public override NodeType NodeType { + get { return NodeType.Member; } + } + + public abstract NRefactory.TypeSystem.SymbolKind SymbolKind { get; } + + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole (AttributeRole); } + } + + public Modifiers Modifiers { + get { return GetModifiers(this); } + set { SetModifiers(this, value); } + } + + public bool HasModifier (Modifiers mod) + { + return (Modifiers & mod) == mod; + } + + public IEnumerable ModifierTokens { + get { return GetChildrenByRole (ModifierRole); } + } + + public virtual string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value, TextLocation.Empty)); + } + } + + public virtual Identifier NameToken { + get { return GetChildByRole (Roles.Identifier); } + set { SetChildByRole (Roles.Identifier, value); } + } + + public virtual AstType ReturnType { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + internal static Modifiers GetModifiers(AstNode node) + { + Modifiers m = 0; + foreach (CSharpModifierToken t in node.GetChildrenByRole (ModifierRole)) { + m |= t.Modifier; + } + return m; + } + + internal static void SetModifiers(AstNode node, Modifiers newValue) + { + Modifiers oldValue = GetModifiers(node); + AstNode insertionPos = node.GetChildrenByRole(AttributeRole).LastOrDefault(); + foreach (Modifiers m in CSharpModifierToken.AllModifiers) { + if ((m & newValue) != 0) { + if ((m & oldValue) == 0) { + // Modifier was added + var newToken = new CSharpModifierToken(TextLocation.Empty, m); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; + } else { + // Modifier already exists + insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); + } + } else { + if ((m & oldValue) != 0) { + // Modifier was removed + node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == m).Remove(); + } + } + } + } + + protected bool MatchAttributesAndModifiers (EntityDeclaration o, PatternMatching.Match match) + { + return (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) && this.Attributes.DoMatch (o.Attributes, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs new file mode 100644 index 000000000..b7c924ab9 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs @@ -0,0 +1,72 @@ +// +// EnumMemberDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class EnumMemberDeclaration : EntityDeclaration + { + public static readonly Role InitializerRole = new Role("Initializer", Expression.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEnumMemberDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEnumMemberDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEnumMemberDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EnumMemberDeclaration o = other as EnumMemberDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EventDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EventDeclaration.cs new file mode 100644 index 000000000..d543f9ea7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/EventDeclaration.cs @@ -0,0 +1,152 @@ +// +// EventDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.ComponentModel; + +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class EventDeclaration : EntityDeclaration + { + public static readonly TokenRole EventKeywordRole = new TokenRole ("event"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Event; } + } + + public CSharpTokenNode EventToken { + get { return GetChildByRole (EventKeywordRole); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + // Hide .Name and .NameToken from users; the actual field names + // are stored in the VariableInitializer. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Name { + get { return string.Empty; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEventDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEventDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEventDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EventDeclaration o = other as EventDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); + } + } + + public class CustomEventDeclaration : EntityDeclaration + { + public static readonly TokenRole EventKeywordRole = new TokenRole ("event"); + public static readonly TokenRole AddKeywordRole = new TokenRole ("add"); + public static readonly TokenRole RemoveKeywordRole = new TokenRole ("remove"); + + public static readonly Role AddAccessorRole = new Role("AddAccessor", Accessor.Null); + public static readonly Role RemoveAccessorRole = new Role("RemoveAccessor", Accessor.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Event; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor AddAccessor { + get { return GetChildByRole (AddAccessorRole); } + set { SetChildByRole (AddAccessorRole, value); } + } + + public Accessor RemoveAccessor { + get { return GetChildByRole (RemoveAccessorRole); } + set { SetChildByRole (RemoveAccessorRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCustomEventDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCustomEventDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCustomEventDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CustomEventDeclaration o = other as CustomEventDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.AddAccessor.DoMatch(o.AddAccessor, match) && this.RemoveAccessor.DoMatch(o.RemoveAccessor, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FieldDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FieldDeclaration.cs new file mode 100644 index 000000000..de220ecd7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FieldDeclaration.cs @@ -0,0 +1,79 @@ +// +// FieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class FieldDeclaration : EntityDeclaration + { + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + // Hide .Name and .NameToken from users; the actual field names + // are stored in the VariableInitializer. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Name { + get { return string.Empty; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFieldDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFieldDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFieldDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FieldDeclaration o = other as FieldDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs new file mode 100644 index 000000000..fea2a2af2 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs @@ -0,0 +1,71 @@ +// +// FixedFieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class FixedFieldDeclaration : EntityDeclaration + { + public static readonly TokenRole FixedKeywordRole = new TokenRole ("fixed"); + public static readonly Role VariableRole = new Role ("FixedVariable"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public CSharpTokenNode FixedToken { + get { return GetChildByRole (FixedKeywordRole); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (VariableRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedFieldDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedFieldDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedFieldDeclaration (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as FixedFieldDeclaration; + return o != null && this.MatchAttributesAndModifiers (o, match) + && this.ReturnType.DoMatch (o.ReturnType, match) && this.Variables.DoMatch (o.Variables, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FixedVariableInitializer.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FixedVariableInitializer.cs new file mode 100644 index 000000000..2c320a826 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/FixedVariableInitializer.cs @@ -0,0 +1,105 @@ +// +// FixedFieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Name [ CountExpression ] + /// + public class FixedVariableInitializer : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public FixedVariableInitializer() + { + } + + public FixedVariableInitializer (string name, Expression initializer = null) + { + this.Name = name; + this.CountExpression = initializer; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public Expression CountExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedVariableInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedVariableInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedVariableInitializer (this, data); + } + + protected internal override bool DoMatch (AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + var o = other as FixedVariableInitializer; + return o != null && MatchString (this.Name, o.Name) && this.CountExpression.DoMatch (o.CountExpression, match); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/IndexerDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/IndexerDeclaration.cs new file mode 100644 index 000000000..56156dd19 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/IndexerDeclaration.cs @@ -0,0 +1,122 @@ +// +// IndexerDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class IndexerDeclaration : EntityDeclaration + { + public static readonly TokenRole ThisKeywordRole = new TokenRole ("this"); + public static readonly Role GetterRole = PropertyDeclaration.GetterRole; + public static readonly Role SetterRole = PropertyDeclaration.SetterRole; + + public override SymbolKind SymbolKind { + get { return SymbolKind.Indexer; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public override string Name { + get { return "Item"; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public CSharpTokenNode ThisToken { + get { return GetChildByRole (ThisKeywordRole); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor Getter { + get { return GetChildByRole(GetterRole); } + set { SetChildByRole(GetterRole, value); } + } + + public Accessor Setter { + get { return GetChildByRole(SetterRole); } + set { SetChildByRole(SetterRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIndexerDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIndexerDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIndexerDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IndexerDeclaration o = other as IndexerDeclaration; + return o != null + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.Parameters.DoMatch(o.Parameters, match) + && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/MethodDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/MethodDeclaration.cs new file mode 100644 index 000000000..90aaa3047 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/MethodDeclaration.cs @@ -0,0 +1,104 @@ +// +// MethodDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class MethodDeclaration : EntityDeclaration + { + public override SymbolKind SymbolKind { + get { return SymbolKind.Method; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole (Roles.Constraint); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public bool IsExtensionMethod { + get { + ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole (Roles.Parameter); + return pd != null && pd.ParameterModifier == ParameterModifier.This; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMethodDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMethodDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMethodDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MethodDeclaration o = other as MethodDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/OperatorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/OperatorDeclaration.cs new file mode 100644 index 000000000..911861cd3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/OperatorDeclaration.cs @@ -0,0 +1,303 @@ +// +// OperatorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum OperatorType + { + // Unary operators + LogicalNot, + OnesComplement, + Increment, + Decrement, + True, + False, + + // Unary and Binary operators + Addition, + Subtraction, + + UnaryPlus, + UnaryNegation, + + // Binary operators + Multiply, + Division, + Modulus, + BitwiseAnd, + BitwiseOr, + ExclusiveOr, + LeftShift, + RightShift, + Equality, + Inequality, + GreaterThan, + LessThan, + GreaterThanOrEqual, + LessThanOrEqual, + + // Implicit and Explicit + Implicit, + Explicit + } + + public class OperatorDeclaration : EntityDeclaration + { + public static readonly TokenRole OperatorKeywordRole = new TokenRole ("operator"); + + // Unary operators + public static readonly TokenRole LogicalNotRole = new TokenRole ("!"); + public static readonly TokenRole OnesComplementRole = new TokenRole ("~"); + public static readonly TokenRole IncrementRole = new TokenRole ("++"); + public static readonly TokenRole DecrementRole = new TokenRole ("--"); + public static readonly TokenRole TrueRole = new TokenRole ("true"); + public static readonly TokenRole FalseRole = new TokenRole ("false"); + + // Unary and Binary operators + public static readonly TokenRole AdditionRole = new TokenRole ("+"); + public static readonly TokenRole SubtractionRole = new TokenRole ("-"); + + // Binary operators + public static readonly TokenRole MultiplyRole = new TokenRole ("*"); + public static readonly TokenRole DivisionRole = new TokenRole ("/"); + public static readonly TokenRole ModulusRole = new TokenRole ("%"); + public static readonly TokenRole BitwiseAndRole = new TokenRole ("&"); + public static readonly TokenRole BitwiseOrRole = new TokenRole ("|"); + public static readonly TokenRole ExclusiveOrRole = new TokenRole ("^"); + public static readonly TokenRole LeftShiftRole = new TokenRole ("<<"); + public static readonly TokenRole RightShiftRole = new TokenRole (">>"); + public static readonly TokenRole EqualityRole = new TokenRole ("=="); + public static readonly TokenRole InequalityRole = new TokenRole ("!="); + public static readonly TokenRole GreaterThanRole = new TokenRole (">"); + public static readonly TokenRole LessThanRole = new TokenRole ("<"); + public static readonly TokenRole GreaterThanOrEqualRole = new TokenRole (">="); + public static readonly TokenRole LessThanOrEqualRole = new TokenRole ("<="); + + public static readonly TokenRole ExplicitRole = new TokenRole ("explicit"); + public static readonly TokenRole ImplicitRole = new TokenRole ("implicit"); + + static readonly string[][] names; + + static OperatorDeclaration() + { + names = new string[(int)OperatorType.Explicit + 1][]; + names[(int)OperatorType.LogicalNot] = new string[] { "!", "op_LogicalNot" }; + names[(int)OperatorType.OnesComplement] = new string[] { "~", "op_OnesComplement" }; + names[(int)OperatorType.Increment] = new string[] { "++", "op_Increment" }; + names[(int)OperatorType.Decrement] = new string[] { "--", "op_Decrement" }; + names[(int)OperatorType.True] = new string[] { "true", "op_True" }; + names[(int)OperatorType.False] = new string[] { "false", "op_False" }; + names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" }; + names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" }; + names[(int)OperatorType.UnaryPlus] = new string[] { "+", "op_UnaryPlus" }; + names[(int)OperatorType.UnaryNegation] = new string[] { "-", "op_UnaryNegation" }; + names[(int)OperatorType.Multiply] = new string[] { "*", "op_Multiply" }; + names[(int)OperatorType.Division] = new string[] { "/", "op_Division" }; + names[(int)OperatorType.Modulus] = new string[] { "%", "op_Modulus" }; + names[(int)OperatorType.BitwiseAnd] = new string[] { "&", "op_BitwiseAnd" }; + names[(int)OperatorType.BitwiseOr] = new string[] { "|", "op_BitwiseOr" }; + names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" }; + names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" }; + names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" }; + names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" }; + names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" }; + names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" }; + names[(int)OperatorType.LessThan] = new string[] { "<", "op_LessThan" }; + names[(int)OperatorType.GreaterThanOrEqual] = new string[] { ">=", "op_GreaterThanOrEqual" }; + names[(int)OperatorType.LessThanOrEqual] = new string[] { "<=", "op_LessThanOrEqual" }; + names[(int)OperatorType.Implicit] = new string[] { "implicit", "op_Implicit" }; + names[(int)OperatorType.Explicit] = new string[] { "explicit", "op_Explicit" }; + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Operator; } + } + + OperatorType operatorType; + + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (OperatorKeywordRole); } + } + + public CSharpTokenNode OperatorTypeToken { + get { return GetChildByRole (GetRole (OperatorType)); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + /// + /// Gets the operator type from the method name, or null, if the method does not represent one of the known operator types. + /// + public static OperatorType? GetOperatorType(string methodName) + { + for (int i = 0; i < names.Length; ++i) { + if (names[i][1] == methodName) + return (OperatorType)i; + } + + return null; + } + + public static TokenRole GetRole (OperatorType type) + { + switch (type) { + case OperatorType.LogicalNot: + return LogicalNotRole; + case OperatorType.OnesComplement: + return OnesComplementRole; + case OperatorType.Increment: + return IncrementRole; + case OperatorType.Decrement: + return DecrementRole; + case OperatorType.True: + return TrueRole; + case OperatorType.False: + return FalseRole; + + case OperatorType.Addition: + case OperatorType.UnaryPlus: + return AdditionRole; + case OperatorType.Subtraction: + case OperatorType.UnaryNegation: + return SubtractionRole; + + case OperatorType.Multiply: + return MultiplyRole; + case OperatorType.Division: + return DivisionRole; + case OperatorType.Modulus: + return ModulusRole; + case OperatorType.BitwiseAnd: + return BitwiseAndRole; + case OperatorType.BitwiseOr: + return BitwiseOrRole; + case OperatorType.ExclusiveOr: + return ExclusiveOrRole; + case OperatorType.LeftShift: + return LeftShiftRole; + case OperatorType.RightShift: + return RightShiftRole; + case OperatorType.Equality: + return EqualityRole; + case OperatorType.Inequality: + return InequalityRole; + case OperatorType.GreaterThan: + return GreaterThanRole; + case OperatorType.LessThan: + return LessThanRole; + case OperatorType.GreaterThanOrEqual: + return GreaterThanOrEqualRole; + case OperatorType.LessThanOrEqual: + return LessThanOrEqualRole; + + case OperatorType.Implicit: + return ImplicitRole; + case OperatorType.Explicit: + return ExplicitRole; + + default: + throw new System.ArgumentOutOfRangeException (); + } + } + + /// + /// Gets the method name for the operator type. ("op_Addition", "op_Implicit", etc.) + /// + public static string GetName (OperatorType type) + { + return names[(int)type][1]; + } + + /// + /// Gets the token for the operator type ("+", "implicit", etc.) + /// + public static string GetToken (OperatorType type) + { + return names[(int)type][0]; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitOperatorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitOperatorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitOperatorDeclaration (this, data); + } + + public override string Name { + get { return GetName (this.OperatorType); } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + OperatorDeclaration o = other as OperatorDeclaration; + return o != null && this.MatchAttributesAndModifiers (o, match) && this.OperatorType == o.OperatorType + && this.ReturnType.DoMatch (o.ReturnType, match) + && this.Parameters.DoMatch (o.Parameters, match) && this.Body.DoMatch (o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/ParameterDeclaration.cs new file mode 100644 index 000000000..cc69ff1fd --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/ParameterDeclaration.cs @@ -0,0 +1,147 @@ +// +// ParameterDeclarationExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum ParameterModifier { + None, + Ref, + Out, + Params, + This + } + + public class ParameterDeclaration : AstNode + { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; + public static readonly TokenRole RefModifierRole = new TokenRole("ref"); + public static readonly TokenRole OutModifierRole = new TokenRole("out"); + public static readonly TokenRole ParamsModifierRole = new TokenRole("params"); + public static readonly TokenRole ThisModifierRole = new TokenRole("this"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstNodeCollection Attributes { + get { return GetChildrenByRole (AttributeRole); } + } + + ParameterModifier parameterModifier; + + public ParameterModifier ParameterModifier { + get { return parameterModifier; } + set { + ThrowIfFrozen(); + parameterModifier = value; + } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression DefaultExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitParameterDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitParameterDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitParameterDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ParameterDeclaration o = other as ParameterDeclaration; + return o != null && this.Attributes.DoMatch(o.Attributes, match) && this.ParameterModifier == o.ParameterModifier + && this.Type.DoMatch(o.Type, match) && MatchString(this.Name, o.Name) + && this.DefaultExpression.DoMatch(o.DefaultExpression, match); + } + + public ParameterDeclaration() + { + } + + public ParameterDeclaration(AstType type, string name, ParameterModifier modifier = ParameterModifier.None) + { + Type = type; + Name = name; + ParameterModifier = modifier; + } + + public ParameterDeclaration(string name, ParameterModifier modifier = ParameterModifier.None) + { + Name = name; + ParameterModifier = modifier; + } + + public new ParameterDeclaration Clone() + { + return (ParameterDeclaration)base.Clone(); + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/PropertyDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/PropertyDeclaration.cs new file mode 100644 index 000000000..1f137e0c9 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/PropertyDeclaration.cs @@ -0,0 +1,92 @@ +// +// PropertyDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class PropertyDeclaration : EntityDeclaration + { + public static readonly TokenRole GetKeywordRole = new TokenRole ("get"); + public static readonly TokenRole SetKeywordRole = new TokenRole ("set"); + public static readonly Role GetterRole = new Role("Getter", Accessor.Null); + public static readonly Role SetterRole = new Role("Setter", Accessor.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Property; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor Getter { + get { return GetChildByRole(GetterRole); } + set { SetChildByRole(GetterRole, value); } + } + + public Accessor Setter { + get { return GetChildByRole(SetterRole); } + set { SetChildByRole(SetterRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPropertyDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPropertyDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPropertyDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PropertyDeclaration o = other as PropertyDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/VariableInitializer.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/VariableInitializer.cs new file mode 100644 index 000000000..dbf4bbe3d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeMembers/VariableInitializer.cs @@ -0,0 +1,174 @@ +// +// VariableInitializer.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public class VariableInitializer : AstNode + { + #region Null + public new static readonly VariableInitializer Null = new NullVariableInitializer (); + + sealed class NullVariableInitializer : VariableInitializer + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator VariableInitializer(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : VariableInitializer, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public VariableInitializer() + { + } + + public VariableInitializer(string name, Expression initializer = null) + { + this.Name = name; + this.Initializer = initializer; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Initializer { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitVariableInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitVariableInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitVariableInitializer (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + VariableInitializer o = other as VariableInitializer; + return o != null && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Ast/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Ast/TypeSystemAstBuilder.cs new file mode 100644 index 000000000..476ab8dcb --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Ast/TypeSystemAstBuilder.cs @@ -0,0 +1,1143 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Converts from type system to the C# AST. + /// + public class TypeSystemAstBuilder + { + readonly CSharpResolver resolver; + + #region Constructor + /// + /// Creates a new TypeSystemAstBuilder. + /// + /// + /// A resolver initialized for the position where the type will be inserted. + /// + public TypeSystemAstBuilder(CSharpResolver resolver) + { + if (resolver == null) + throw new ArgumentNullException("resolver"); + this.resolver = resolver; + InitProperties(); + } + + /// + /// Creates a new TypeSystemAstBuilder. + /// + public TypeSystemAstBuilder() + { + InitProperties(); + } + #endregion + + #region Properties + void InitProperties() + { + this.ShowAccessibility = true; + this.ShowModifiers = true; + this.ShowBaseTypes = true; + this.ShowTypeParameters = true; + this.ShowTypeParameterConstraints = true; + this.ShowParameterNames = true; + this.ShowConstantValues = true; + this.UseAliases = true; + } + + /// + /// Specifies whether the ast builder should add annotations to type references. + /// The default value is false. + /// + public bool AddTypeReferenceAnnotations { get; set; } + + /// + /// Specifies whether the ast builder should add ResolveResult annotations to AST nodes. + /// The default value is false. + /// + public bool AddResolveResultAnnotations { get; set; } + + /// + /// Controls the accessibility modifiers are shown. + /// The default value is true. + /// + public bool ShowAccessibility { get; set; } + + /// + /// Controls the non-accessibility modifiers are shown. + /// The default value is true. + /// + public bool ShowModifiers { get; set; } + + /// + /// Controls whether base type references are shown. + /// The default value is true. + /// + public bool ShowBaseTypes { get; set; } + + /// + /// Controls whether type parameter declarations are shown. + /// The default value is true. + /// + public bool ShowTypeParameters { get; set; } + + /// + /// Controls whether constraints on type parameter declarations are shown. + /// Has no effect if ShowTypeParameters is false. + /// The default value is true. + /// + public bool ShowTypeParameterConstraints { get; set; } + + /// + /// Controls whether the names of parameters are shown. + /// The default value is true. + /// + public bool ShowParameterNames { get; set; } + + /// + /// Controls whether to show default values of optional parameters, and the values of constant fields. + /// The default value is true. + /// + public bool ShowConstantValues { get; set; } + + /// + /// Controls whether to show attributes. + /// The default value is false. + /// + public bool ShowAttributes { get; set; } + + /// + /// Controls whether to use fully-qualified type names or short type names. + /// The default value is false. + /// + public bool AlwaysUseShortTypeNames { get; set; } + + /// + /// Determines the name lookup mode for converting a type name. + /// + /// The default value is NameLookupMode.Expression, which means the name is disambiguated + /// for use in expression context. + /// + public NameLookupMode NameLookupMode { get; set; } + + /// + /// Controls whether to generate a body that throws a System.NotImplementedException. + /// The default value is false. + /// + public bool GenerateBody { get; set; } + + /// + /// Controls whether to generate custom events. + /// The default value is false. + /// + public bool UseCustomEvents { get; set; } + + /// + /// Controls if unbound type argument names are inserted in the ast or not. + /// The default value is false. + /// + public bool ConvertUnboundTypeArguments { get; set;} + + /// + /// Controls if aliases should be used inside the type name or not. + /// The default value is true. + /// + public bool UseAliases { get; set; } + #endregion + + #region Convert Type + public AstType ConvertType(IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + AstType astType = ConvertTypeHelper(type); + if (AddTypeReferenceAnnotations) + astType.AddAnnotation(type); + if (AddResolveResultAnnotations) + astType.AddAnnotation(new TypeResolveResult(type)); + return astType; + } + + public AstType ConvertType(FullTypeName fullTypeName) + { + if (resolver != null) { + foreach (var asm in resolver.Compilation.Assemblies) { + var def = asm.GetTypeDefinition(fullTypeName); + if (def != null) { + return ConvertType(def); + } + } + } + TopLevelTypeName top = fullTypeName.TopLevelTypeName; + AstType type; + if (string.IsNullOrEmpty(top.Namespace)) { + type = new SimpleType(top.Name); + } else { + type = new SimpleType(top.Namespace).MemberType(top.Name); + } + for (int i = 0; i < fullTypeName.NestingLevel; i++) { + type = type.MemberType(fullTypeName.GetNestedTypeName(i)); + } + return type; + } + + AstType ConvertTypeHelper(IType type) + { + TypeWithElementType typeWithElementType = type as TypeWithElementType; + if (typeWithElementType != null) { + if (typeWithElementType is PointerType) { + return ConvertType(typeWithElementType.ElementType).MakePointerType(); + } else if (typeWithElementType is ArrayType) { + return ConvertType(typeWithElementType.ElementType).MakeArrayType(((ArrayType)type).Dimensions); + } else if (typeWithElementType is ByReferenceType) { + return ConvertType(typeWithElementType.ElementType).MakeRefType(); + } else { + // e.g. ByReferenceType; not supported as type in C# + return ConvertType(typeWithElementType.ElementType); + } + } + ParameterizedType pt = type as ParameterizedType; + if (pt != null) { + if (pt.Name == "Nullable" && pt.Namespace == "System" && pt.TypeParameterCount == 1) { + return ConvertType(pt.TypeArguments[0]).MakeNullableType(); + } + return ConvertTypeHelper(pt.GetDefinition(), pt.TypeArguments); + } + ITypeDefinition typeDef = type as ITypeDefinition; + if (typeDef != null) { + if (typeDef.TypeParameterCount > 0) { + // Unbound type + IType[] typeArguments = new IType[typeDef.TypeParameterCount]; + for (int i = 0; i < typeArguments.Length; i++) { + typeArguments[i] = SpecialType.UnboundTypeArgument; + } + return ConvertTypeHelper(typeDef, typeArguments); + } else { + return ConvertTypeHelper(typeDef, EmptyList.Instance); + } + } + return new SimpleType(type.Name); + } + + AstType ConvertTypeHelper(ITypeDefinition typeDef, IList typeArguments) + { + Debug.Assert(typeArguments.Count >= typeDef.TypeParameterCount); + + string keyword = KnownTypeReference.GetCSharpNameByTypeCode(typeDef.KnownTypeCode); + if (keyword != null) + return new PrimitiveType(keyword); + + // The number of type parameters belonging to outer classes + int outerTypeParameterCount; + if (typeDef.DeclaringType != null) + outerTypeParameterCount = typeDef.DeclaringType.TypeParameterCount; + else + outerTypeParameterCount = 0; + + if (resolver != null) { + // Look if there's an alias to the target type + if (UseAliases) { + for (ResolvedUsingScope usingScope = resolver.CurrentUsingScope; usingScope != null; usingScope = usingScope.Parent) { + foreach (var pair in usingScope.UsingAliases) { + if (pair.Value is TypeResolveResult) { + if (TypeMatches(pair.Value.Type, typeDef, typeArguments)) + return new SimpleType(pair.Key); + } + } + } + } + + IList localTypeArguments; + if (typeDef.TypeParameterCount > outerTypeParameterCount) { + localTypeArguments = new IType[typeDef.TypeParameterCount - outerTypeParameterCount]; + for (int i = 0; i < localTypeArguments.Count; i++) { + localTypeArguments[i] = typeArguments[outerTypeParameterCount + i]; + } + } else { + localTypeArguments = EmptyList.Instance; + } + ResolveResult rr = resolver.LookupSimpleNameOrTypeName(typeDef.Name, localTypeArguments, NameLookupMode); + TypeResolveResult trr = rr as TypeResolveResult; + if (trr != null || (localTypeArguments.Count == 0 && resolver.IsVariableReferenceWithSameType(rr, typeDef.Name, out trr))) { + if (!trr.IsError && TypeMatches(trr.Type, typeDef, typeArguments)) { + // We can use the short type name + SimpleType shortResult = new SimpleType(typeDef.Name); + AddTypeArguments(shortResult, typeDef, typeArguments, outerTypeParameterCount, typeDef.TypeParameterCount); + return shortResult; + } + } + } + + if (AlwaysUseShortTypeNames) { + var shortResult = new SimpleType(typeDef.Name); + AddTypeArguments(shortResult, typeDef, typeArguments, outerTypeParameterCount, typeDef.TypeParameterCount); + return shortResult; + } + MemberType result = new MemberType(); + if (typeDef.DeclaringTypeDefinition != null) { + // Handle nested types + result.Target = ConvertTypeHelper(typeDef.DeclaringTypeDefinition, typeArguments); + } else { + // Handle top-level types + if (string.IsNullOrEmpty(typeDef.Namespace)) { + result.Target = new SimpleType("global"); + result.IsDoubleColon = true; + } else { + result.Target = ConvertNamespace(typeDef.Namespace); + } + } + result.MemberName = typeDef.Name; + AddTypeArguments(result, typeDef, typeArguments, outerTypeParameterCount, typeDef.TypeParameterCount); + return result; + } + + /// + /// Gets whether 'type' is the same as 'typeDef' parameterized with the given type arguments. + /// + bool TypeMatches(IType type, ITypeDefinition typeDef, IList typeArguments) + { + if (typeDef.TypeParameterCount == 0) { + return typeDef.Equals(type); + } else { + if (!typeDef.Equals(type.GetDefinition())) + return false; + ParameterizedType pt = type as ParameterizedType; + if (pt == null) { + return typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument); + } + var ta = pt.TypeArguments; + for (int i = 0; i < ta.Count; i++) { + if (!ta[i].Equals(typeArguments[i])) + return false; + } + return true; + } + } + + /// + /// Adds type arguments to the result type. + /// + /// The result AST node (a SimpleType or MemberType) + /// The type definition that owns the type parameters + /// The list of type arguments + /// Index of first type argument to add + /// Index after last type argument to add + void AddTypeArguments(AstType result, ITypeDefinition typeDef, IList typeArguments, int startIndex, int endIndex) + { + Debug.Assert(endIndex <= typeDef.TypeParameterCount); + for (int i = startIndex; i < endIndex; i++) { + if (ConvertUnboundTypeArguments && typeArguments[i].Kind == TypeKind.UnboundTypeArgument) { + result.AddChild(new SimpleType(typeDef.TypeParameters[i].Name), Roles.TypeArgument); + } else { + result.AddChild(ConvertType(typeArguments[i]), Roles.TypeArgument); + } + } + } + + public AstType ConvertNamespace(string namespaceName) + { + if (resolver != null) { + // Look if there's an alias to the target namespace + if (UseAliases) { + for (ResolvedUsingScope usingScope = resolver.CurrentUsingScope; usingScope != null; usingScope = usingScope.Parent) { + foreach (var pair in usingScope.UsingAliases) { + NamespaceResolveResult nrr = pair.Value as NamespaceResolveResult; + if (nrr != null && nrr.NamespaceName == namespaceName) + return new SimpleType(pair.Key); + } + } + } + } + + int pos = namespaceName.LastIndexOf('.'); + if (pos < 0) { + if (IsValidNamespace(namespaceName)) { + return new SimpleType(namespaceName); + } else { + return new MemberType { + Target = new SimpleType("global"), + IsDoubleColon = true, + MemberName = namespaceName + }; + } + } else { + string parentNamespace = namespaceName.Substring(0, pos); + string localNamespace = namespaceName.Substring(pos + 1); + return new MemberType { + Target = ConvertNamespace(parentNamespace), + MemberName = localNamespace + }; + } + } + + bool IsValidNamespace(string firstNamespacePart) + { + if (resolver == null) + return true; // just assume namespaces are valid if we don't have a resolver + NamespaceResolveResult nrr = resolver.ResolveSimpleName(firstNamespacePart, EmptyList.Instance) as NamespaceResolveResult; + return nrr != null && !nrr.IsError && nrr.NamespaceName == firstNamespacePart; + } + #endregion + + #region Convert Attribute + public Attribute ConvertAttribute(IAttribute attribute) + { + Attribute attr = new Attribute(); + attr.Type = ConvertType(attribute.AttributeType); + SimpleType st = attr.Type as SimpleType; + MemberType mt = attr.Type as MemberType; + if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { + st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9); + } else if (mt != null && mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) { + mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); + } + foreach (ResolveResult arg in attribute.PositionalArguments) { + attr.Arguments.Add(ConvertConstantValue(arg)); + } + foreach (var pair in attribute.NamedArguments) { + attr.Arguments.Add(new NamedExpression(pair.Key.Name, ConvertConstantValue(pair.Value))); + } + return attr; + } + #endregion + + #region Convert Constant Value + public Expression ConvertConstantValue(ResolveResult rr) + { + if (rr == null) + throw new ArgumentNullException("rr"); + if (rr is ConversionResolveResult) { + // unpack ConversionResolveResult if necessary + // (e.g. a boxing conversion or string->object reference conversion) + rr = ((ConversionResolveResult)rr).Input; + } + + if (rr is TypeOfResolveResult) { + var expr = new TypeOfExpression(ConvertType(((TypeOfResolveResult)rr).ReferencedType)); + if (AddResolveResultAnnotations) + expr.AddAnnotation(rr); + return expr; + } else if (rr is ArrayCreateResolveResult) { + ArrayCreateResolveResult acrr = (ArrayCreateResolveResult)rr; + ArrayCreateExpression ace = new ArrayCreateExpression(); + ace.Type = ConvertType(acrr.Type); + ComposedType composedType = ace.Type as ComposedType; + if (composedType != null) { + composedType.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + if (!composedType.HasNullableSpecifier && composedType.PointerRank == 0) + ace.Type = composedType.BaseType; + } + + if (acrr.SizeArguments != null && acrr.InitializerElements == null) { + ace.AdditionalArraySpecifiers.FirstOrNullObject().Remove(); + ace.Arguments.AddRange(acrr.SizeArguments.Select(ConvertConstantValue)); + } + if (acrr.InitializerElements != null) { + ArrayInitializerExpression initializer = new ArrayInitializerExpression(); + initializer.Elements.AddRange(acrr.InitializerElements.Select(ConvertConstantValue)); + ace.Initializer = initializer; + } + if (AddResolveResultAnnotations) + ace.AddAnnotation(rr); + return ace; + } else if (rr.IsCompileTimeConstant) { + return ConvertConstantValue(rr.Type, rr.ConstantValue); + } else { + return new ErrorExpression(); + } + } + + public Expression ConvertConstantValue(IType type, object constantValue) + { + if (type == null) + throw new ArgumentNullException("type"); + if (constantValue == null) { + if (type.IsReferenceType == true) { + var expr = new NullReferenceExpression(); + if (AddResolveResultAnnotations) + expr.AddAnnotation(new ConstantResolveResult(SpecialType.NullType, null)); + return expr; + } else { + var expr = new DefaultValueExpression(ConvertType(type)); + if (AddResolveResultAnnotations) + expr.AddAnnotation(new ConstantResolveResult(type, null)); + return expr; + } + } else if (type.Kind == TypeKind.Enum) { + return ConvertEnumValue(type, (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false)); + } else { + return new PrimitiveExpression(constantValue); + } + } + + bool IsFlagsEnum(ITypeDefinition type) + { + IType flagsAttributeType = type.Compilation.FindType(typeof(System.FlagsAttribute)); + return type.GetAttribute(flagsAttributeType) != null; + } + + Expression ConvertEnumValue(IType type, long val) + { + ITypeDefinition enumDefinition = type.GetDefinition(); + TypeCode enumBaseTypeCode = ReflectionHelper.GetTypeCode(enumDefinition.EnumUnderlyingType); + foreach (IField field in enumDefinition.Fields) { + if (field.IsConst && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.ConstantValue, false), val)) + return ConvertType(type).Member(field.Name); + } + if (IsFlagsEnum(enumDefinition)) { + long enumValue = val; + Expression expr = null; + long negatedEnumValue = ~val; + // limit negatedEnumValue to the appropriate range + switch (enumBaseTypeCode) { + case TypeCode.Byte: + case TypeCode.SByte: + negatedEnumValue &= byte.MaxValue; + break; + case TypeCode.Int16: + case TypeCode.UInt16: + negatedEnumValue &= ushort.MaxValue; + break; + case TypeCode.Int32: + case TypeCode.UInt32: + negatedEnumValue &= uint.MaxValue; + break; + } + Expression negatedExpr = null; + foreach (IField field in enumDefinition.Fields.Where(fld => fld.IsConst)) { + long fieldValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.ConstantValue, false); + if (fieldValue == 0) + continue; // skip None enum value + + if ((fieldValue & enumValue) == fieldValue) { + var fieldExpression = ConvertType(type).Member(field.Name); + if (expr == null) + expr = fieldExpression; + else + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression); + + enumValue &= ~fieldValue; + } + if ((fieldValue & negatedEnumValue) == fieldValue) { + var fieldExpression = ConvertType(type).Member(field.Name); + if (negatedExpr == null) + negatedExpr = fieldExpression; + else + negatedExpr = new BinaryOperatorExpression(negatedExpr, BinaryOperatorType.BitwiseOr, fieldExpression); + + negatedEnumValue &= ~fieldValue; + } + } + if (enumValue == 0 && expr != null) { + if (!(negatedEnumValue == 0 && negatedExpr != null && negatedExpr.Descendants.Count() < expr.Descendants.Count())) { + return expr; + } + } + if (negatedEnumValue == 0 && negatedExpr != null) { + return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); + } + } + return new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type)); + } + + #endregion + + #region Convert Parameter + public ParameterDeclaration ConvertParameter(IParameter parameter) + { + if (parameter == null) + throw new ArgumentNullException("parameter"); + ParameterDeclaration decl = new ParameterDeclaration(); + if (parameter.IsRef) { + decl.ParameterModifier = ParameterModifier.Ref; + } else if (parameter.IsOut) { + decl.ParameterModifier = ParameterModifier.Out; + } else if (parameter.IsParams) { + decl.ParameterModifier = ParameterModifier.Params; + } + if (ShowAttributes) { + decl.Attributes.AddRange (parameter.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (parameter.Type.Kind == TypeKind.ByReference) { + // avoid 'out ref' + decl.Type = ConvertType(((ByReferenceType)parameter.Type).ElementType); + } else { + decl.Type = ConvertType(parameter.Type); + } + if (this.ShowParameterNames) { + decl.Name = parameter.Name; + } + if (parameter.IsOptional && this.ShowConstantValues) { + decl.DefaultExpression = ConvertConstantValue(parameter.Type, parameter.ConstantValue); + } + return decl; + } + #endregion + + #region Convert Entity + public AstNode ConvertSymbol(ISymbol symbol) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + switch (symbol.SymbolKind) { + case SymbolKind.Namespace: + return ConvertNamespaceDeclaration((INamespace)symbol); + case SymbolKind.Variable: + return ConvertVariable((IVariable)symbol); + case SymbolKind.Parameter: + return ConvertParameter((IParameter)symbol); + case SymbolKind.TypeParameter: + return ConvertTypeParameter((ITypeParameter)symbol); + default: + IEntity entity = symbol as IEntity; + if (entity != null) + return ConvertEntity(entity); + throw new ArgumentException("Invalid value for SymbolKind: " + symbol.SymbolKind); + } + } + + public EntityDeclaration ConvertEntity(IEntity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + switch (entity.SymbolKind) { + case SymbolKind.TypeDefinition: + return ConvertTypeDefinition((ITypeDefinition)entity); + case SymbolKind.Field: + return ConvertField((IField)entity); + case SymbolKind.Property: + return ConvertProperty((IProperty)entity); + case SymbolKind.Indexer: + return ConvertIndexer((IProperty)entity); + case SymbolKind.Event: + return ConvertEvent((IEvent)entity); + case SymbolKind.Method: + return ConvertMethod((IMethod)entity); + case SymbolKind.Operator: + return ConvertOperator((IMethod)entity); + case SymbolKind.Constructor: + return ConvertConstructor((IMethod)entity); + case SymbolKind.Destructor: + return ConvertDestructor((IMethod)entity); + case SymbolKind.Accessor: + IMethod accessor = (IMethod)entity; + return ConvertAccessor(accessor, accessor.AccessorOwner != null ? accessor.AccessorOwner.Accessibility : Accessibility.None, false); + default: + throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind); + } + } + + EntityDeclaration ConvertTypeDefinition(ITypeDefinition typeDefinition) + { + Modifiers modifiers = Modifiers.None; + if (this.ShowAccessibility) { + modifiers |= ModifierFromAccessibility(typeDefinition.Accessibility); + } + if (this.ShowModifiers) { + if (typeDefinition.IsStatic) { + modifiers |= Modifiers.Static; + } else if (typeDefinition.IsAbstract) { + modifiers |= Modifiers.Abstract; + } else if (typeDefinition.IsSealed) { + modifiers |= Modifiers.Sealed; + } + if (typeDefinition.IsShadowing) { + modifiers |= Modifiers.New; + } + } + + ClassType classType; + switch (typeDefinition.Kind) { + case TypeKind.Struct: + classType = ClassType.Struct; + modifiers &= ~Modifiers.Sealed; + break; + case TypeKind.Enum: + classType = ClassType.Enum; + modifiers &= ~Modifiers.Sealed; + break; + case TypeKind.Interface: + classType = ClassType.Interface; + modifiers &= ~Modifiers.Abstract; + break; + case TypeKind.Delegate: + IMethod invoke = typeDefinition.GetDelegateInvokeMethod(); + if (invoke != null) { + return ConvertDelegate(invoke, modifiers); + } else { + goto default; + } + default: + classType = ClassType.Class; + break; + } + + var decl = new TypeDeclaration(); + decl.ClassType = classType; + decl.Modifiers = modifiers; + if (ShowAttributes) { + decl.Attributes.AddRange (typeDefinition.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new TypeResolveResult(typeDefinition)); + } + decl.Name = typeDefinition.Name; + + int outerTypeParameterCount = (typeDefinition.DeclaringTypeDefinition == null) ? 0 : typeDefinition.DeclaringTypeDefinition.TypeParameterCount; + + if (this.ShowTypeParameters) { + foreach (ITypeParameter tp in typeDefinition.TypeParameters.Skip(outerTypeParameterCount)) { + decl.TypeParameters.Add(ConvertTypeParameter(tp)); + } + } + + if (this.ShowBaseTypes) { + foreach (IType baseType in typeDefinition.DirectBaseTypes) { + if (baseType.IsKnownType (KnownTypeCode.Enum)) { + if (!typeDefinition.EnumUnderlyingType.IsKnownType (KnownTypeCode.Int32)) { + decl.BaseTypes.Add (ConvertType (typeDefinition.EnumUnderlyingType)); + } + } else if (!baseType.IsKnownType (KnownTypeCode.Object) && + !baseType.IsKnownType (KnownTypeCode.ValueType)) { + decl.BaseTypes.Add (ConvertType (baseType)); + } + } + } + + if (this.ShowTypeParameters && this.ShowTypeParameterConstraints) { + foreach (ITypeParameter tp in typeDefinition.TypeParameters.Skip(outerTypeParameterCount)) { + var constraint = ConvertTypeParameterConstraint(tp); + if (constraint != null) + decl.Constraints.Add(constraint); + } + } + return decl; + } + + DelegateDeclaration ConvertDelegate(IMethod invokeMethod, Modifiers modifiers) + { + ITypeDefinition d = invokeMethod.DeclaringTypeDefinition; + + DelegateDeclaration decl = new DelegateDeclaration(); + decl.Modifiers = modifiers & ~Modifiers.Sealed; + if (ShowAttributes) { + decl.Attributes.AddRange (d.Attributes.Select (a => new AttributeSection (ConvertAttribute (a)))); + decl.Attributes.AddRange (invokeMethod.ReturnTypeAttributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) { + AttributeTarget = "return" + })); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new TypeResolveResult(d)); + } + decl.ReturnType = ConvertType(invokeMethod.ReturnType); + decl.Name = d.Name; + + int outerTypeParameterCount = (d.DeclaringTypeDefinition == null) ? 0 : d.DeclaringTypeDefinition.TypeParameterCount; + + if (this.ShowTypeParameters) { + foreach (ITypeParameter tp in d.TypeParameters.Skip(outerTypeParameterCount)) { + decl.TypeParameters.Add(ConvertTypeParameter(tp)); + } + } + + foreach (IParameter p in invokeMethod.Parameters) { + decl.Parameters.Add(ConvertParameter(p)); + } + + if (this.ShowTypeParameters && this.ShowTypeParameterConstraints) { + foreach (ITypeParameter tp in d.TypeParameters.Skip(outerTypeParameterCount)) { + var constraint = ConvertTypeParameterConstraint(tp); + if (constraint != null) + decl.Constraints.Add(constraint); + } + } + return decl; + } + + FieldDeclaration ConvertField(IField field) + { + FieldDeclaration decl = new FieldDeclaration(); + if (ShowModifiers) { + Modifiers m = GetMemberModifiers(field); + if (field.IsConst) { + m &= ~Modifiers.Static; + m |= Modifiers.Const; + } else if (field.IsReadOnly) { + m |= Modifiers.Readonly; + } else if (field.IsVolatile) { + m |= Modifiers.Volatile; + } + decl.Modifiers = m; + } + if (ShowAttributes) { + decl.Attributes.AddRange (field.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, field)); + } + decl.ReturnType = ConvertType(field.ReturnType); + Expression initializer = null; + if (field.IsConst && this.ShowConstantValues) + initializer = ConvertConstantValue(field.Type, field.ConstantValue); + decl.Variables.Add(new VariableInitializer(field.Name, initializer)); + return decl; + } + + BlockStatement GenerateBodyBlock() + { + if (GenerateBody) { + return new BlockStatement { + new ThrowStatement(new ObjectCreateExpression(ConvertType(new TopLevelTypeName("System", "NotImplementedException", 0)))) + }; + } else { + return BlockStatement.Null; + } + } + + Accessor ConvertAccessor(IMethod accessor, Accessibility ownerAccessibility, bool addParamterAttribute) + { + if (accessor == null) + return Accessor.Null; + Accessor decl = new Accessor(); + if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility) + decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility); + if (ShowAttributes) { + decl.Attributes.AddRange (accessor.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + decl.Attributes.AddRange (accessor.ReturnTypeAttributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) { + AttributeTarget = "return" + })); + if (addParamterAttribute && accessor.Parameters.Count > 0) { + decl.Attributes.AddRange (accessor.Parameters.Last ().Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) { + AttributeTarget = "param" + })); + } + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, accessor)); + } + decl.Body = GenerateBodyBlock(); + return decl; + } + + PropertyDeclaration ConvertProperty(IProperty property) + { + PropertyDeclaration decl = new PropertyDeclaration(); + decl.Modifiers = GetMemberModifiers(property); + if (ShowAttributes) { + decl.Attributes.AddRange (property.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, property)); + } + decl.ReturnType = ConvertType(property.ReturnType); + decl.Name = property.Name; + decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false); + decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true); + decl.PrivateImplementationType = GetExplicitInterfaceType (property); + return decl; + } + + IndexerDeclaration ConvertIndexer(IProperty indexer) + { + IndexerDeclaration decl = new IndexerDeclaration(); + decl.Modifiers = GetMemberModifiers(indexer); + if (ShowAttributes) { + decl.Attributes.AddRange (indexer.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, indexer)); + } + decl.ReturnType = ConvertType(indexer.ReturnType); + foreach (IParameter p in indexer.Parameters) { + decl.Parameters.Add(ConvertParameter(p)); + } + decl.Getter = ConvertAccessor(indexer.Getter, indexer.Accessibility, false); + decl.Setter = ConvertAccessor(indexer.Setter, indexer.Accessibility, true); + decl.PrivateImplementationType = GetExplicitInterfaceType (indexer); + return decl; + } + + EntityDeclaration ConvertEvent(IEvent ev) + { + if (this.UseCustomEvents) { + CustomEventDeclaration decl = new CustomEventDeclaration(); + decl.Modifiers = GetMemberModifiers(ev); + if (ShowAttributes) { + decl.Attributes.AddRange (ev.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, ev)); + } + decl.ReturnType = ConvertType(ev.ReturnType); + decl.Name = ev.Name; + decl.AddAccessor = ConvertAccessor(ev.AddAccessor, ev.Accessibility, true); + decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, ev.Accessibility, true); + return decl; + } else { + EventDeclaration decl = new EventDeclaration(); + decl.Modifiers = GetMemberModifiers(ev); + if (ShowAttributes) { + decl.Attributes.AddRange (ev.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, ev)); + } + decl.ReturnType = ConvertType(ev.ReturnType); + decl.Variables.Add(new VariableInitializer(ev.Name)); + return decl; + } + } + + MethodDeclaration ConvertMethod(IMethod method) + { + MethodDeclaration decl = new MethodDeclaration(); + decl.Modifiers = GetMemberModifiers(method); + if (method.IsAsync && ShowModifiers) + decl.Modifiers |= Modifiers.Async; + if (ShowAttributes) { + decl.Attributes.AddRange (method.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + decl.Attributes.AddRange (method.ReturnTypeAttributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) { + AttributeTarget = "return" + })); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, method)); + } + decl.ReturnType = ConvertType(method.ReturnType); + decl.Name = method.Name; + + if (this.ShowTypeParameters) { + foreach (ITypeParameter tp in method.TypeParameters) { + decl.TypeParameters.Add(ConvertTypeParameter(tp)); + } + } + + foreach (IParameter p in method.Parameters) { + decl.Parameters.Add(ConvertParameter(p)); + } + if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any() && decl.Parameters.First().ParameterModifier == ParameterModifier.None) + decl.Parameters.First().ParameterModifier = ParameterModifier.This; + + if (this.ShowTypeParameters && this.ShowTypeParameterConstraints && !method.IsOverride && !method.IsExplicitInterfaceImplementation) { + foreach (ITypeParameter tp in method.TypeParameters) { + var constraint = ConvertTypeParameterConstraint(tp); + if (constraint != null) + decl.Constraints.Add(constraint); + } + } + decl.Body = GenerateBodyBlock(); + decl.PrivateImplementationType = GetExplicitInterfaceType (method); + return decl; + } + + EntityDeclaration ConvertOperator(IMethod op) + { + OperatorType? opType = OperatorDeclaration.GetOperatorType(op.Name); + if (opType == null) + return ConvertMethod(op); + + OperatorDeclaration decl = new OperatorDeclaration(); + decl.Modifiers = GetMemberModifiers(op); + decl.OperatorType = opType.Value; + decl.ReturnType = ConvertType(op.ReturnType); + foreach (IParameter p in op.Parameters) { + decl.Parameters.Add(ConvertParameter(p)); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, op)); + } + decl.Body = GenerateBodyBlock(); + return decl; + } + + ConstructorDeclaration ConvertConstructor(IMethod ctor) + { + ConstructorDeclaration decl = new ConstructorDeclaration(); + decl.Modifiers = GetMemberModifiers(ctor); + if (ShowAttributes) + decl.Attributes.AddRange (ctor.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + if (ctor.DeclaringTypeDefinition != null) + decl.Name = ctor.DeclaringTypeDefinition.Name; + foreach (IParameter p in ctor.Parameters) { + decl.Parameters.Add(ConvertParameter(p)); + } + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, ctor)); + } + decl.Body = GenerateBodyBlock(); + return decl; + } + + DestructorDeclaration ConvertDestructor(IMethod dtor) + { + DestructorDeclaration decl = new DestructorDeclaration(); + if (dtor.DeclaringTypeDefinition != null) + decl.Name = dtor.DeclaringTypeDefinition.Name; + if (AddResolveResultAnnotations) { + decl.AddAnnotation(new MemberResolveResult(null, dtor)); + } + decl.Body = GenerateBodyBlock(); + return decl; + } + #endregion + + #region Convert Modifiers + public static Modifiers ModifierFromAccessibility(Accessibility accessibility) + { + switch (accessibility) { + case Accessibility.Private: + return Modifiers.Private; + case Accessibility.Public: + return Modifiers.Public; + case Accessibility.Protected: + return Modifiers.Protected; + case Accessibility.Internal: + return Modifiers.Internal; + case Accessibility.ProtectedOrInternal: + case Accessibility.ProtectedAndInternal: + return Modifiers.Protected | Modifiers.Internal; + default: + return Modifiers.None; + } + } + + bool NeedsAccessibility(IMember member) + { + var declaringType = member.DeclaringType; + if ((declaringType != null && declaringType.Kind == TypeKind.Interface) || member.IsExplicitInterfaceImplementation) + return false; + switch (member.SymbolKind) { + case SymbolKind.Constructor: + return !member.IsStatic; + case SymbolKind.Destructor: + return false; + default: + return true; + } + } + + Modifiers GetMemberModifiers(IMember member) + { + Modifiers m = Modifiers.None; + if (this.ShowAccessibility && NeedsAccessibility(member)) { + m |= ModifierFromAccessibility (member.Accessibility); + } + if (this.ShowModifiers) { + if (member.IsStatic) { + m |= Modifiers.Static; + } else { + var declaringType = member.DeclaringType; + if (member.IsAbstract && declaringType != null && declaringType.Kind != TypeKind.Interface) + m |= Modifiers.Abstract; + if (member.IsOverride) + m |= Modifiers.Override; + if (member.IsVirtual && !member.IsAbstract && !member.IsOverride) + m |= Modifiers.Virtual; + if (member.IsSealed) + m |= Modifiers.Sealed; + } + if (member.IsShadowing) + m |= Modifiers.New; + } + return m; + } + #endregion + + #region Convert Type Parameter + TypeParameterDeclaration ConvertTypeParameter(ITypeParameter tp) + { + TypeParameterDeclaration decl = new TypeParameterDeclaration(); + decl.Variance = tp.Variance; + decl.Name = tp.Name; + if (ShowAttributes) + decl.Attributes.AddRange (tp.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)))); + return decl; + } + + Constraint ConvertTypeParameterConstraint(ITypeParameter tp) + { + if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.DirectBaseTypes.All(IsObjectOrValueType)) { + return null; + } + Constraint c = new Constraint(); + c.TypeParameter = new SimpleType (tp.Name); + if (tp.HasReferenceTypeConstraint) { + c.BaseTypes.Add(new PrimitiveType("class")); + } else if (tp.HasValueTypeConstraint) { + c.BaseTypes.Add(new PrimitiveType("struct")); + } + foreach (IType t in tp.DirectBaseTypes) { + if (!IsObjectOrValueType(t)) + c.BaseTypes.Add(ConvertType(t)); + } + if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) { + c.BaseTypes.Add(new PrimitiveType("new")); + } + return c; + } + + static bool IsObjectOrValueType(IType type) + { + ITypeDefinition d = type.GetDefinition(); + return d != null && (d.KnownTypeCode == KnownTypeCode.Object || d.KnownTypeCode == KnownTypeCode.ValueType); + } + #endregion + + #region Convert Variable + public VariableDeclarationStatement ConvertVariable(IVariable v) + { + VariableDeclarationStatement decl = new VariableDeclarationStatement(); + decl.Modifiers = v.IsConst ? Modifiers.Const : Modifiers.None; + decl.Type = ConvertType(v.Type); + Expression initializer = null; + if (v.IsConst) + initializer = ConvertConstantValue(v.Type, v.ConstantValue); + decl.Variables.Add(new VariableInitializer(v.Name, initializer)); + return decl; + } + #endregion + + NamespaceDeclaration ConvertNamespaceDeclaration(INamespace ns) + { + return new NamespaceDeclaration(ns.FullName); + } + + AstType GetExplicitInterfaceType (IMember member) + { + if (member.IsExplicitInterfaceImplementation) { + var baseMember = member.ImplementedInterfaceMembers.FirstOrDefault (); + if (baseMember != null) + return ConvertType (baseMember.DeclaringType); + } + return null; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 929ac7419..a7b2ca295 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -23,7 +23,6 @@ using System.Linq; using System.Threading; using ICSharpCode.Decompiler.Ast; using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; @@ -32,6 +31,7 @@ using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.IL.Transforms; +using ICSharpCode.NRefactory.CSharp.Refactoring; namespace ICSharpCode.Decompiler.CSharp { diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs new file mode 100644 index 000000000..8476f9515 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -0,0 +1,312 @@ +// Copyright (c) 2010-2013 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.IO; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// C# ambience. Used to convert type system symbols to text (usually for displaying the symbol to the user; e.g. in editor tooltips) + /// + public class CSharpAmbience : IAmbience + { + public ConversionFlags ConversionFlags { get; set; } + + #region ConvertSymbol + [Obsolete("Use ConvertSymbol() instead")] + public string ConvertEntity(IEntity entity) + { + return ConvertSymbol(entity); + } + + public string ConvertSymbol(ISymbol symbol) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + + StringWriter writer = new StringWriter(); + ConvertSymbol(symbol, new TextWriterTokenWriter(writer), FormattingOptionsFactory.CreateMono ()); + return writer.ToString(); + } + + [Obsolete("Use ConvertSymbol() instead")] + public void ConvertEntity(IEntity entity, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + ConvertSymbol(entity, writer, formattingPolicy); + } + + public void ConvertSymbol(ISymbol symbol, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + if (writer == null) + throw new ArgumentNullException("writer"); + if (formattingPolicy == null) + throw new ArgumentNullException("formattingPolicy"); + + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + AstNode node = astBuilder.ConvertSymbol(symbol); + EntityDeclaration entityDecl = node as EntityDeclaration; + if (entityDecl != null) + PrintModifiers(entityDecl.Modifiers, writer); + + if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) { + if (node is TypeDeclaration) { + switch (((TypeDeclaration)node).ClassType) { + case ClassType.Class: + writer.WriteKeyword(Roles.ClassKeyword, "class"); + break; + case ClassType.Struct: + writer.WriteKeyword(Roles.StructKeyword, "struct"); + break; + case ClassType.Interface: + writer.WriteKeyword(Roles.InterfaceKeyword, "interface"); + break; + case ClassType.Enum: + writer.WriteKeyword(Roles.EnumKeyword, "enum"); + break; + default: + throw new Exception("Invalid value for ClassType"); + } + writer.Space(); + } else if (node is DelegateDeclaration) { + writer.WriteKeyword(Roles.DelegateKeyword, "delegate"); + writer.Space(); + } else if (node is EventDeclaration) { + writer.WriteKeyword(EventDeclaration.EventKeywordRole, "event"); + writer.Space(); + } else if (node is NamespaceDeclaration) { + writer.WriteKeyword(Roles.NamespaceKeyword, "namespace"); + writer.Space(); + } + } + + if ((ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { + var rt = node.GetChildByRole(Roles.Type); + if (!rt.IsNull) { + rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + writer.Space(); + } + } + + if (symbol is ITypeDefinition) + WriteTypeDeclarationName((ITypeDefinition)symbol, writer, formattingPolicy); + else if (symbol is IMember) + WriteMemberDeclarationName((IMember)symbol, writer, formattingPolicy); + else + writer.WriteIdentifier(Identifier.Create(symbol.Name)); + + if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList && HasParameters(symbol)) { + writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.LBracket : Roles.LPar, symbol.SymbolKind == SymbolKind.Indexer ? "[" : "("); + bool first = true; + foreach (var param in node.GetChildrenByRole(Roles.Parameter)) { + if (first) { + first = false; + } else { + writer.WriteToken(Roles.Comma, ","); + writer.Space(); + } + param.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + } + writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.RBracket : Roles.RPar, symbol.SymbolKind == SymbolKind.Indexer ? "]" : ")"); + } + + if ((ConversionFlags & ConversionFlags.ShowBody) == ConversionFlags.ShowBody && !(node is TypeDeclaration)) { + IProperty property = symbol as IProperty; + if (property != null) { + writer.Space(); + writer.WriteToken(Roles.LBrace, "{"); + writer.Space(); + if (property.CanGet) { + writer.WriteKeyword(PropertyDeclaration.GetKeywordRole, "get"); + writer.WriteToken(Roles.Semicolon, ";"); + writer.Space(); + } + if (property.CanSet) { + writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set"); + writer.WriteToken(Roles.Semicolon, ";"); + writer.Space(); + } + writer.WriteToken(Roles.RBrace, "}"); + } else { + writer.WriteToken(Roles.Semicolon, ";"); + } + } + } + + static bool HasParameters(ISymbol e) + { + switch (e.SymbolKind) { + case SymbolKind.TypeDefinition: + return ((ITypeDefinition)e).Kind == TypeKind.Delegate; + case SymbolKind.Indexer: + case SymbolKind.Method: + case SymbolKind.Operator: + case SymbolKind.Constructor: + case SymbolKind.Destructor: + return true; + default: + return false; + } + } + + TypeSystemAstBuilder CreateAstBuilder() + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(); + astBuilder.AddTypeReferenceAnnotations = true; + astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; + astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + return astBuilder; + } + + void WriteTypeDeclarationName(ITypeDefinition typeDef, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + EntityDeclaration node = astBuilder.ConvertEntity(typeDef); + if (typeDef.DeclaringTypeDefinition != null) { + WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } else if ((ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) == ConversionFlags.UseFullyQualifiedEntityNames) { + if (!string.IsNullOrEmpty(typeDef.Namespace)) { + WriteQualifiedName(typeDef.Namespace, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + } + writer.WriteIdentifier(node.NameToken); + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList) { + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter)); + } + } + + void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + EntityDeclaration node = astBuilder.ConvertEntity(member); + if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null) { + ConvertType(member.DeclaringType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + switch (member.SymbolKind) { + case SymbolKind.Indexer: + writer.WriteKeyword(Roles.Identifier, "this"); + break; + case SymbolKind.Constructor: + WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + break; + case SymbolKind.Destructor: + writer.WriteToken(DestructorDeclaration.TildeRole, "~"); + WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + break; + case SymbolKind.Operator: + switch (member.Name) { + case "op_Implicit": + writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit"); + writer.Space(); + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + ConvertType(member.ReturnType, writer, formattingPolicy); + break; + case "op_Explicit": + writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit"); + writer.Space(); + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + ConvertType(member.ReturnType, writer, formattingPolicy); + break; + default: + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + var operatorType = OperatorDeclaration.GetOperatorType(member.Name); + if (operatorType.HasValue) + writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value)); + else + writer.WriteIdentifier(node.NameToken); + break; + } + break; + default: + writer.WriteIdentifier(Identifier.Create(member.Name)); + break; + } + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList && member.SymbolKind == SymbolKind.Method) { + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter)); + } + } + + void PrintModifiers(Modifiers modifiers, TokenWriter writer) + { + foreach (var m in CSharpModifierToken.AllModifiers) { + if ((modifiers & m) == m) { + writer.WriteKeyword(EntityDeclaration.ModifierRole, CSharpModifierToken.GetModifierName(m)); + writer.Space(); + } + } + } + + void WriteQualifiedName(string name, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + var node = AstType.Create(name); + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + node.AcceptVisitor(outputVisitor); + } + #endregion + + public string ConvertVariable(IVariable v) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + AstNode astNode = astBuilder.ConvertVariable(v); + return astNode.ToString().TrimEnd(';', '\r', '\n', (char)8232); + } + + public string ConvertType(IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; + AstType astType = astBuilder.ConvertType(type); + return astType.ToString(); + } + + public void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; + AstType astType = astBuilder.ConvertType(type); + astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + } + + public string ConvertConstantValue(object constantValue) + { + return TextWriterTokenWriter.PrintPrimitiveValue(constantValue); + } + + public string WrapComment(string comment) + { + return "// " + comment; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpFormattingOptions.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpFormattingOptions.cs new file mode 100644 index 000000000..0a97a1c0b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpFormattingOptions.cs @@ -0,0 +1,1039 @@ +// +// CSharpFormattingOptions.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Reflection; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum BraceStyle + { + DoNotChange, + EndOfLine, + EndOfLineWithoutSpace, + NextLine, + NextLineShifted, + NextLineShifted2, + BannerStyle + } + + public enum PropertyFormatting + { + AllowOneLine, + ForceOneLine, + ForceNewLine + } + + public enum Wrapping { + DoNotChange, + DoNotWrap, + WrapAlways, + WrapIfTooLong + } + + public enum NewLinePlacement { + DoNotCare, + NewLine, + SameLine + } + + public enum UsingPlacement { + TopOfFile, + InsideNamespace + } + + public enum EmptyLineFormatting { + DoNotChange, + Indent, + DoNotIndent + } + + public class CSharpFormattingOptions + { + public string Name { + get; + set; + } + + public bool IsBuiltIn { + get; + set; + } + + public CSharpFormattingOptions Clone () + { + return (CSharpFormattingOptions)MemberwiseClone (); + } + + #region Indentation + public bool IndentNamespaceBody { // tested + get; + set; + } + + public bool IndentClassBody { // tested + get; + set; + } + + public bool IndentInterfaceBody { // tested + get; + set; + } + + public bool IndentStructBody { // tested + get; + set; + } + + public bool IndentEnumBody { // tested + get; + set; + } + + public bool IndentMethodBody { // tested + get; + set; + } + + public bool IndentPropertyBody { // tested + get; + set; + } + + public bool IndentEventBody { // tested + get; + set; + } + + public bool IndentBlocks { // tested + get; + set; + } + + public bool IndentSwitchBody { // tested + get; + set; + } + + public bool IndentCaseBody { // tested + get; + set; + } + + public bool IndentBreakStatements { // tested + get; + set; + } + + public bool AlignEmbeddedStatements { // tested + get; + set; + } + + public bool AlignElseInIfStatements { + get; + set; + } + + + + public PropertyFormatting AutoPropertyFormatting { // tested + get; + set; + } + + public PropertyFormatting SimplePropertyFormatting { // tested + get; + set; + } + + public EmptyLineFormatting EmptyLineFormatting { + get; + set; + } + + public bool IndentPreprocessorDirectives { // tested + get; + set; + } + + public bool AlignToMemberReferenceDot { // TODO! + get; + set; + } + + public bool IndentBlocksInsideExpressions { + get; + set; + } + #endregion + + #region Braces + public BraceStyle NamespaceBraceStyle { // tested + get; + set; + } + + public BraceStyle ClassBraceStyle { // tested + get; + set; + } + + public BraceStyle InterfaceBraceStyle { // tested + get; + set; + } + + public BraceStyle StructBraceStyle { // tested + get; + set; + } + + public BraceStyle EnumBraceStyle { // tested + get; + set; + } + + public BraceStyle MethodBraceStyle { // tested + get; + set; + } + + public BraceStyle AnonymousMethodBraceStyle { + get; + set; + } + + public BraceStyle ConstructorBraceStyle { // tested + get; + set; + } + + public BraceStyle DestructorBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertyBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertyGetBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertySetBraceStyle { // tested + get; + set; + } + + public PropertyFormatting SimpleGetBlockFormatting { // tested + get; + set; + } + + public PropertyFormatting SimpleSetBlockFormatting { // tested + get; + set; + } + + public BraceStyle EventBraceStyle { // tested + get; + set; + } + + public BraceStyle EventAddBraceStyle { // tested + get; + set; + } + + public BraceStyle EventRemoveBraceStyle { // tested + get; + set; + } + + public bool AllowEventAddBlockInline { // tested + get; + set; + } + + public bool AllowEventRemoveBlockInline { // tested + get; + set; + } + + public BraceStyle StatementBraceStyle { // tested + get; + set; + } + + public bool AllowIfBlockInline { + get; + set; + } + + bool allowOneLinedArrayInitialziers = true; + public bool AllowOneLinedArrayInitialziers { + get { + return allowOneLinedArrayInitialziers; + } + set { + allowOneLinedArrayInitialziers = value; + } + } + #endregion + + #region NewLines + public NewLinePlacement ElseNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement ElseIfNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement CatchNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement FinallyNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement WhileNewLinePlacement { // tested + get; + set; + } + + NewLinePlacement embeddedStatementPlacement = NewLinePlacement.NewLine; + public NewLinePlacement EmbeddedStatementPlacement { + get { + return embeddedStatementPlacement; + } + set { + embeddedStatementPlacement = value; + } + } + #endregion + + #region Spaces + // Methods + public bool SpaceBeforeMethodDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyMethodDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeMethodDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceAfterMethodDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceWithinMethodDeclarationParentheses { // tested + get; + set; + } + + // Method calls + public bool SpaceBeforeMethodCallParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyMethodCallParentheses { // tested + get; + set; + } + + public bool SpaceBeforeMethodCallParameterComma { // tested + get; + set; + } + + public bool SpaceAfterMethodCallParameterComma { // tested + get; + set; + } + + public bool SpaceWithinMethodCallParentheses { // tested + get; + set; + } + + // fields + + public bool SpaceBeforeFieldDeclarationComma { // tested + get; + set; + } + + public bool SpaceAfterFieldDeclarationComma { // tested + get; + set; + } + + // local variables + + public bool SpaceBeforeLocalVariableDeclarationComma { // tested + get; + set; + } + + public bool SpaceAfterLocalVariableDeclarationComma { // tested + get; + set; + } + + // constructors + + public bool SpaceBeforeConstructorDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyConstructorDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBeforeConstructorDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceAfterConstructorDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceWithinConstructorDeclarationParentheses { // tested + get; + set; + } + + public NewLinePlacement NewLineBeforeConstructorInitializerColon { + get; + set; + } + + public NewLinePlacement NewLineAfterConstructorInitializerColon { + get; + set; + } + + // indexer + public bool SpaceBeforeIndexerDeclarationBracket { // tested + get; + set; + } + + public bool SpaceWithinIndexerDeclarationBracket { // tested + get; + set; + } + + public bool SpaceBeforeIndexerDeclarationParameterComma { + get; + set; + } + + public bool SpaceAfterIndexerDeclarationParameterComma { + get; + set; + } + + // delegates + + public bool SpaceBeforeDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBetweenEmptyDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeDelegateDeclarationParameterComma { + get; + set; + } + + public bool SpaceAfterDelegateDeclarationParameterComma { + get; + set; + } + + public bool SpaceWithinDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeNewParentheses { // tested + get; + set; + } + + public bool SpaceBeforeIfParentheses { // tested + get; + set; + } + + public bool SpaceBeforeWhileParentheses { // tested + get; + set; + } + + public bool SpaceBeforeForParentheses { // tested + get; + set; + } + + public bool SpaceBeforeForeachParentheses { // tested + get; + set; + } + + public bool SpaceBeforeCatchParentheses { // tested + get; + set; + } + + public bool SpaceBeforeSwitchParentheses { // tested + get; + set; + } + + public bool SpaceBeforeLockParentheses { // tested + get; + set; + } + + public bool SpaceBeforeUsingParentheses { // tested + get; + set; + } + + public bool SpaceAroundAssignment { // tested + get; + set; + } + + public bool SpaceAroundLogicalOperator { // tested + get; + set; + } + + public bool SpaceAroundEqualityOperator { // tested + get; + set; + } + + public bool SpaceAroundRelationalOperator { // tested + get; + set; + } + + public bool SpaceAroundBitwiseOperator { // tested + get; + set; + } + + public bool SpaceAroundAdditiveOperator { // tested + get; + set; + } + + public bool SpaceAroundMultiplicativeOperator { // tested + get; + set; + } + + public bool SpaceAroundShiftOperator { // tested + get; + set; + } + + public bool SpaceAroundNullCoalescingOperator { // Tested + get; + set; + } + + public bool SpaceAfterUnsafeAddressOfOperator { // Tested + get; + set; + } + + public bool SpaceAfterUnsafeAsteriskOfOperator { // Tested + get; + set; + } + + public bool SpaceAroundUnsafeArrowOperator { // Tested + get; + set; + } + + public bool SpacesWithinParentheses { // tested + get; + set; + } + + public bool SpacesWithinIfParentheses { // tested + get; + set; + } + + public bool SpacesWithinWhileParentheses { // tested + get; + set; + } + + public bool SpacesWithinForParentheses { // tested + get; + set; + } + + public bool SpacesWithinForeachParentheses { // tested + get; + set; + } + + public bool SpacesWithinCatchParentheses { // tested + get; + set; + } + + public bool SpacesWithinSwitchParentheses { // tested + get; + set; + } + + public bool SpacesWithinLockParentheses { // tested + get; + set; + } + + public bool SpacesWithinUsingParentheses { // tested + get; + set; + } + + public bool SpacesWithinCastParentheses { // tested + get; + set; + } + + public bool SpacesWithinSizeOfParentheses { // tested + get; + set; + } + + public bool SpaceBeforeSizeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinTypeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinNewParentheses { // tested + get; + set; + } + + public bool SpacesBetweenEmptyNewParentheses { // tested + get; + set; + } + + public bool SpaceBeforeNewParameterComma { // tested + get; + set; + } + + public bool SpaceAfterNewParameterComma { // tested + get; + set; + } + + public bool SpaceBeforeTypeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinCheckedExpressionParantheses { // tested + get; + set; + } + + public bool SpaceBeforeConditionalOperatorCondition { // tested + get; + set; + } + + public bool SpaceAfterConditionalOperatorCondition { // tested + get; + set; + } + + public bool SpaceBeforeConditionalOperatorSeparator { // tested + get; + set; + } + + public bool SpaceAfterConditionalOperatorSeparator { // tested + get; + set; + } + + // brackets + public bool SpacesWithinBrackets { // tested + get; + set; + } + + public bool SpacesBeforeBrackets { // tested + get; + set; + } + + public bool SpaceBeforeBracketComma { // tested + get; + set; + } + + public bool SpaceAfterBracketComma { // tested + get; + set; + } + + public bool SpaceBeforeForSemicolon { // tested + get; + set; + } + + public bool SpaceAfterForSemicolon { // tested + get; + set; + } + + public bool SpaceAfterTypecast { // tested + get; + set; + } + + public bool SpaceBeforeArrayDeclarationBrackets { // tested + get; + set; + } + + public bool SpaceInNamedArgumentAfterDoubleColon { + get; + set; + } + + public bool RemoveEndOfLineWhiteSpace { + get; + set; + } + + public bool SpaceBeforeSemicolon { + get; + set; + } + #endregion + + #region Blank Lines + public int MinimumBlankLinesBeforeUsings { + get; + set; + } + + public int MinimumBlankLinesAfterUsings { + get; + set; + } + + public int MinimumBlankLinesBeforeFirstDeclaration { + get; + set; + } + + public int MinimumBlankLinesBetweenTypes { + get; + set; + } + + public int MinimumBlankLinesBetweenFields { + get; + set; + } + + public int MinimumBlankLinesBetweenEventFields { + get; + set; + } + + public int MinimumBlankLinesBetweenMembers { + get; + set; + } + + public int MinimumBlankLinesAroundRegion { + get; + set; + } + + public int MinimumBlankLinesInsideRegion { + get; + set; + } + + #endregion + + + #region Keep formatting + public bool KeepCommentsAtFirstColumn { + get; + set; + } + #endregion + + #region Wrapping + + public Wrapping ArrayInitializerWrapping { + get; + set; + } + + public BraceStyle ArrayInitializerBraceStyle { + get; + set; + } + + public Wrapping ChainedMethodCallWrapping { + get; + set; + } + + public Wrapping MethodCallArgumentWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferMethodCallOpenParentheses { + get; + set; + } + + public NewLinePlacement MethodCallClosingParenthesesOnNewLine { + get; + set; + } + + public Wrapping IndexerArgumentWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferIndexerOpenBracket { + get; + set; + } + + public NewLinePlacement IndexerClosingBracketOnNewLine { + get; + set; + } + + public Wrapping MethodDeclarationParameterWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferMethodDeclarationOpenParentheses { + get; + set; + } + + public NewLinePlacement MethodDeclarationClosingParenthesesOnNewLine { + get; + set; + } + + public Wrapping IndexerDeclarationParameterWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferIndexerDeclarationOpenBracket { + get; + set; + } + + public NewLinePlacement IndexerDeclarationClosingBracketOnNewLine { + get; + set; + } + + public bool AlignToFirstIndexerArgument { + get; + set; + } + + public bool AlignToFirstIndexerDeclarationParameter { + get; + set; + } + + public bool AlignToFirstMethodCallArgument { + get; + set; + } + + public bool AlignToFirstMethodDeclarationParameter { + get; + set; + } + + public NewLinePlacement NewLineBeforeNewQueryClause { + get; + set; + } + + #endregion + + #region Using Declarations + public UsingPlacement UsingPlacement { + get; + set; + } + #endregion + + internal CSharpFormattingOptions() + { + } + + /*public static CSharpFormattingOptions Load (FilePath selectedFile) + { + using (var stream = System.IO.File.OpenRead (selectedFile)) { + return Load (stream); + } + } + + public static CSharpFormattingOptions Load (System.IO.Stream input) + { + CSharpFormattingOptions result = FormattingOptionsFactory.CreateMonoOptions (); + result.Name = "noname"; + using (XmlTextReader reader = new XmlTextReader (input)) { + while (reader.Read ()) { + if (reader.NodeType == XmlNodeType.Element) { + if (reader.LocalName == "Property") { + var info = typeof(CSharpFormattingOptions).GetProperty (reader.GetAttribute ("name")); + string valString = reader.GetAttribute ("value"); + object value; + if (info.PropertyType == typeof(bool)) { + value = Boolean.Parse (valString); + } else if (info.PropertyType == typeof(int)) { + value = Int32.Parse (valString); + } else { + value = Enum.Parse (info.PropertyType, valString); + } + info.SetValue (result, value, null); + } else if (reader.LocalName == "FormattingProfile") { + result.Name = reader.GetAttribute ("name"); + } + } else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "FormattingProfile") { + //Console.WriteLine ("result:" + result.Name); + return result; + } + } + } + return result; + } + + public void Save (string fileName) + { + using (var writer = new XmlTextWriter (fileName, Encoding.Default)) { + writer.Formatting = System.Xml.Formatting.Indented; + writer.Indentation = 1; + writer.IndentChar = '\t'; + writer.WriteStartElement ("FormattingProfile"); + writer.WriteAttributeString ("name", Name); + foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { + if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { + writer.WriteStartElement ("Property"); + writer.WriteAttributeString ("name", info.Name); + writer.WriteAttributeString ("value", info.GetValue (this, null).ToString ()); + writer.WriteEndElement (); + } + } + writer.WriteEndElement (); + } + } + + public bool Equals (CSharpFormattingOptions other) + { + foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { + if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { + object val = info.GetValue (this, null); + object otherVal = info.GetValue (other, null); + if (val == null) { + if (otherVal == null) + continue; + return false; + } + if (!val.Equals (otherVal)) { + //Console.WriteLine ("!equal"); + return false; + } + } + } + //Console.WriteLine ("== equal"); + return true; + }*/ + } +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs new file mode 100644 index 000000000..683a632e9 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -0,0 +1,2440 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Outputs the AST. + /// + public class CSharpOutputVisitor : IAstVisitor + { + readonly protected TokenWriter writer; + readonly protected CSharpFormattingOptions policy; + readonly protected Stack containerStack = new Stack (); + + public CSharpOutputVisitor (TextWriter textWriter, CSharpFormattingOptions formattingPolicy) + { + if (textWriter == null) { + throw new ArgumentNullException ("textWriter"); + } + if (formattingPolicy == null) { + throw new ArgumentNullException ("formattingPolicy"); + } + this.writer = TokenWriter.Create(textWriter); + this.policy = formattingPolicy; + } + + public CSharpOutputVisitor (TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + if (writer == null) { + throw new ArgumentNullException ("writer"); + } + if (formattingPolicy == null) { + throw new ArgumentNullException ("formattingPolicy"); + } + this.writer = new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(writer)); + this.policy = formattingPolicy; + } + + #region StartNode/EndNode + protected virtual void StartNode(AstNode node) + { + // Ensure that nodes are visited in the proper nested order. + // Jumps to different subtrees are allowed only for the child of a placeholder node. + Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek() || containerStack.Peek().NodeType == NodeType.Pattern); + containerStack.Push(node); + writer.StartNode(node); + } + + protected virtual void EndNode(AstNode node) + { + Debug.Assert(node == containerStack.Peek()); + containerStack.Pop(); + writer.EndNode(node); + } + #endregion + + #region Comma + /// + /// Writes a comma. + /// + /// The next node after the comma. + /// When set prevents printing a space after comma. + protected virtual void Comma(AstNode nextNode, bool noSpaceAfterComma = false) + { + Space(policy.SpaceBeforeBracketComma); + // TODO: Comma policy has changed. + writer.WriteToken(Roles.Comma, ","); + Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma); + // TODO: Comma policy has changed. + } + + /// + /// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer + /// + protected virtual void OptionalComma(AstNode pos) + { + // Look if there's a comma after the current node, and insert it if it exists. + while (pos != null && pos.NodeType == NodeType.Whitespace) { + pos = pos.NextSibling; + } + if (pos != null && pos.Role == Roles.Comma) { + Comma(null, noSpaceAfterComma: true); + } + } + + /// + /// Writes an optional semicolon, e.g. at the end of a type or namespace declaration. + /// + protected virtual void OptionalSemicolon(AstNode pos) + { + // Look if there's a semicolon after the current node, and insert it if it exists. + while (pos != null && pos.NodeType == NodeType.Whitespace) { + pos = pos.PrevSibling; + } + if (pos != null && pos.Role == Roles.Semicolon) { + Semicolon(); + } + } + + protected virtual void WriteCommaSeparatedList(IEnumerable list) + { + bool isFirst = true; + foreach (AstNode node in list) { + if (isFirst) { + isFirst = false; + } else { + Comma(node); + } + node.AcceptVisitor(this); + } + } + + protected virtual void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + LPar(); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list); + Space(spaceWithin); + } + RPar(); + } + + #if DOTNET35 + void WriteCommaSeparatedList(IEnumerable list) + { + WriteCommaSeparatedList(list.SafeCast()); + } + + void WriteCommaSeparatedList(IEnumerable list) + { + WriteCommaSeparatedList(list.SafeCast()); + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); + } + + #endif + + protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) + { + WriteToken(Roles.LBracket); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list); + Space(spaceWithin); + } + WriteToken(Roles.RBracket); + } + + protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list) + { + WriteToken(Roles.LBracket); + if (list.Any()) { + Space(policy.SpacesWithinBrackets); + WriteCommaSeparatedList(list); + Space(policy.SpacesWithinBrackets); + } + WriteToken(Roles.RBracket); + } + #endregion + + #region Write tokens + protected bool isAtStartOfLine = true; + + /// + /// Writes a keyword, and all specials up to + /// + protected virtual void WriteKeyword(TokenRole tokenRole) + { + WriteKeyword(tokenRole.Token, tokenRole); + } + + protected virtual void WriteKeyword(string token, Role tokenRole = null) + { + writer.WriteKeyword(tokenRole, token); + isAtStartOfLine = false; + } + + protected virtual void WriteIdentifier(Identifier identifier) + { + writer.WriteIdentifier(identifier); + isAtStartOfLine = false; + } + + protected virtual void WriteIdentifier(string identifier) + { + AstType.Create(identifier).AcceptVisitor(this); + isAtStartOfLine = false; + } + + protected virtual void WriteToken(TokenRole tokenRole) + { + WriteToken(tokenRole.Token, tokenRole); + } + + protected virtual void WriteToken(string token, Role tokenRole) + { + writer.WriteToken(tokenRole, token); + isAtStartOfLine = false; + } + + protected virtual void LPar() + { + WriteToken(Roles.LPar); + } + + protected virtual void RPar() + { + WriteToken(Roles.RPar); + } + + /// + /// Marks the end of a statement + /// + protected virtual void Semicolon() + { + Role role = containerStack.Peek().Role; + // get the role of the current node + if (!(role == ForStatement.InitializerRole || role == ForStatement.IteratorRole || role == UsingStatement.ResourceAcquisitionRole)) { + WriteToken(Roles.Semicolon); + NewLine(); + } + } + + /// + /// Writes a space depending on policy. + /// + protected virtual void Space(bool addSpace = true) + { + if (addSpace) { + writer.Space(); + } + } + + protected virtual void NewLine() + { + writer.NewLine(); + isAtStartOfLine = true; + } + + protected virtual void OpenBrace(BraceStyle style) + { + switch (style) { + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.BannerStyle: + if (!isAtStartOfLine) + writer.Space(); + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.EndOfLineWithoutSpace: + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.NextLine: + if (!isAtStartOfLine) + NewLine(); + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.NextLineShifted: + NewLine(); + writer.Indent(); + writer.WriteToken(Roles.LBrace, "{"); + NewLine(); + return; + case BraceStyle.NextLineShifted2: + NewLine(); + writer.Indent(); + writer.WriteToken(Roles.LBrace, "{"); + break; + default: + throw new ArgumentOutOfRangeException (); + } + writer.Indent(); + NewLine(); + } + + protected virtual void CloseBrace(BraceStyle style) + { + switch (style) { + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.NextLine: + writer.Unindent(); + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + break; + case BraceStyle.BannerStyle: + case BraceStyle.NextLineShifted: + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + writer.Unindent(); + break; + case BraceStyle.NextLineShifted2: + writer.Unindent(); + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + writer.Unindent(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + #endregion + + #region IsKeyword Test + static readonly HashSet unconditionalKeywords = new HashSet { + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", + "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", + "do", "double", "else", "enum", "event", "explicit", "extern", "false", + "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", + "in", "int", "interface", "internal", "is", "lock", "long", "namespace", + "new", "null", "object", "operator", "out", "override", "params", "private", + "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", + "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", + "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", + "using", "virtual", "void", "volatile", "while" + }; + static readonly HashSet queryKeywords = new HashSet { + "from", "where", "join", "on", "equals", "into", "let", "orderby", + "ascending", "descending", "select", "group", "by" + }; + + /// + /// Determines whether the specified identifier is a keyword in the given context. + /// + public static bool IsKeyword(string identifier, AstNode context) + { + if (unconditionalKeywords.Contains(identifier)) { + return true; + } + foreach (AstNode ancestor in context.Ancestors) { + if (ancestor is QueryExpression && queryKeywords.Contains(identifier)) { + return true; + } + if (identifier == "await") { + // with lambdas/anonymous methods, + if (ancestor is LambdaExpression) { + return ((LambdaExpression)ancestor).IsAsync; + } + if (ancestor is AnonymousMethodExpression) { + return ((AnonymousMethodExpression)ancestor).IsAsync; + } + if (ancestor is EntityDeclaration) { + return (((EntityDeclaration)ancestor).Modifiers & Modifiers.Async) == Modifiers.Async; + } + } + } + return false; + } + #endregion + + #region Write constructs + protected virtual void WriteTypeArguments(IEnumerable typeArguments) + { + if (typeArguments.Any()) { + WriteToken(Roles.LChevron); + WriteCommaSeparatedList(typeArguments); + WriteToken(Roles.RChevron); + } + } + + public virtual void WriteTypeParameters(IEnumerable typeParameters) + { + if (typeParameters.Any()) { + WriteToken(Roles.LChevron); + WriteCommaSeparatedList(typeParameters); + WriteToken(Roles.RChevron); + } + } + + protected virtual void WriteModifiers(IEnumerable modifierTokens) + { + foreach (CSharpModifierToken modifier in modifierTokens) { + modifier.AcceptVisitor(this); + } + } + + protected virtual void WriteQualifiedIdentifier(IEnumerable identifiers) + { + bool first = true; + foreach (Identifier ident in identifiers) { + if (first) { + first = false; + } else { + writer.WriteToken(Roles.Dot, "."); + } + writer.WriteIdentifier(ident); + } + } + + /// + /// Writes an embedded statement. + /// + /// The statement to write. + /// Determines whether a trailing newline should be written following a block. + /// Non-blocks always write a trailing newline. + /// + /// Blocks may or may not write a leading newline depending on StatementBraceStyle. + /// Non-blocks always write a leading newline. + /// + protected virtual void WriteEmbeddedStatement(Statement embeddedStatement, NewLinePlacement nlp = NewLinePlacement.NewLine) + { + if (embeddedStatement.IsNull) { + NewLine(); + return; + } + BlockStatement block = embeddedStatement as BlockStatement; + if (block != null) { + WriteBlock(block, policy.StatementBraceStyle); + if (nlp == NewLinePlacement.SameLine) { + Space(); // if not a trailing newline, then at least a trailing space + } else { + NewLine(); + } + } else { + NewLine(); + writer.Indent(); + embeddedStatement.AcceptVisitor(this); + writer.Unindent(); + } + } + + protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style) + { + if (body.IsNull) { + Semicolon(); + } else { + WriteBlock(body, style); + NewLine(); + } + } + + protected virtual void WriteAttributes(IEnumerable attributes) + { + foreach (AttributeSection attr in attributes) { + attr.AcceptVisitor(this); + } + } + + protected virtual void WritePrivateImplementationType(AstType privateImplementationType) + { + if (!privateImplementationType.IsNull) { + privateImplementationType.AcceptVisitor(this); + WriteToken(Roles.Dot); + } + } + + #endregion + + #region Expressions + public virtual void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + StartNode(anonymousMethodExpression); + if (anonymousMethodExpression.IsAsync) { + WriteKeyword(AnonymousMethodExpression.AsyncModifierRole); + Space(); + } + WriteKeyword(AnonymousMethodExpression.DelegateKeywordRole); + if (anonymousMethodExpression.HasParameterList) { + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(anonymousMethodExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } + WriteBlock(anonymousMethodExpression.Body, policy.AnonymousMethodBraceStyle); + EndNode(anonymousMethodExpression); + } + + public virtual void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + StartNode(undocumentedExpression); + switch (undocumentedExpression.UndocumentedExpressionType) { + case UndocumentedExpressionType.ArgList: + case UndocumentedExpressionType.ArgListAccess: + WriteKeyword(UndocumentedExpression.ArglistKeywordRole); + break; + case UndocumentedExpressionType.MakeRef: + WriteKeyword(UndocumentedExpression.MakerefKeywordRole); + break; + case UndocumentedExpressionType.RefType: + WriteKeyword(UndocumentedExpression.ReftypeKeywordRole); + break; + case UndocumentedExpressionType.RefValue: + WriteKeyword(UndocumentedExpression.RefvalueKeywordRole); + break; + } + if (undocumentedExpression.UndocumentedExpressionType != UndocumentedExpressionType.ArgListAccess) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(undocumentedExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + } + EndNode(undocumentedExpression); + } + + public virtual void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) + { + StartNode(arrayCreateExpression); + WriteKeyword(ArrayCreateExpression.NewKeywordRole); + arrayCreateExpression.Type.AcceptVisitor(this); + if (arrayCreateExpression.Arguments.Count > 0) { + WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); + } + foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { + specifier.AcceptVisitor(this); + } + arrayCreateExpression.Initializer.AcceptVisitor(this); + EndNode(arrayCreateExpression); + } + + public virtual void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + StartNode(arrayInitializerExpression); + // "new List { { 1 } }" and "new List { 1 }" are the same semantically. + // We also use the same AST for both: we always use two nested ArrayInitializerExpressions + // for collection initializers, even if the user did not write nested brackets. + // The output visitor will output nested braces only if they are necessary, + // or if the braces tokens exist in the AST. + bool bracesAreOptional = arrayInitializerExpression.Elements.Count == 1 + && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent) + && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single()); + if (bracesAreOptional && arrayInitializerExpression.LBraceToken.IsNull) { + arrayInitializerExpression.Elements.Single().AcceptVisitor(this); + } else { + PrintInitializerElements(arrayInitializerExpression.Elements); + } + EndNode(arrayInitializerExpression); + } + + protected bool CanBeConfusedWithObjectInitializer(Expression expr) + { + // "int a; new List { a = 1 };" is an object initalizers and invalid, but + // "int a; new List { { a = 1 } };" is a valid collection initializer. + AssignmentExpression ae = expr as AssignmentExpression; + return ae != null && ae.Operator == AssignmentOperatorType.Assign; + } + + protected bool IsObjectOrCollectionInitializer(AstNode node) + { + if (!(node is ArrayInitializerExpression)) { + return false; + } + if (node.Parent is ObjectCreateExpression) { + return node.Role == ObjectCreateExpression.InitializerRole; + } + if (node.Parent is NamedExpression) { + return node.Role == Roles.Expression; + } + return false; + } + + protected virtual void PrintInitializerElements(AstNodeCollection elements) + { + BraceStyle style; + if (policy.ArrayInitializerWrapping == Wrapping.WrapAlways) { + style = BraceStyle.NextLine; + } else { + style = BraceStyle.EndOfLine; + } + OpenBrace(style); + bool isFirst = true; + AstNode last = null; + foreach (AstNode node in elements) { + if (isFirst) { + isFirst = false; + } else { + Comma(node, noSpaceAfterComma: true); + NewLine(); + } + last = node; + node.AcceptVisitor(this); + } + if (last != null) + OptionalComma(last.NextSibling); + NewLine(); + CloseBrace(style); + } + + public virtual void VisitAsExpression(AsExpression asExpression) + { + StartNode(asExpression); + asExpression.Expression.AcceptVisitor(this); + Space(); + WriteKeyword(AsExpression.AsKeywordRole); + Space(); + asExpression.Type.AcceptVisitor(this); + EndNode(asExpression); + } + + public virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + StartNode(assignmentExpression); + assignmentExpression.Left.AcceptVisitor(this); + Space(policy.SpaceAroundAssignment); + WriteToken(AssignmentExpression.GetOperatorRole(assignmentExpression.Operator)); + Space(policy.SpaceAroundAssignment); + assignmentExpression.Right.AcceptVisitor(this); + EndNode(assignmentExpression); + } + + public virtual void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + StartNode(baseReferenceExpression); + WriteKeyword("base", baseReferenceExpression.Role); + EndNode(baseReferenceExpression); + } + + public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + StartNode(binaryOperatorExpression); + binaryOperatorExpression.Left.AcceptVisitor(this); + bool spacePolicy; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + spacePolicy = policy.SpaceAroundBitwiseOperator; + break; + case BinaryOperatorType.ConditionalAnd: + case BinaryOperatorType.ConditionalOr: + spacePolicy = policy.SpaceAroundLogicalOperator; + break; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.LessThan: + spacePolicy = policy.SpaceAroundRelationalOperator; + break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + spacePolicy = policy.SpaceAroundEqualityOperator; + break; + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + spacePolicy = policy.SpaceAroundAdditiveOperator; + break; + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + spacePolicy = policy.SpaceAroundMultiplicativeOperator; + break; + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + spacePolicy = policy.SpaceAroundShiftOperator; + break; + case BinaryOperatorType.NullCoalescing: + spacePolicy = true; + break; + default: + throw new NotSupportedException ("Invalid value for BinaryOperatorType"); + } + Space(spacePolicy); + WriteToken(BinaryOperatorExpression.GetOperatorRole(binaryOperatorExpression.Operator)); + Space(spacePolicy); + binaryOperatorExpression.Right.AcceptVisitor(this); + EndNode(binaryOperatorExpression); + } + + public virtual void VisitCastExpression(CastExpression castExpression) + { + StartNode(castExpression); + LPar(); + Space(policy.SpacesWithinCastParentheses); + castExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinCastParentheses); + RPar(); + Space(policy.SpaceAfterTypecast); + castExpression.Expression.AcceptVisitor(this); + EndNode(castExpression); + } + + public virtual void VisitCheckedExpression(CheckedExpression checkedExpression) + { + StartNode(checkedExpression); + WriteKeyword(CheckedExpression.CheckedKeywordRole); + LPar(); + Space(policy.SpacesWithinCheckedExpressionParantheses); + checkedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinCheckedExpressionParantheses); + RPar(); + EndNode(checkedExpression); + } + + public virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + StartNode(conditionalExpression); + conditionalExpression.Condition.AcceptVisitor(this); + + Space(policy.SpaceBeforeConditionalOperatorCondition); + WriteToken(ConditionalExpression.QuestionMarkRole); + Space(policy.SpaceAfterConditionalOperatorCondition); + + conditionalExpression.TrueExpression.AcceptVisitor(this); + + Space(policy.SpaceBeforeConditionalOperatorSeparator); + WriteToken(ConditionalExpression.ColonRole); + Space(policy.SpaceAfterConditionalOperatorSeparator); + + conditionalExpression.FalseExpression.AcceptVisitor(this); + + EndNode(conditionalExpression); + } + + public virtual void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + StartNode(defaultValueExpression); + + WriteKeyword(DefaultValueExpression.DefaultKeywordRole); + LPar(); + Space(policy.SpacesWithinTypeOfParentheses); + defaultValueExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinTypeOfParentheses); + RPar(); + + EndNode(defaultValueExpression); + } + + public virtual void VisitDirectionExpression(DirectionExpression directionExpression) + { + StartNode(directionExpression); + + switch (directionExpression.FieldDirection) { + case FieldDirection.Out: + WriteKeyword(DirectionExpression.OutKeywordRole); + break; + case FieldDirection.Ref: + WriteKeyword(DirectionExpression.RefKeywordRole); + break; + default: + throw new NotSupportedException ("Invalid value for FieldDirection"); + } + Space(); + directionExpression.Expression.AcceptVisitor(this); + + EndNode(directionExpression); + } + + public virtual void VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + StartNode(identifierExpression); + WriteIdentifier(identifierExpression.IdentifierToken); + WriteTypeArguments(identifierExpression.TypeArguments); + EndNode(identifierExpression); + } + + public virtual void VisitIndexerExpression(IndexerExpression indexerExpression) + { + StartNode(indexerExpression); + indexerExpression.Target.AcceptVisitor(this); + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); + EndNode(indexerExpression); + } + + public virtual void VisitInvocationExpression(InvocationExpression invocationExpression) + { + StartNode(invocationExpression); + invocationExpression.Target.AcceptVisitor(this); + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(invocationExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + EndNode(invocationExpression); + } + + public virtual void VisitIsExpression(IsExpression isExpression) + { + StartNode(isExpression); + isExpression.Expression.AcceptVisitor(this); + Space(); + WriteKeyword(IsExpression.IsKeywordRole); + isExpression.Type.AcceptVisitor(this); + EndNode(isExpression); + } + + public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + StartNode(lambdaExpression); + if (lambdaExpression.IsAsync) { + WriteKeyword(LambdaExpression.AsyncModifierRole); + Space(); + } + if (LambdaNeedsParenthesis(lambdaExpression)) { + WriteCommaSeparatedListInParenthesis(lambdaExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } else { + lambdaExpression.Parameters.Single().AcceptVisitor(this); + } + Space(); + WriteToken(LambdaExpression.ArrowRole); + if (lambdaExpression.Body is BlockStatement) { + WriteBlock((BlockStatement)lambdaExpression.Body, policy.AnonymousMethodBraceStyle); + } else { + Space(); + lambdaExpression.Body.AcceptVisitor(this); + } + EndNode(lambdaExpression); + } + + protected bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) + { + if (lambdaExpression.Parameters.Count != 1) { + return true; + } + var p = lambdaExpression.Parameters.Single(); + return !(p.Type.IsNull && p.ParameterModifier == ParameterModifier.None); + } + + public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + StartNode(memberReferenceExpression); + memberReferenceExpression.Target.AcceptVisitor(this); + WriteToken(Roles.Dot); + WriteIdentifier(memberReferenceExpression.MemberNameToken); + WriteTypeArguments(memberReferenceExpression.TypeArguments); + EndNode(memberReferenceExpression); + } + + public virtual void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + StartNode(namedArgumentExpression); + WriteIdentifier(namedArgumentExpression.NameToken); + WriteToken(Roles.Colon); + Space(); + namedArgumentExpression.Expression.AcceptVisitor(this); + EndNode(namedArgumentExpression); + } + + public virtual void VisitNamedExpression(NamedExpression namedExpression) + { + StartNode(namedExpression); + WriteIdentifier(namedExpression.NameToken); + Space(); + WriteToken(Roles.Assign); + Space(); + namedExpression.Expression.AcceptVisitor(this); + EndNode(namedExpression); + } + + public virtual void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + StartNode(nullReferenceExpression); + writer.WritePrimitiveValue(null); + EndNode(nullReferenceExpression); + } + + public virtual void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + StartNode(objectCreateExpression); + WriteKeyword(ObjectCreateExpression.NewKeywordRole); + objectCreateExpression.Type.AcceptVisitor(this); + bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer.IsNull; + // also use parenthesis if there is an '(' token + if (!objectCreateExpression.LParToken.IsNull) { + useParenthesis = true; + } + if (useParenthesis) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(objectCreateExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + } + objectCreateExpression.Initializer.AcceptVisitor(this); + EndNode(objectCreateExpression); + } + + public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + StartNode(anonymousTypeCreateExpression); + WriteKeyword(AnonymousTypeCreateExpression.NewKeywordRole); + PrintInitializerElements(anonymousTypeCreateExpression.Initializers); + EndNode(anonymousTypeCreateExpression); + } + + public virtual void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + StartNode(parenthesizedExpression); + LPar(); + Space(policy.SpacesWithinParentheses); + parenthesizedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinParentheses); + RPar(); + EndNode(parenthesizedExpression); + } + + public virtual void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + StartNode(pointerReferenceExpression); + pointerReferenceExpression.Target.AcceptVisitor(this); + WriteToken(PointerReferenceExpression.ArrowRole); + WriteIdentifier(pointerReferenceExpression.MemberNameToken); + WriteTypeArguments(pointerReferenceExpression.TypeArguments); + EndNode(pointerReferenceExpression); + } + + #region VisitPrimitiveExpression + public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + StartNode(primitiveExpression); + writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.UnsafeLiteralValue); + EndNode(primitiveExpression); + } + #endregion + + public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + StartNode(sizeOfExpression); + + WriteKeyword(SizeOfExpression.SizeofKeywordRole); + LPar(); + Space(policy.SpacesWithinSizeOfParentheses); + sizeOfExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinSizeOfParentheses); + RPar(); + + EndNode(sizeOfExpression); + } + + public virtual void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + StartNode(stackAllocExpression); + WriteKeyword(StackAllocExpression.StackallocKeywordRole); + stackAllocExpression.Type.AcceptVisitor(this); + WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression }); + EndNode(stackAllocExpression); + } + + public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + StartNode(thisReferenceExpression); + WriteKeyword("this", thisReferenceExpression.Role); + EndNode(thisReferenceExpression); + } + + public virtual void VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + StartNode(typeOfExpression); + + WriteKeyword(TypeOfExpression.TypeofKeywordRole); + LPar(); + Space(policy.SpacesWithinTypeOfParentheses); + typeOfExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinTypeOfParentheses); + RPar(); + + EndNode(typeOfExpression); + } + + public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + StartNode(typeReferenceExpression); + typeReferenceExpression.Type.AcceptVisitor(this); + EndNode(typeReferenceExpression); + } + + public virtual void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + StartNode(unaryOperatorExpression); + UnaryOperatorType opType = unaryOperatorExpression.Operator; + var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); + if (opType == UnaryOperatorType.Await) { + WriteKeyword(opSymbol); + } else if (!(opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement)) { + WriteToken(opSymbol); + } + unaryOperatorExpression.Expression.AcceptVisitor(this); + if (opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement) { + WriteToken(opSymbol); + } + EndNode(unaryOperatorExpression); + } + + public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + StartNode(uncheckedExpression); + WriteKeyword(UncheckedExpression.UncheckedKeywordRole); + LPar(); + Space(policy.SpacesWithinCheckedExpressionParantheses); + uncheckedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinCheckedExpressionParantheses); + RPar(); + EndNode(uncheckedExpression); + } + + #endregion + + #region Query Expressions + public virtual void VisitQueryExpression(QueryExpression queryExpression) + { + StartNode(queryExpression); + bool indent = queryExpression.Parent is QueryClause && !(queryExpression.Parent is QueryContinuationClause); + if (indent) { + writer.Indent(); + NewLine(); + } + bool first = true; + foreach (var clause in queryExpression.Clauses) { + if (first) { + first = false; + } else { + if (!(clause is QueryContinuationClause)) { + NewLine(); + } + } + clause.AcceptVisitor(this); + } + if (indent) { + writer.Unindent(); + } + EndNode(queryExpression); + } + + public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + StartNode(queryContinuationClause); + queryContinuationClause.PrecedingQuery.AcceptVisitor(this); + Space(); + WriteKeyword(QueryContinuationClause.IntoKeywordRole); + Space(); + WriteIdentifier(queryContinuationClause.IdentifierToken); + EndNode(queryContinuationClause); + } + + public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) + { + StartNode(queryFromClause); + WriteKeyword(QueryFromClause.FromKeywordRole); + queryFromClause.Type.AcceptVisitor(this); + Space(); + WriteIdentifier(queryFromClause.IdentifierToken); + Space(); + WriteKeyword(QueryFromClause.InKeywordRole); + Space(); + queryFromClause.Expression.AcceptVisitor(this); + EndNode(queryFromClause); + } + + public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) + { + StartNode(queryLetClause); + WriteKeyword(QueryLetClause.LetKeywordRole); + Space(); + WriteIdentifier(queryLetClause.IdentifierToken); + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + queryLetClause.Expression.AcceptVisitor(this); + EndNode(queryLetClause); + } + + public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + StartNode(queryWhereClause); + WriteKeyword(QueryWhereClause.WhereKeywordRole); + Space(); + queryWhereClause.Condition.AcceptVisitor(this); + EndNode(queryWhereClause); + } + + public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + StartNode(queryJoinClause); + WriteKeyword(QueryJoinClause.JoinKeywordRole); + queryJoinClause.Type.AcceptVisitor(this); + Space(); + WriteIdentifier(queryJoinClause.JoinIdentifierToken); + Space(); + WriteKeyword(QueryJoinClause.InKeywordRole); + Space(); + queryJoinClause.InExpression.AcceptVisitor(this); + Space(); + WriteKeyword(QueryJoinClause.OnKeywordRole); + Space(); + queryJoinClause.OnExpression.AcceptVisitor(this); + Space(); + WriteKeyword(QueryJoinClause.EqualsKeywordRole); + Space(); + queryJoinClause.EqualsExpression.AcceptVisitor(this); + if (queryJoinClause.IsGroupJoin) { + Space(); + WriteKeyword(QueryJoinClause.IntoKeywordRole); + WriteIdentifier(queryJoinClause.IntoIdentifierToken); + } + EndNode(queryJoinClause); + } + + public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + StartNode(queryOrderClause); + WriteKeyword(QueryOrderClause.OrderbyKeywordRole); + Space(); + WriteCommaSeparatedList(queryOrderClause.Orderings); + EndNode(queryOrderClause); + } + + public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) + { + StartNode(queryOrdering); + queryOrdering.Expression.AcceptVisitor(this); + switch (queryOrdering.Direction) { + case QueryOrderingDirection.Ascending: + Space(); + WriteKeyword(QueryOrdering.AscendingKeywordRole); + break; + case QueryOrderingDirection.Descending: + Space(); + WriteKeyword(QueryOrdering.DescendingKeywordRole); + break; + } + EndNode(queryOrdering); + } + + public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + StartNode(querySelectClause); + WriteKeyword(QuerySelectClause.SelectKeywordRole); + Space(); + querySelectClause.Expression.AcceptVisitor(this); + EndNode(querySelectClause); + } + + public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + StartNode(queryGroupClause); + WriteKeyword(QueryGroupClause.GroupKeywordRole); + Space(); + queryGroupClause.Projection.AcceptVisitor(this); + Space(); + WriteKeyword(QueryGroupClause.ByKeywordRole); + Space(); + queryGroupClause.Key.AcceptVisitor(this); + EndNode(queryGroupClause); + } + + #endregion + + #region GeneralScope + public virtual void VisitAttribute(Attribute attribute) + { + StartNode(attribute); + attribute.Type.AcceptVisitor(this); + if (attribute.Arguments.Count != 0 || !attribute.GetChildByRole(Roles.LPar).IsNull) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(attribute.Arguments, policy.SpaceWithinMethodCallParentheses); + } + EndNode(attribute); + } + + public virtual void VisitAttributeSection(AttributeSection attributeSection) + { + StartNode(attributeSection); + WriteToken(Roles.LBracket); + if (!string.IsNullOrEmpty(attributeSection.AttributeTarget)) { + WriteKeyword(attributeSection.AttributeTarget, Roles.Identifier); + WriteToken(Roles.Colon); + Space(); + } + WriteCommaSeparatedList(attributeSection.Attributes); + WriteToken(Roles.RBracket); + if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) { + Space(); + } else { + NewLine(); + } + EndNode(attributeSection); + } + + public virtual void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + StartNode(delegateDeclaration); + WriteAttributes(delegateDeclaration.Attributes); + WriteModifiers(delegateDeclaration.ModifierTokens); + WriteKeyword(Roles.DelegateKeyword); + delegateDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteIdentifier(delegateDeclaration.NameToken); + WriteTypeParameters(delegateDeclaration.TypeParameters); + Space(policy.SpaceBeforeDelegateDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(delegateDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in delegateDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + Semicolon(); + EndNode(delegateDeclaration); + } + + public virtual void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + StartNode(namespaceDeclaration); + WriteKeyword(Roles.NamespaceKeyword); + namespaceDeclaration.NamespaceName.AcceptVisitor (this); + OpenBrace(policy.NamespaceBraceStyle); + foreach (var member in namespaceDeclaration.Members) { + member.AcceptVisitor(this); + MaybeNewLinesAfterUsings(member); + } + CloseBrace(policy.NamespaceBraceStyle); + OptionalSemicolon(namespaceDeclaration.LastChild); + NewLine(); + EndNode(namespaceDeclaration); + } + + public virtual void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + StartNode(typeDeclaration); + WriteAttributes(typeDeclaration.Attributes); + WriteModifiers(typeDeclaration.ModifierTokens); + BraceStyle braceStyle; + switch (typeDeclaration.ClassType) { + case ClassType.Enum: + WriteKeyword(Roles.EnumKeyword); + braceStyle = policy.EnumBraceStyle; + break; + case ClassType.Interface: + WriteKeyword(Roles.InterfaceKeyword); + braceStyle = policy.InterfaceBraceStyle; + break; + case ClassType.Struct: + WriteKeyword(Roles.StructKeyword); + braceStyle = policy.StructBraceStyle; + break; + default: + WriteKeyword(Roles.ClassKeyword); + braceStyle = policy.ClassBraceStyle; + break; + } + WriteIdentifier(typeDeclaration.NameToken); + WriteTypeParameters(typeDeclaration.TypeParameters); + if (typeDeclaration.BaseTypes.Any()) { + Space(); + WriteToken(Roles.Colon); + Space(); + WriteCommaSeparatedList(typeDeclaration.BaseTypes); + } + foreach (Constraint constraint in typeDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + OpenBrace(braceStyle); + if (typeDeclaration.ClassType == ClassType.Enum) { + bool first = true; + AstNode last = null; + foreach (var member in typeDeclaration.Members) { + if (first) { + first = false; + } else { + Comma(member, noSpaceAfterComma: true); + NewLine(); + } + last = member; + member.AcceptVisitor(this); + } + if (last != null) + OptionalComma(last.NextSibling); + NewLine(); + } else { + bool first = true; + foreach (var member in typeDeclaration.Members) { + if (!first) { + for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++) + NewLine(); + } + first = false; + member.AcceptVisitor(this); + } + } + CloseBrace(braceStyle); + OptionalSemicolon(typeDeclaration.LastChild); + NewLine(); + EndNode(typeDeclaration); + } + + public virtual void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) + { + StartNode(usingAliasDeclaration); + WriteKeyword(UsingAliasDeclaration.UsingKeywordRole); + WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole)); + Space(policy.SpaceAroundEqualityOperator); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundEqualityOperator); + usingAliasDeclaration.Import.AcceptVisitor(this); + Semicolon(); + EndNode(usingAliasDeclaration); + } + + public virtual void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + StartNode(usingDeclaration); + WriteKeyword(UsingDeclaration.UsingKeywordRole); + usingDeclaration.Import.AcceptVisitor(this); + Semicolon(); + EndNode(usingDeclaration); + } + + public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + StartNode(externAliasDeclaration); + WriteKeyword(Roles.ExternKeyword); + Space(); + WriteKeyword(Roles.AliasKeyword); + Space(); + WriteIdentifier(externAliasDeclaration.NameToken); + Semicolon(); + EndNode(externAliasDeclaration); + } + + #endregion + + #region Statements + public virtual void VisitBlockStatement(BlockStatement blockStatement) + { + WriteBlock(blockStatement, policy.StatementBraceStyle); + NewLine(); + } + + /// + /// Writes a block statement. + /// Similar to VisitBlockStatement() except that: + /// 1) it allows customizing the BraceStyle + /// 2) it does not write a trailing newline after the '}' (this job is left to the caller) + /// + protected virtual void WriteBlock(BlockStatement blockStatement, BraceStyle style) + { + StartNode(blockStatement); + OpenBrace(style); + foreach (var node in blockStatement.Statements) { + node.AcceptVisitor(this); + } + EndNode(blockStatement); + CloseBrace(style); + } + + public virtual void VisitBreakStatement(BreakStatement breakStatement) + { + StartNode(breakStatement); + WriteKeyword("break", BreakStatement.BreakKeywordRole); + Semicolon(); + EndNode(breakStatement); + } + + public virtual void VisitCheckedStatement(CheckedStatement checkedStatement) + { + StartNode(checkedStatement); + WriteKeyword(CheckedStatement.CheckedKeywordRole); + checkedStatement.Body.AcceptVisitor(this); + EndNode(checkedStatement); + } + + public virtual void VisitContinueStatement(ContinueStatement continueStatement) + { + StartNode(continueStatement); + WriteKeyword("continue", ContinueStatement.ContinueKeywordRole); + Semicolon(); + EndNode(continueStatement); + } + + public virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + StartNode(doWhileStatement); + WriteKeyword(DoWhileStatement.DoKeywordRole); + WriteEmbeddedStatement(doWhileStatement.EmbeddedStatement, policy.WhileNewLinePlacement); + WriteKeyword(DoWhileStatement.WhileKeywordRole); + Space(policy.SpaceBeforeWhileParentheses); + LPar(); + Space(policy.SpacesWithinWhileParentheses); + doWhileStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinWhileParentheses); + RPar(); + Semicolon(); + EndNode(doWhileStatement); + } + + public virtual void VisitEmptyStatement(EmptyStatement emptyStatement) + { + StartNode(emptyStatement); + Semicolon(); + EndNode(emptyStatement); + } + + public virtual void VisitExpressionStatement(ExpressionStatement expressionStatement) + { + StartNode(expressionStatement); + expressionStatement.Expression.AcceptVisitor(this); + Semicolon(); + EndNode(expressionStatement); + } + + public virtual void VisitFixedStatement(FixedStatement fixedStatement) + { + StartNode(fixedStatement); + WriteKeyword(FixedStatement.FixedKeywordRole); + Space(policy.SpaceBeforeUsingParentheses); + LPar(); + Space(policy.SpacesWithinUsingParentheses); + fixedStatement.Type.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fixedStatement.Variables); + Space(policy.SpacesWithinUsingParentheses); + RPar(); + WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); + EndNode(fixedStatement); + } + + public virtual void VisitForeachStatement(ForeachStatement foreachStatement) + { + StartNode(foreachStatement); + WriteKeyword(ForeachStatement.ForeachKeywordRole); + Space(policy.SpaceBeforeForeachParentheses); + LPar(); + Space(policy.SpacesWithinForeachParentheses); + foreachStatement.VariableType.AcceptVisitor(this); + Space(); + WriteIdentifier(foreachStatement.VariableNameToken); + WriteKeyword(ForeachStatement.InKeywordRole); + Space(); + foreachStatement.InExpression.AcceptVisitor(this); + Space(policy.SpacesWithinForeachParentheses); + RPar(); + WriteEmbeddedStatement(foreachStatement.EmbeddedStatement); + EndNode(foreachStatement); + } + + public virtual void VisitForStatement(ForStatement forStatement) + { + StartNode(forStatement); + WriteKeyword(ForStatement.ForKeywordRole); + Space(policy.SpaceBeforeForParentheses); + LPar(); + Space(policy.SpacesWithinForParentheses); + + WriteCommaSeparatedList(forStatement.Initializers); + Space(policy.SpaceBeforeForSemicolon); + WriteToken(Roles.Semicolon); + Space(policy.SpaceAfterForSemicolon); + + forStatement.Condition.AcceptVisitor(this); + Space(policy.SpaceBeforeForSemicolon); + WriteToken(Roles.Semicolon); + if (forStatement.Iterators.Any()) { + Space(policy.SpaceAfterForSemicolon); + WriteCommaSeparatedList(forStatement.Iterators); + } + + Space(policy.SpacesWithinForParentheses); + RPar(); + WriteEmbeddedStatement(forStatement.EmbeddedStatement); + EndNode(forStatement); + } + + public virtual void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + StartNode(gotoCaseStatement); + WriteKeyword(GotoCaseStatement.GotoKeywordRole); + WriteKeyword(GotoCaseStatement.CaseKeywordRole); + Space(); + gotoCaseStatement.LabelExpression.AcceptVisitor(this); + Semicolon(); + EndNode(gotoCaseStatement); + } + + public virtual void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + StartNode(gotoDefaultStatement); + WriteKeyword(GotoDefaultStatement.GotoKeywordRole); + WriteKeyword(GotoDefaultStatement.DefaultKeywordRole); + Semicolon(); + EndNode(gotoDefaultStatement); + } + + public virtual void VisitGotoStatement(GotoStatement gotoStatement) + { + StartNode(gotoStatement); + WriteKeyword(GotoStatement.GotoKeywordRole); + WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier)); + Semicolon(); + EndNode(gotoStatement); + } + + public virtual void VisitIfElseStatement(IfElseStatement ifElseStatement) + { + StartNode(ifElseStatement); + WriteKeyword(IfElseStatement.IfKeywordRole); + Space(policy.SpaceBeforeIfParentheses); + LPar(); + Space(policy.SpacesWithinIfParentheses); + ifElseStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinIfParentheses); + RPar(); + + if (ifElseStatement.FalseStatement.IsNull) { + WriteEmbeddedStatement(ifElseStatement.TrueStatement); + } else { + WriteEmbeddedStatement(ifElseStatement.TrueStatement, policy.ElseNewLinePlacement); + WriteKeyword(IfElseStatement.ElseKeywordRole); + if (ifElseStatement.FalseStatement is IfElseStatement) { + // don't put newline between 'else' and 'if' + ifElseStatement.FalseStatement.AcceptVisitor(this); + } else { + WriteEmbeddedStatement(ifElseStatement.FalseStatement); + } + } + EndNode(ifElseStatement); + } + + public virtual void VisitLabelStatement(LabelStatement labelStatement) + { + StartNode(labelStatement); + WriteIdentifier(labelStatement.GetChildByRole(Roles.Identifier)); + WriteToken(Roles.Colon); + bool foundLabelledStatement = false; + for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) { + if (tmp.Role == labelStatement.Role) { + foundLabelledStatement = true; + } + } + if (!foundLabelledStatement) { + // introduce an EmptyStatement so that the output becomes syntactically valid + WriteToken(Roles.Semicolon); + } + NewLine(); + EndNode(labelStatement); + } + + public virtual void VisitLockStatement(LockStatement lockStatement) + { + StartNode(lockStatement); + WriteKeyword(LockStatement.LockKeywordRole); + Space(policy.SpaceBeforeLockParentheses); + LPar(); + Space(policy.SpacesWithinLockParentheses); + lockStatement.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinLockParentheses); + RPar(); + WriteEmbeddedStatement(lockStatement.EmbeddedStatement); + EndNode(lockStatement); + } + + public virtual void VisitReturnStatement(ReturnStatement returnStatement) + { + StartNode(returnStatement); + WriteKeyword(ReturnStatement.ReturnKeywordRole); + if (!returnStatement.Expression.IsNull) { + Space(); + returnStatement.Expression.AcceptVisitor(this); + } + Semicolon(); + EndNode(returnStatement); + } + + public virtual void VisitSwitchStatement(SwitchStatement switchStatement) + { + StartNode(switchStatement); + WriteKeyword(SwitchStatement.SwitchKeywordRole); + Space(policy.SpaceBeforeSwitchParentheses); + LPar(); + Space(policy.SpacesWithinSwitchParentheses); + switchStatement.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinSwitchParentheses); + RPar(); + OpenBrace(policy.StatementBraceStyle); + if (!policy.IndentSwitchBody) { + writer.Unindent(); + } + + foreach (var section in switchStatement.SwitchSections) { + section.AcceptVisitor(this); + } + + if (!policy.IndentSwitchBody) { + writer.Indent(); + } + CloseBrace(policy.StatementBraceStyle); + NewLine(); + EndNode(switchStatement); + } + + public virtual void VisitSwitchSection(SwitchSection switchSection) + { + StartNode(switchSection); + bool first = true; + foreach (var label in switchSection.CaseLabels) { + if (!first) { + NewLine(); + } + label.AcceptVisitor(this); + first = false; + } + bool isBlock = switchSection.Statements.Count == 1 && switchSection.Statements.Single() is BlockStatement; + if (policy.IndentCaseBody && !isBlock) { + writer.Indent(); + } + + if (!isBlock) + NewLine(); + + foreach (var statement in switchSection.Statements) { + statement.AcceptVisitor(this); + } + + if (policy.IndentCaseBody && !isBlock) { + writer.Unindent(); + } + + EndNode(switchSection); + } + + public virtual void VisitCaseLabel(CaseLabel caseLabel) + { + StartNode(caseLabel); + if (caseLabel.Expression.IsNull) { + WriteKeyword(CaseLabel.DefaultKeywordRole); + } else { + WriteKeyword(CaseLabel.CaseKeywordRole); + Space(); + caseLabel.Expression.AcceptVisitor(this); + } + WriteToken(Roles.Colon); + EndNode(caseLabel); + } + + public virtual void VisitThrowStatement(ThrowStatement throwStatement) + { + StartNode(throwStatement); + WriteKeyword(ThrowStatement.ThrowKeywordRole); + if (!throwStatement.Expression.IsNull) { + Space(); + throwStatement.Expression.AcceptVisitor(this); + } + Semicolon(); + EndNode(throwStatement); + } + + public virtual void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + StartNode(tryCatchStatement); + WriteKeyword(TryCatchStatement.TryKeywordRole); + WriteBlock(tryCatchStatement.TryBlock, policy.StatementBraceStyle); + foreach (var catchClause in tryCatchStatement.CatchClauses) { + if (policy.CatchNewLinePlacement == NewLinePlacement.SameLine) + Space(); + else + NewLine(); + catchClause.AcceptVisitor(this); + } + if (!tryCatchStatement.FinallyBlock.IsNull) { + if (policy.FinallyNewLinePlacement == NewLinePlacement.SameLine) + Space(); + else + NewLine(); + WriteKeyword(TryCatchStatement.FinallyKeywordRole); + WriteBlock(tryCatchStatement.FinallyBlock, policy.StatementBraceStyle); + } + NewLine(); + EndNode(tryCatchStatement); + } + + public virtual void VisitCatchClause(CatchClause catchClause) + { + StartNode(catchClause); + WriteKeyword(CatchClause.CatchKeywordRole); + if (!catchClause.Type.IsNull) { + Space(policy.SpaceBeforeCatchParentheses); + LPar(); + Space(policy.SpacesWithinCatchParentheses); + catchClause.Type.AcceptVisitor(this); + if (!string.IsNullOrEmpty(catchClause.VariableName)) { + Space(); + WriteIdentifier(catchClause.VariableNameToken); + } + Space(policy.SpacesWithinCatchParentheses); + RPar(); + } + if (!catchClause.Condition.IsNull) { + Space(); + WriteKeyword(CatchClause.WhenKeywordRole); + Space(policy.SpaceBeforeIfParentheses); + LPar(); + Space(policy.SpacesWithinIfParentheses); + catchClause.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinIfParentheses); + RPar(); + } + WriteBlock(catchClause.Body, policy.StatementBraceStyle); + EndNode(catchClause); + } + + public virtual void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + StartNode(uncheckedStatement); + WriteKeyword(UncheckedStatement.UncheckedKeywordRole); + uncheckedStatement.Body.AcceptVisitor(this); + EndNode(uncheckedStatement); + } + + public virtual void VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + StartNode(unsafeStatement); + WriteKeyword(UnsafeStatement.UnsafeKeywordRole); + unsafeStatement.Body.AcceptVisitor(this); + EndNode(unsafeStatement); + } + + public virtual void VisitUsingStatement(UsingStatement usingStatement) + { + StartNode(usingStatement); + WriteKeyword(UsingStatement.UsingKeywordRole); + Space(policy.SpaceBeforeUsingParentheses); + LPar(); + Space(policy.SpacesWithinUsingParentheses); + + usingStatement.ResourceAcquisition.AcceptVisitor(this); + + Space(policy.SpacesWithinUsingParentheses); + RPar(); + + WriteEmbeddedStatement(usingStatement.EmbeddedStatement); + + EndNode(usingStatement); + } + + public virtual void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + StartNode(variableDeclarationStatement); + WriteModifiers(variableDeclarationStatement.GetChildrenByRole(VariableDeclarationStatement.ModifierRole)); + variableDeclarationStatement.Type.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(variableDeclarationStatement.Variables); + Semicolon(); + EndNode(variableDeclarationStatement); + } + + public virtual void VisitWhileStatement(WhileStatement whileStatement) + { + StartNode(whileStatement); + WriteKeyword(WhileStatement.WhileKeywordRole); + Space(policy.SpaceBeforeWhileParentheses); + LPar(); + Space(policy.SpacesWithinWhileParentheses); + whileStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinWhileParentheses); + RPar(); + WriteEmbeddedStatement(whileStatement.EmbeddedStatement); + EndNode(whileStatement); + } + + public virtual void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + StartNode(yieldBreakStatement); + WriteKeyword(YieldBreakStatement.YieldKeywordRole); + WriteKeyword(YieldBreakStatement.BreakKeywordRole); + Semicolon(); + EndNode(yieldBreakStatement); + } + + public virtual void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) + { + StartNode(yieldReturnStatement); + WriteKeyword(YieldReturnStatement.YieldKeywordRole); + WriteKeyword(YieldReturnStatement.ReturnKeywordRole); + Space(); + yieldReturnStatement.Expression.AcceptVisitor(this); + Semicolon(); + EndNode(yieldReturnStatement); + } + + #endregion + + #region TypeMembers + public virtual void VisitAccessor(Accessor accessor) + { + StartNode(accessor); + WriteAttributes(accessor.Attributes); + WriteModifiers(accessor.ModifierTokens); + BraceStyle style = policy.StatementBraceStyle; + if (accessor.Role == PropertyDeclaration.GetterRole) { + WriteKeyword("get", PropertyDeclaration.GetKeywordRole); + style = policy.PropertyGetBraceStyle; + } else if (accessor.Role == PropertyDeclaration.SetterRole) { + WriteKeyword("set", PropertyDeclaration.SetKeywordRole); + style = policy.PropertySetBraceStyle; + } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { + WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); + style = policy.EventAddBraceStyle; + } else if (accessor.Role == CustomEventDeclaration.RemoveAccessorRole) { + WriteKeyword("remove", CustomEventDeclaration.RemoveKeywordRole); + style = policy.EventRemoveBraceStyle; + } + WriteMethodBody(accessor.Body, style); + EndNode(accessor); + } + + public virtual void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + StartNode(constructorDeclaration); + WriteAttributes(constructorDeclaration.Attributes); + WriteModifiers(constructorDeclaration.ModifierTokens); + TypeDeclaration type = constructorDeclaration.Parent as TypeDeclaration; + if (type != null && type.Name != constructorDeclaration.Name) + WriteIdentifier((Identifier)type.NameToken.Clone()); + else + WriteIdentifier(constructorDeclaration.NameToken); + Space(policy.SpaceBeforeConstructorDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + if (!constructorDeclaration.Initializer.IsNull) { + Space(); + constructorDeclaration.Initializer.AcceptVisitor(this); + } + WriteMethodBody(constructorDeclaration.Body, policy.ConstructorBraceStyle); + EndNode(constructorDeclaration); + } + + public virtual void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + StartNode(constructorInitializer); + WriteToken(Roles.Colon); + Space(); + if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.This) { + WriteKeyword(ConstructorInitializer.ThisKeywordRole); + } else { + WriteKeyword(ConstructorInitializer.BaseKeywordRole); + } + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(constructorInitializer.Arguments, policy.SpaceWithinMethodCallParentheses); + EndNode(constructorInitializer); + } + + public virtual void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + StartNode(destructorDeclaration); + WriteAttributes(destructorDeclaration.Attributes); + WriteModifiers(destructorDeclaration.ModifierTokens); + if (destructorDeclaration.ModifierTokens.Any()) { + Space(); + } + WriteToken(DestructorDeclaration.TildeRole); + TypeDeclaration type = destructorDeclaration.Parent as TypeDeclaration; + if (type != null && type.Name != destructorDeclaration.Name) + WriteIdentifier((Identifier)type.NameToken.Clone()); + else + WriteIdentifier(destructorDeclaration.NameToken); + Space(policy.SpaceBeforeConstructorDeclarationParentheses); + LPar(); + RPar(); + WriteMethodBody(destructorDeclaration.Body, policy.DestructorBraceStyle); + EndNode(destructorDeclaration); + } + + public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + StartNode(enumMemberDeclaration); + WriteAttributes(enumMemberDeclaration.Attributes); + WriteModifiers(enumMemberDeclaration.ModifierTokens); + WriteIdentifier(enumMemberDeclaration.NameToken); + if (!enumMemberDeclaration.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + enumMemberDeclaration.Initializer.AcceptVisitor(this); + } + EndNode(enumMemberDeclaration); + } + + public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + StartNode(eventDeclaration); + WriteAttributes(eventDeclaration.Attributes); + WriteModifiers(eventDeclaration.ModifierTokens); + WriteKeyword(EventDeclaration.EventKeywordRole); + eventDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(eventDeclaration.Variables); + Semicolon(); + EndNode(eventDeclaration); + } + + public virtual void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) + { + StartNode(customEventDeclaration); + WriteAttributes(customEventDeclaration.Attributes); + WriteModifiers(customEventDeclaration.ModifierTokens); + WriteKeyword(CustomEventDeclaration.EventKeywordRole); + customEventDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); + WriteIdentifier(customEventDeclaration.NameToken); + OpenBrace(policy.EventBraceStyle); + // output add/remove in their original order + foreach (AstNode node in customEventDeclaration.Children) { + if (node.Role == CustomEventDeclaration.AddAccessorRole || node.Role == CustomEventDeclaration.RemoveAccessorRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.EventBraceStyle); + NewLine(); + EndNode(customEventDeclaration); + } + + public virtual void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + StartNode(fieldDeclaration); + WriteAttributes(fieldDeclaration.Attributes); + WriteModifiers(fieldDeclaration.ModifierTokens); + fieldDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fieldDeclaration.Variables); + Semicolon(); + EndNode(fieldDeclaration); + } + + public virtual void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + StartNode(fixedFieldDeclaration); + WriteAttributes(fixedFieldDeclaration.Attributes); + WriteModifiers(fixedFieldDeclaration.ModifierTokens); + WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); + Space(); + fixedFieldDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fixedFieldDeclaration.Variables); + Semicolon(); + EndNode(fixedFieldDeclaration); + } + + public virtual void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + StartNode(fixedVariableInitializer); + WriteIdentifier(fixedVariableInitializer.NameToken); + if (!fixedVariableInitializer.CountExpression.IsNull) { + WriteToken(Roles.LBracket); + Space(policy.SpacesWithinBrackets); + fixedVariableInitializer.CountExpression.AcceptVisitor(this); + Space(policy.SpacesWithinBrackets); + WriteToken(Roles.RBracket); + } + EndNode(fixedVariableInitializer); + } + + public virtual void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + StartNode(indexerDeclaration); + WriteAttributes(indexerDeclaration.Attributes); + WriteModifiers(indexerDeclaration.ModifierTokens); + indexerDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); + WriteKeyword(IndexerDeclaration.ThisKeywordRole); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in indexerDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.PropertyBraceStyle); + NewLine(); + EndNode(indexerDeclaration); + } + + public virtual void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + StartNode(methodDeclaration); + WriteAttributes(methodDeclaration.Attributes); + WriteModifiers(methodDeclaration.ModifierTokens); + methodDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); + WriteIdentifier(methodDeclaration.NameToken); + WriteTypeParameters(methodDeclaration.TypeParameters); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(methodDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in methodDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + WriteMethodBody(methodDeclaration.Body, policy.MethodBraceStyle); + EndNode(methodDeclaration); + } + + public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + StartNode(operatorDeclaration); + WriteAttributes(operatorDeclaration.Attributes); + WriteModifiers(operatorDeclaration.ModifierTokens); + if (operatorDeclaration.OperatorType == OperatorType.Explicit) { + WriteKeyword(OperatorDeclaration.ExplicitRole); + } else if (operatorDeclaration.OperatorType == OperatorType.Implicit) { + WriteKeyword(OperatorDeclaration.ImplicitRole); + } else { + operatorDeclaration.ReturnType.AcceptVisitor(this); + } + WriteKeyword(OperatorDeclaration.OperatorKeywordRole); + Space(); + if (operatorDeclaration.OperatorType == OperatorType.Explicit + || operatorDeclaration.OperatorType == OperatorType.Implicit) { + operatorDeclaration.ReturnType.AcceptVisitor(this); + } else { + WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType)); + } + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(operatorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + WriteMethodBody(operatorDeclaration.Body, policy.MethodBraceStyle); + EndNode(operatorDeclaration); + } + + public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + StartNode(parameterDeclaration); + WriteAttributes(parameterDeclaration.Attributes); + switch (parameterDeclaration.ParameterModifier) { + case ParameterModifier.Ref: + WriteKeyword(ParameterDeclaration.RefModifierRole); + break; + case ParameterModifier.Out: + WriteKeyword(ParameterDeclaration.OutModifierRole); + break; + case ParameterModifier.Params: + WriteKeyword(ParameterDeclaration.ParamsModifierRole); + break; + case ParameterModifier.This: + WriteKeyword(ParameterDeclaration.ThisModifierRole); + break; + } + parameterDeclaration.Type.AcceptVisitor(this); + if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) { + Space(); + } + if (!string.IsNullOrEmpty(parameterDeclaration.Name)) { + WriteIdentifier(parameterDeclaration.NameToken); + } + if (!parameterDeclaration.DefaultExpression.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + parameterDeclaration.DefaultExpression.AcceptVisitor(this); + } + EndNode(parameterDeclaration); + } + + public virtual void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + StartNode(propertyDeclaration); + WriteAttributes(propertyDeclaration.Attributes); + WriteModifiers(propertyDeclaration.ModifierTokens); + propertyDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); + WriteIdentifier(propertyDeclaration.NameToken); + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in propertyDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.PropertyBraceStyle); + NewLine(); + EndNode(propertyDeclaration); + } + + #endregion + + #region Other nodes + public virtual void VisitVariableInitializer(VariableInitializer variableInitializer) + { + StartNode(variableInitializer); + WriteIdentifier(variableInitializer.NameToken); + if (!variableInitializer.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + variableInitializer.Initializer.AcceptVisitor(this); + } + EndNode(variableInitializer); + } + + void MaybeNewLinesAfterUsings(AstNode node) + { + var nextSibling = node.NextSibling; + while (nextSibling is WhitespaceNode || nextSibling is NewLineNode) + nextSibling = nextSibling.NextSibling; + + if ((node is UsingDeclaration || node is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { + for (int i = 0; i < policy.MinimumBlankLinesAfterUsings; i++) + NewLine(); + } + } + + public virtual void VisitSyntaxTree(SyntaxTree syntaxTree) + { + // don't do node tracking as we visit all children directly + foreach (AstNode node in syntaxTree.Children) { + node.AcceptVisitor(this); + MaybeNewLinesAfterUsings(node); + } + } + + public virtual void VisitSimpleType(SimpleType simpleType) + { + StartNode(simpleType); + WriteIdentifier(simpleType.IdentifierToken); + WriteTypeArguments(simpleType.TypeArguments); + EndNode(simpleType); + } + + public virtual void VisitMemberType(MemberType memberType) + { + StartNode(memberType); + memberType.Target.AcceptVisitor(this); + if (memberType.IsDoubleColon) { + WriteToken(Roles.DoubleColon); + } else { + WriteToken(Roles.Dot); + } + WriteIdentifier(memberType.MemberNameToken); + WriteTypeArguments(memberType.TypeArguments); + EndNode(memberType); + } + + public virtual void VisitComposedType(ComposedType composedType) + { + StartNode(composedType); + if (composedType.HasRefSpecifier) { + WriteKeyword(ComposedType.RefRole); + } + composedType.BaseType.AcceptVisitor(this); + if (composedType.HasNullableSpecifier) { + WriteToken(ComposedType.NullableRole); + } + for (int i = 0; i < composedType.PointerRank; i++) { + WriteToken(ComposedType.PointerRole); + } + foreach (var node in composedType.ArraySpecifiers) { + node.AcceptVisitor(this); + } + EndNode(composedType); + } + + public virtual void VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + StartNode(arraySpecifier); + WriteToken(Roles.LBracket); + foreach (var comma in arraySpecifier.GetChildrenByRole(Roles.Comma)) { + writer.WriteToken(Roles.Comma, ","); + } + WriteToken(Roles.RBracket); + EndNode(arraySpecifier); + } + + public virtual void VisitPrimitiveType(PrimitiveType primitiveType) + { + StartNode(primitiveType); + writer.WritePrimitiveType(primitiveType.Keyword); + EndNode(primitiveType); + } + + public virtual void VisitComment(Comment comment) + { + writer.StartNode(comment); + writer.WriteComment(comment.CommentType, comment.Content); + writer.EndNode(comment); + } + + public virtual void VisitNewLine(NewLineNode newLineNode) + { +// formatter.StartNode(newLineNode); +// formatter.NewLine(); +// formatter.EndNode(newLineNode); + } + + public virtual void VisitWhitespace(WhitespaceNode whitespaceNode) + { + // unused + } + + public virtual void VisitText(TextNode textNode) + { + // unused + } + + public virtual void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + writer.StartNode(preProcessorDirective); + writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument); + writer.EndNode(preProcessorDirective); + } + + public virtual void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + StartNode(typeParameterDeclaration); + WriteAttributes(typeParameterDeclaration.Attributes); + switch (typeParameterDeclaration.Variance) { + case VarianceModifier.Invariant: + break; + case VarianceModifier.Covariant: + WriteKeyword(TypeParameterDeclaration.OutVarianceKeywordRole); + break; + case VarianceModifier.Contravariant: + WriteKeyword(TypeParameterDeclaration.InVarianceKeywordRole); + break; + default: + throw new NotSupportedException ("Invalid value for VarianceModifier"); + } + WriteIdentifier(typeParameterDeclaration.NameToken); + EndNode(typeParameterDeclaration); + } + + public virtual void VisitConstraint(Constraint constraint) + { + StartNode(constraint); + Space(); + WriteKeyword(Roles.WhereKeyword); + constraint.TypeParameter.AcceptVisitor(this); + Space(); + WriteToken(Roles.Colon); + Space(); + WriteCommaSeparatedList(constraint.BaseTypes); + EndNode(constraint); + } + + public virtual void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) + { + CSharpModifierToken mod = cSharpTokenNode as CSharpModifierToken; + if (mod != null) { + // ITokenWriter assumes that each node processed between a + // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. + WriteKeyword(CSharpModifierToken.GetModifierName(mod.Modifier), cSharpTokenNode.Role); + } else { + throw new NotSupportedException ("Should never visit individual tokens"); + } + } + + public virtual void VisitIdentifier(Identifier identifier) + { + // Do not call StartNode and EndNode for Identifier, because they are handled by the ITokenWriter. + // ITokenWriter assumes that each node processed between a + // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. + WriteIdentifier(identifier); + } + + void IAstVisitor.VisitNullNode(AstNode nullNode) + { + } + + void IAstVisitor.VisitErrorNode(AstNode errorNode) + { + StartNode(errorNode); + EndNode(errorNode); + } + #endregion + + #region Pattern Nodes + public virtual void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + StartNode(placeholder); + VisitNodeInPattern(pattern); + EndNode(placeholder); + } + + void VisitAnyNode(AnyNode anyNode) + { + if (!string.IsNullOrEmpty(anyNode.GroupName)) { + WriteIdentifier(anyNode.GroupName); + WriteToken(Roles.Colon); + } + } + + void VisitBackreference(Backreference backreference) + { + WriteKeyword("backreference"); + LPar(); + WriteIdentifier(backreference.ReferencedGroupName); + RPar(); + } + + void VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference) + { + WriteKeyword("identifierBackreference"); + LPar(); + WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); + RPar(); + } + + void VisitChoice(Choice choice) + { + WriteKeyword("choice"); + Space(); + LPar(); + NewLine(); + writer.Indent(); + foreach (INode alternative in choice) { + VisitNodeInPattern(alternative); + if (alternative != choice.Last()) { + WriteToken(Roles.Comma); + } + NewLine(); + } + writer.Unindent(); + RPar(); + } + + void VisitNamedNode(NamedNode namedNode) + { + if (!string.IsNullOrEmpty(namedNode.GroupName)) { + WriteIdentifier(namedNode.GroupName); + WriteToken(Roles.Colon); + } + VisitNodeInPattern(namedNode.ChildNode); + } + + void VisitRepeat(Repeat repeat) + { + WriteKeyword("repeat"); + LPar(); + if (repeat.MinCount != 0 || repeat.MaxCount != int.MaxValue) { + WriteIdentifier(repeat.MinCount.ToString()); + WriteToken(Roles.Comma); + WriteIdentifier(repeat.MaxCount.ToString()); + WriteToken(Roles.Comma); + } + VisitNodeInPattern(repeat.ChildNode); + RPar(); + } + + void VisitOptionalNode(OptionalNode optionalNode) + { + WriteKeyword("optional"); + LPar(); + VisitNodeInPattern(optionalNode.ChildNode); + RPar(); + } + + void VisitNodeInPattern(INode childNode) + { + if (childNode is AstNode) { + ((AstNode)childNode).AcceptVisitor(this); + } else if (childNode is IdentifierExpressionBackreference) { + VisitIdentifierExpressionBackreference((IdentifierExpressionBackreference)childNode); + } else if (childNode is Choice) { + VisitChoice((Choice)childNode); + } else if (childNode is AnyNode) { + VisitAnyNode((AnyNode)childNode); + } else if (childNode is Backreference) { + VisitBackreference((Backreference)childNode); + } else if (childNode is NamedNode) { + VisitNamedNode((NamedNode)childNode); + } else if (childNode is OptionalNode) { + VisitOptionalNode((OptionalNode)childNode); + } else if (childNode is Repeat) { + VisitRepeat((Repeat)childNode); + } else { + TextWriterTokenWriter.PrintPrimitiveValue(childNode); + } + } + #endregion + + #region Documentation Reference + public virtual void VisitDocumentationReference(DocumentationReference documentationReference) + { + StartNode(documentationReference); + if (!documentationReference.DeclaringType.IsNull) { + documentationReference.DeclaringType.AcceptVisitor(this); + if (documentationReference.SymbolKind != SymbolKind.TypeDefinition) { + WriteToken(Roles.Dot); + } + } + switch (documentationReference.SymbolKind) { + case SymbolKind.TypeDefinition: + // we already printed the DeclaringType + break; + case SymbolKind.Indexer: + WriteKeyword(IndexerDeclaration.ThisKeywordRole); + break; + case SymbolKind.Operator: + var opType = documentationReference.OperatorType; + if (opType == OperatorType.Explicit) { + WriteKeyword(OperatorDeclaration.ExplicitRole); + } else if (opType == OperatorType.Implicit) { + WriteKeyword(OperatorDeclaration.ImplicitRole); + } + WriteKeyword(OperatorDeclaration.OperatorKeywordRole); + Space(); + if (opType == OperatorType.Explicit || opType == OperatorType.Implicit) { + documentationReference.ConversionOperatorReturnType.AcceptVisitor(this); + } else { + WriteToken(OperatorDeclaration.GetToken(opType), OperatorDeclaration.GetRole(opType)); + } + break; + default: + WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier)); + break; + } + WriteTypeArguments(documentationReference.TypeArguments); + if (documentationReference.HasParameterList) { + Space(policy.SpaceBeforeMethodDeclarationParentheses); + if (documentationReference.SymbolKind == SymbolKind.Indexer) { + WriteCommaSeparatedListInBrackets(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } else { + WriteCommaSeparatedListInParenthesis(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } + } + EndNode(documentationReference); + } + #endregion + + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string text) + { + return TextWriterTokenWriter.ConvertString(text); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs new file mode 100644 index 000000000..ff32c1f8c --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs @@ -0,0 +1,446 @@ +// +// FormattingOptionsFactory.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The formatting options factory creates pre defined formatting option styles. + /// + public static class FormattingOptionsFactory + { + /// + /// Creates empty CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateEmpty() + { + return new CSharpFormattingOptions(); + } + + /// + /// Creates mono indent style CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateMono() + { + return new CSharpFormattingOptions { + IndentNamespaceBody = true, + IndentClassBody = true, + IndentInterfaceBody = true, + IndentStructBody = true, + IndentEnumBody = true, + IndentMethodBody = true, + IndentPropertyBody = true, + IndentEventBody = true, + IndentBlocks = true, + IndentSwitchBody = false, + IndentCaseBody = true, + IndentBreakStatements = true, + IndentPreprocessorDirectives = true, + IndentBlocksInsideExpressions = false, + NamespaceBraceStyle = BraceStyle.NextLine, + ClassBraceStyle = BraceStyle.NextLine, + InterfaceBraceStyle = BraceStyle.NextLine, + StructBraceStyle = BraceStyle.NextLine, + EnumBraceStyle = BraceStyle.NextLine, + MethodBraceStyle = BraceStyle.NextLine, + ConstructorBraceStyle = BraceStyle.NextLine, + DestructorBraceStyle = BraceStyle.NextLine, + AnonymousMethodBraceStyle = BraceStyle.EndOfLine, + + PropertyBraceStyle = BraceStyle.EndOfLine, + PropertyGetBraceStyle = BraceStyle.EndOfLine, + PropertySetBraceStyle = BraceStyle.EndOfLine, + SimpleGetBlockFormatting = PropertyFormatting.AllowOneLine, + SimpleSetBlockFormatting = PropertyFormatting.AllowOneLine, + + EventBraceStyle = BraceStyle.EndOfLine, + EventAddBraceStyle = BraceStyle.EndOfLine, + EventRemoveBraceStyle = BraceStyle.EndOfLine, + AllowEventAddBlockInline = true, + AllowEventRemoveBlockInline = true, + StatementBraceStyle = BraceStyle.EndOfLine, + + ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, + CatchNewLinePlacement = NewLinePlacement.SameLine, + FinallyNewLinePlacement = NewLinePlacement.SameLine, + WhileNewLinePlacement = NewLinePlacement.SameLine, + ArrayInitializerWrapping = Wrapping.WrapIfTooLong, + ArrayInitializerBraceStyle = BraceStyle.EndOfLine, + AllowOneLinedArrayInitialziers = true, + + SpaceBeforeMethodCallParentheses = true, + SpaceBeforeMethodDeclarationParentheses = true, + SpaceBeforeConstructorDeclarationParentheses = true, + SpaceBeforeDelegateDeclarationParentheses = true, + SpaceAfterMethodCallParameterComma = true, + SpaceAfterConstructorDeclarationParameterComma = true, + + SpaceBeforeNewParentheses = true, + SpacesWithinNewParentheses = false, + SpacesBetweenEmptyNewParentheses = false, + SpaceBeforeNewParameterComma = false, + SpaceAfterNewParameterComma = true, + + SpaceBeforeIfParentheses = true, + SpaceBeforeWhileParentheses = true, + SpaceBeforeForParentheses = true, + SpaceBeforeForeachParentheses = true, + SpaceBeforeCatchParentheses = true, + SpaceBeforeSwitchParentheses = true, + SpaceBeforeLockParentheses = true, + SpaceBeforeUsingParentheses = true, + SpaceAroundAssignment = true, + SpaceAroundLogicalOperator = true, + SpaceAroundEqualityOperator = true, + SpaceAroundRelationalOperator = true, + SpaceAroundBitwiseOperator = true, + SpaceAroundAdditiveOperator = true, + SpaceAroundMultiplicativeOperator = true, + SpaceAroundShiftOperator = true, + SpaceAroundNullCoalescingOperator = true, + SpacesWithinParentheses = false, + SpaceWithinMethodCallParentheses = false, + SpaceWithinMethodDeclarationParentheses = false, + SpacesWithinIfParentheses = false, + SpacesWithinWhileParentheses = false, + SpacesWithinForParentheses = false, + SpacesWithinForeachParentheses = false, + SpacesWithinCatchParentheses = false, + SpacesWithinSwitchParentheses = false, + SpacesWithinLockParentheses = false, + SpacesWithinUsingParentheses = false, + SpacesWithinCastParentheses = false, + SpacesWithinSizeOfParentheses = false, + SpacesWithinTypeOfParentheses = false, + SpacesWithinCheckedExpressionParantheses = false, + SpaceBeforeConditionalOperatorCondition = true, + SpaceAfterConditionalOperatorCondition = true, + SpaceBeforeConditionalOperatorSeparator = true, + SpaceAfterConditionalOperatorSeparator = true, + + SpacesWithinBrackets = false, + SpacesBeforeBrackets = true, + SpaceBeforeBracketComma = false, + SpaceAfterBracketComma = true, + + SpaceBeforeForSemicolon = false, + SpaceAfterForSemicolon = true, + SpaceAfterTypecast = false, + + AlignEmbeddedStatements = true, + SimplePropertyFormatting = PropertyFormatting.AllowOneLine, + AutoPropertyFormatting = PropertyFormatting.AllowOneLine, + EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, + SpaceBeforeMethodDeclarationParameterComma = false, + SpaceAfterMethodDeclarationParameterComma = true, + SpaceAfterDelegateDeclarationParameterComma = true, + SpaceBeforeFieldDeclarationComma = false, + SpaceAfterFieldDeclarationComma = true, + SpaceBeforeLocalVariableDeclarationComma = false, + SpaceAfterLocalVariableDeclarationComma = true, + + SpaceBeforeIndexerDeclarationBracket = true, + SpaceWithinIndexerDeclarationBracket = false, + SpaceBeforeIndexerDeclarationParameterComma = false, + SpaceInNamedArgumentAfterDoubleColon = true, + RemoveEndOfLineWhiteSpace = true, + + SpaceAfterIndexerDeclarationParameterComma = true, + + MinimumBlankLinesBeforeUsings = 0, + MinimumBlankLinesAfterUsings = 1, + UsingPlacement = UsingPlacement.TopOfFile, + + MinimumBlankLinesBeforeFirstDeclaration = 0, + MinimumBlankLinesBetweenTypes = 1, + MinimumBlankLinesBetweenFields = 0, + MinimumBlankLinesBetweenEventFields = 0, + MinimumBlankLinesBetweenMembers = 1, + MinimumBlankLinesAroundRegion = 1, + MinimumBlankLinesInsideRegion = 1, + AlignToFirstIndexerArgument = false, + AlignToFirstIndexerDeclarationParameter = true, + AlignToFirstMethodCallArgument = false, + AlignToFirstMethodDeclarationParameter = true, + KeepCommentsAtFirstColumn = true, + ChainedMethodCallWrapping = Wrapping.DoNotChange, + MethodCallArgumentWrapping = Wrapping.DoNotChange, + NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, + MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, + + IndexerArgumentWrapping = Wrapping.DoNotChange, + NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, + IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, + + NewLineBeforeNewQueryClause = NewLinePlacement.NewLine + }; + } + + /// + /// Creates sharp develop indent style CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateSharpDevelop() + { + var baseOptions = CreateKRStyle(); + return baseOptions; + } + + /// + /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, + /// is commonly used in C. It is less common for C++, C#, and others. + /// + public static CSharpFormattingOptions CreateKRStyle() + { + return new CSharpFormattingOptions() { + IndentNamespaceBody = true, + IndentClassBody = true, + IndentInterfaceBody = true, + IndentStructBody = true, + IndentEnumBody = true, + IndentMethodBody = true, + IndentPropertyBody = true, + IndentEventBody = true, + IndentBlocks = true, + IndentSwitchBody = true, + IndentCaseBody = true, + IndentBreakStatements = true, + IndentPreprocessorDirectives = true, + NamespaceBraceStyle = BraceStyle.NextLine, + ClassBraceStyle = BraceStyle.NextLine, + InterfaceBraceStyle = BraceStyle.NextLine, + StructBraceStyle = BraceStyle.NextLine, + EnumBraceStyle = BraceStyle.NextLine, + MethodBraceStyle = BraceStyle.NextLine, + ConstructorBraceStyle = BraceStyle.NextLine, + DestructorBraceStyle = BraceStyle.NextLine, + AnonymousMethodBraceStyle = BraceStyle.EndOfLine, + PropertyBraceStyle = BraceStyle.EndOfLine, + PropertyGetBraceStyle = BraceStyle.EndOfLine, + PropertySetBraceStyle = BraceStyle.EndOfLine, + SimpleGetBlockFormatting = PropertyFormatting.AllowOneLine, + SimpleSetBlockFormatting = PropertyFormatting.AllowOneLine, + + EventBraceStyle = BraceStyle.EndOfLine, + EventAddBraceStyle = BraceStyle.EndOfLine, + EventRemoveBraceStyle = BraceStyle.EndOfLine, + AllowEventAddBlockInline = true, + AllowEventRemoveBlockInline = true, + StatementBraceStyle = BraceStyle.EndOfLine, + + ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, + CatchNewLinePlacement = NewLinePlacement.SameLine, + FinallyNewLinePlacement = NewLinePlacement.SameLine, + WhileNewLinePlacement = NewLinePlacement.SameLine, + ArrayInitializerWrapping = Wrapping.WrapIfTooLong, + ArrayInitializerBraceStyle = BraceStyle.EndOfLine, + + SpaceBeforeMethodCallParentheses = false, + SpaceBeforeMethodDeclarationParentheses = false, + SpaceBeforeConstructorDeclarationParentheses = false, + SpaceBeforeDelegateDeclarationParentheses = false, + SpaceBeforeIndexerDeclarationBracket = false, + SpaceAfterMethodCallParameterComma = true, + SpaceAfterConstructorDeclarationParameterComma = true, + NewLineBeforeConstructorInitializerColon = NewLinePlacement.NewLine, + NewLineAfterConstructorInitializerColon = NewLinePlacement.SameLine, + + SpaceBeforeNewParentheses = false, + SpacesWithinNewParentheses = false, + SpacesBetweenEmptyNewParentheses = false, + SpaceBeforeNewParameterComma = false, + SpaceAfterNewParameterComma = true, + + SpaceBeforeIfParentheses = true, + SpaceBeforeWhileParentheses = true, + SpaceBeforeForParentheses = true, + SpaceBeforeForeachParentheses = true, + SpaceBeforeCatchParentheses = true, + SpaceBeforeSwitchParentheses = true, + SpaceBeforeLockParentheses = true, + SpaceBeforeUsingParentheses = true, + + SpaceAroundAssignment = true, + SpaceAroundLogicalOperator = true, + SpaceAroundEqualityOperator = true, + SpaceAroundRelationalOperator = true, + SpaceAroundBitwiseOperator = true, + SpaceAroundAdditiveOperator = true, + SpaceAroundMultiplicativeOperator = true, + SpaceAroundShiftOperator = true, + SpaceAroundNullCoalescingOperator = true, + SpacesWithinParentheses = false, + SpaceWithinMethodCallParentheses = false, + SpaceWithinMethodDeclarationParentheses = false, + SpacesWithinIfParentheses = false, + SpacesWithinWhileParentheses = false, + SpacesWithinForParentheses = false, + SpacesWithinForeachParentheses = false, + SpacesWithinCatchParentheses = false, + SpacesWithinSwitchParentheses = false, + SpacesWithinLockParentheses = false, + SpacesWithinUsingParentheses = false, + SpacesWithinCastParentheses = false, + SpacesWithinSizeOfParentheses = false, + SpacesWithinTypeOfParentheses = false, + SpacesWithinCheckedExpressionParantheses = false, + SpaceBeforeConditionalOperatorCondition = true, + SpaceAfterConditionalOperatorCondition = true, + SpaceBeforeConditionalOperatorSeparator = true, + SpaceAfterConditionalOperatorSeparator = true, + SpaceBeforeArrayDeclarationBrackets = false, + + SpacesWithinBrackets = false, + SpacesBeforeBrackets = false, + SpaceBeforeBracketComma = false, + SpaceAfterBracketComma = true, + + SpaceBeforeForSemicolon = false, + SpaceAfterForSemicolon = true, + SpaceAfterTypecast = false, + + AlignEmbeddedStatements = true, + SimplePropertyFormatting = PropertyFormatting.AllowOneLine, + AutoPropertyFormatting = PropertyFormatting.AllowOneLine, + EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, + SpaceBeforeMethodDeclarationParameterComma = false, + SpaceAfterMethodDeclarationParameterComma = true, + SpaceAfterDelegateDeclarationParameterComma = true, + SpaceBeforeFieldDeclarationComma = false, + SpaceAfterFieldDeclarationComma = true, + SpaceBeforeLocalVariableDeclarationComma = false, + SpaceAfterLocalVariableDeclarationComma = true, + + SpaceWithinIndexerDeclarationBracket = false, + SpaceBeforeIndexerDeclarationParameterComma = false, + SpaceInNamedArgumentAfterDoubleColon = true, + + SpaceAfterIndexerDeclarationParameterComma = true, + RemoveEndOfLineWhiteSpace = true, + + MinimumBlankLinesBeforeUsings = 0, + MinimumBlankLinesAfterUsings = 1, + + MinimumBlankLinesBeforeFirstDeclaration = 0, + MinimumBlankLinesBetweenTypes = 1, + MinimumBlankLinesBetweenFields = 0, + MinimumBlankLinesBetweenEventFields = 0, + MinimumBlankLinesBetweenMembers = 1, + MinimumBlankLinesAroundRegion = 1, + MinimumBlankLinesInsideRegion = 1, + + KeepCommentsAtFirstColumn = true, + ChainedMethodCallWrapping = Wrapping.DoNotChange, + MethodCallArgumentWrapping = Wrapping.DoNotChange, + NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, + MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, + + IndexerArgumentWrapping = Wrapping.DoNotChange, + NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, + IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, + + NewLineBeforeNewQueryClause = NewLinePlacement.NewLine + }; + } + + /// + /// Creates allman indent style CSharpFormatting options used in Visual Studio. + /// + public static CSharpFormattingOptions CreateAllman() + { + var baseOptions = CreateKRStyle(); + baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLine; + baseOptions.PropertyBraceStyle = BraceStyle.NextLine; + baseOptions.PropertyGetBraceStyle = BraceStyle.NextLine; + baseOptions.PropertySetBraceStyle = BraceStyle.NextLine; + + baseOptions.EventBraceStyle = BraceStyle.NextLine; + baseOptions.EventAddBraceStyle = BraceStyle.NextLine; + baseOptions.EventRemoveBraceStyle = BraceStyle.NextLine; + baseOptions.StatementBraceStyle = BraceStyle.NextLine; + baseOptions.ArrayInitializerBraceStyle = BraceStyle.NextLine; + + baseOptions.CatchNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.ElseNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.ElseIfNewLinePlacement = NewLinePlacement.SameLine; + + baseOptions.FinallyNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.WhileNewLinePlacement = NewLinePlacement.DoNotCare; + baseOptions.ArrayInitializerWrapping = Wrapping.DoNotChange; + baseOptions.IndentBlocksInsideExpressions = true; + + return baseOptions; + } + + /// + /// The Whitesmiths style, also called Wishart style to a lesser extent, is less common today than the previous three. It was originally used in the documentation for the first commercial C compiler, the Whitesmiths Compiler. + /// + public static CSharpFormattingOptions CreateWhitesmiths() + { + var baseOptions = CreateKRStyle(); + + baseOptions.NamespaceBraceStyle = BraceStyle.NextLineShifted; + baseOptions.ClassBraceStyle = BraceStyle.NextLineShifted; + baseOptions.InterfaceBraceStyle = BraceStyle.NextLineShifted; + baseOptions.StructBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EnumBraceStyle = BraceStyle.NextLineShifted; + baseOptions.MethodBraceStyle = BraceStyle.NextLineShifted; + baseOptions.ConstructorBraceStyle = BraceStyle.NextLineShifted; + baseOptions.DestructorBraceStyle = BraceStyle.NextLineShifted; + baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertyBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertyGetBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertySetBraceStyle = BraceStyle.NextLineShifted; + + baseOptions.EventBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EventAddBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EventRemoveBraceStyle = BraceStyle.NextLineShifted; + baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted; + baseOptions.IndentBlocksInsideExpressions = true; + return baseOptions; + } + + /// + /// Like the Allman and Whitesmiths styles, GNU style puts braces on a line by themselves, indented by 2 spaces, + /// except when opening a function definition, where they are not indented. + /// In either case, the contained code is indented by 2 spaces from the braces. + /// Popularised by Richard Stallman, the layout may be influenced by his background of writing Lisp code. + /// In Lisp the equivalent to a block (a progn) + /// is a first class data entity and giving it its own indent level helps to emphasize that, + /// whereas in C a block is just syntax. + /// Although not directly related to indentation, GNU coding style also includes a space before the bracketed + /// list of arguments to a function. + /// + public static CSharpFormattingOptions CreateGNU() + { + var baseOptions = CreateAllman(); + baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted2; + return baseOptions; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs new file mode 100644 index 000000000..31b73f987 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs @@ -0,0 +1,161 @@ +// Copyright (c) 2010-2013 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.IO; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class TokenWriter + { + public abstract void StartNode(AstNode node); + public abstract void EndNode(AstNode node); + + /// + /// Writes an identifier. + /// + public abstract void WriteIdentifier(Identifier identifier); + + /// + /// Writes a keyword to the output. + /// + public abstract void WriteKeyword(Role role, string keyword); + + /// + /// Writes a token to the output. + /// + public abstract void WriteToken(Role role, string token); + + /// + /// Writes a primitive/literal value + /// + public abstract void WritePrimitiveValue(object value, string literalValue = null); + + public abstract void WritePrimitiveType(string type); + + public abstract void Space(); + public abstract void Indent(); + public abstract void Unindent(); + public abstract void NewLine(); + + public abstract void WriteComment(CommentType commentType, string content); + public abstract void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); + + public static TokenWriter Create(TextWriter writer, string indentation = "\t") + { + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(writer) { IndentationString = indentation })); + } + + public static TokenWriter CreateWriterThatSetsLocationsInAST(TextWriter writer, string indentation = "\t") + { + var target = new TextWriterTokenWriter(writer) { IndentationString = indentation }; + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(target, target))); + } + + public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer) + { + if (!(writer is ILocatable)) + throw new InvalidOperationException("writer does not provide locations!"); + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(writer, (ILocatable)writer))); + } + } + + public interface ILocatable + { + TextLocation Location { get; } + } + + public abstract class DecoratingTokenWriter : TokenWriter + { + TokenWriter decoratedWriter; + + protected DecoratingTokenWriter(TokenWriter decoratedWriter) + { + if (decoratedWriter == null) + throw new ArgumentNullException("decoratedWriter"); + this.decoratedWriter = decoratedWriter; + } + + public override void StartNode(AstNode node) + { + decoratedWriter.StartNode(node); + } + + public override void EndNode(AstNode node) + { + decoratedWriter.EndNode(node); + } + + public override void WriteIdentifier(Identifier identifier) + { + decoratedWriter.WriteIdentifier(identifier); + } + + public override void WriteKeyword(Role role, string keyword) + { + decoratedWriter.WriteKeyword(role, keyword); + } + + public override void WriteToken(Role role, string token) + { + decoratedWriter.WriteToken(role, token); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + decoratedWriter.WritePrimitiveValue(value, literalValue); + } + + public override void WritePrimitiveType(string type) + { + decoratedWriter.WritePrimitiveType(type); + } + + public override void Space() + { + decoratedWriter.Space(); + } + + public override void Indent() + { + decoratedWriter.Indent(); + } + + public override void Unindent() + { + decoratedWriter.Unindent(); + } + + public override void NewLine() + { + decoratedWriter.NewLine(); + } + + public override void WriteComment(CommentType commentType, string content) + { + decoratedWriter.WriteComment(commentType, content); + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + decoratedWriter.WritePreProcessorDirective(type, argument); + } + } +} + + diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs new file mode 100644 index 000000000..3f9200145 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs @@ -0,0 +1,123 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertMissingTokensDecorator : DecoratingTokenWriter + { + readonly Stack> nodes = new Stack>(); + List currentList; + readonly ILocatable locationProvider; + + public InsertMissingTokensDecorator(TokenWriter writer, ILocatable locationProvider) + : base(writer) + { + this.locationProvider = locationProvider; + currentList = new List(); + } + + public override void StartNode(AstNode node) + { + currentList.Add(node); + nodes.Push(currentList); + currentList = new List(); + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + System.Diagnostics.Debug.Assert(currentList != null); + foreach (var removable in node.Children.Where(n => n is CSharpTokenNode)) { + removable.Remove(); + } + foreach (var child in currentList) { + System.Diagnostics.Debug.Assert(child.Parent == null || node == child.Parent); + child.Remove(); + node.AddChildWithExistingRole(child); + } + currentList = nodes.Pop(); + base.EndNode(node); + } + + public override void WriteToken(Role role, string token) + { + CSharpTokenNode t = new CSharpTokenNode(locationProvider.Location, (TokenRole)role); + t.Role = role; + EmptyStatement node = nodes.Peek().LastOrDefault() as EmptyStatement; + if (node == null) + currentList.Add(t); + else { + node.Location = locationProvider.Location; + } + base.WriteToken(role, token); + } + + public override void WriteKeyword(Role role, string keyword) + { + TextLocation start = locationProvider.Location; + CSharpTokenNode t = null; + if (role is TokenRole) + t = new CSharpTokenNode(start, (TokenRole)role); + else if (role == EntityDeclaration.ModifierRole) + t = new CSharpModifierToken(start, CSharpModifierToken.GetModifierValue(keyword)); + else if (keyword == "this") { + ThisReferenceExpression node = nodes.Peek().LastOrDefault() as ThisReferenceExpression; + if (node != null) + node.Location = start; + } else if (keyword == "base") { + BaseReferenceExpression node = nodes.Peek().LastOrDefault() as BaseReferenceExpression; + if (node != null) + node.Location = start; + } + if (t != null) currentList.Add(t); + base.WriteKeyword(role, keyword); + } + + public override void WriteIdentifier(Identifier identifier) + { + if (!identifier.IsNull) + identifier.SetStartLocation(locationProvider.Location); + currentList.Add(identifier); + base.WriteIdentifier(identifier); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + Expression node = nodes.Peek().LastOrDefault() as Expression; + if (node is PrimitiveExpression) { + ((PrimitiveExpression)node).SetStartLocation(locationProvider.Location); + } + if (node is NullReferenceExpression) { + ((NullReferenceExpression)node).SetStartLocation(locationProvider.Location); + } + base.WritePrimitiveValue(value, literalValue); + } + + public override void WritePrimitiveType(string type) + { + PrimitiveType node = nodes.Peek().LastOrDefault() as PrimitiveType; + if (node != null) + node.SetStartLocation(locationProvider.Location); + base.WritePrimitiveType(type); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs new file mode 100644 index 000000000..475ca40b1 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -0,0 +1,363 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Inserts the parentheses into the AST that are needed to ensure the AST can be printed correctly. + /// For example, if the AST contains + /// BinaryOperatorExpresson(2, Mul, BinaryOperatorExpression(1, Add, 1))); printing that AST + /// would incorrectly result in "2 * 1 + 1". By running InsertParenthesesVisitor, the necessary + /// parentheses are inserted: "2 * (1 + 1)". + /// + public class InsertParenthesesVisitor : DepthFirstAstVisitor + { + /// + /// Gets/Sets whether the visitor should insert parentheses to make the code better looking. + /// If this property is false, it will insert parentheses only where strictly required by the language spec. + /// + public bool InsertParenthesesForReadability { get; set; } + + const int Primary = 16; + const int QueryOrLambda = 15; + const int Unary = 14; + const int RelationalAndTypeTesting = 10; + const int Equality = 9; + const int Conditional = 2; + const int Assignment = 1; + + /// + /// Gets the row number in the C# 4.0 spec operator precedence table. + /// + static int GetPrecedence(Expression expr) + { + // Note: the operator precedence table on MSDN is incorrect + if (expr is QueryExpression) { + // Not part of the table in the C# spec, but we need to ensure that queries within + // primary expressions get parenthesized. + return QueryOrLambda; + } + UnaryOperatorExpression uoe = expr as UnaryOperatorExpression; + if (uoe != null) { + if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement) + return Primary; + else + return Unary; + } + if (expr is CastExpression) + return Unary; + if (expr is PrimitiveExpression) { + var value = ((PrimitiveExpression)expr).Value; + if (value is int && (int)value < 0) + return Unary; + if (value is long && (long)value < 0) + return Unary; + } + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null) { + switch (boe.Operator) { + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + return 13; // multiplicative + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + return 12; // additive + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + return 11; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + return RelationalAndTypeTesting; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + return Equality; + case BinaryOperatorType.BitwiseAnd: + return 8; + case BinaryOperatorType.ExclusiveOr: + return 7; + case BinaryOperatorType.BitwiseOr: + return 6; + case BinaryOperatorType.ConditionalAnd: + return 5; + case BinaryOperatorType.ConditionalOr: + return 4; + case BinaryOperatorType.NullCoalescing: + return 3; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + if (expr is IsExpression || expr is AsExpression) + return RelationalAndTypeTesting; + if (expr is ConditionalExpression) + return Conditional; + if (expr is AssignmentExpression || expr is LambdaExpression) + return Assignment; + // anything else: primary expression + return Primary; + } + + /// + /// Parenthesizes the expression if it does not have the minimum required precedence. + /// + static void ParenthesizeIfRequired(Expression expr, int minimumPrecedence) + { + if (GetPrecedence(expr) < minimumPrecedence) { + Parenthesize(expr); + } + } + + static void Parenthesize(Expression expr) + { + expr.ReplaceWith(e => new ParenthesizedExpression { Expression = e }); + } + + // Primary expressions + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + ParenthesizeIfRequired(memberReferenceExpression.Target, Primary); + base.VisitMemberReferenceExpression(memberReferenceExpression); + } + + public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + ParenthesizeIfRequired(pointerReferenceExpression.Target, Primary); + base.VisitPointerReferenceExpression(pointerReferenceExpression); + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + ParenthesizeIfRequired(invocationExpression.Target, Primary); + base.VisitInvocationExpression(invocationExpression); + } + + public override void VisitIndexerExpression(IndexerExpression indexerExpression) + { + ParenthesizeIfRequired(indexerExpression.Target, Primary); + ArrayCreateExpression ace = indexerExpression.Target as ArrayCreateExpression; + if (ace != null && (InsertParenthesesForReadability || ace.Initializer.IsNull)) { + // require parentheses for "(new int[1])[0]" + Parenthesize(indexerExpression.Target); + } + base.VisitIndexerExpression(indexerExpression); + } + + // Unary expressions + public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + ParenthesizeIfRequired(unaryOperatorExpression.Expression, GetPrecedence(unaryOperatorExpression)); + UnaryOperatorExpression child = unaryOperatorExpression.Expression as UnaryOperatorExpression; + if (child != null && InsertParenthesesForReadability) + Parenthesize(child); + base.VisitUnaryOperatorExpression(unaryOperatorExpression); + } + + public override void VisitCastExpression(CastExpression castExpression) + { + // Even in readability mode, don't parenthesize casts of casts. + if (!(castExpression.Expression is CastExpression)) { + ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? Primary : Unary); + } + // There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases + // "(int)-1" is fine, but "(A)-b" is not a cast. + UnaryOperatorExpression uoe = castExpression.Expression as UnaryOperatorExpression; + if (uoe != null && !(uoe.Operator == UnaryOperatorType.BitNot || uoe.Operator == UnaryOperatorType.Not)) { + if (TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { + Parenthesize(castExpression.Expression); + } + } + // The above issue can also happen with PrimitiveExpressions representing negative values: + PrimitiveExpression pe = castExpression.Expression as PrimitiveExpression; + if (pe != null && pe.Value != null && TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { + TypeCode typeCode = Type.GetTypeCode(pe.Value.GetType()); + switch (typeCode) { + case TypeCode.SByte: + if ((sbyte)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int16: + if ((short)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int32: + if ((int)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int64: + if ((long)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Single: + if ((float)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Double: + if ((double)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Decimal: + if ((decimal)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + } + } + base.VisitCastExpression(castExpression); + } + + static bool TypeCanBeMisinterpretedAsExpression(AstType type) + { + // SimpleTypes can always be misinterpreted as IdentifierExpressions + // MemberTypes can be misinterpreted as MemberReferenceExpressions if they don't use double colon + // PrimitiveTypes or ComposedTypes can never be misinterpreted as expressions. + MemberType mt = type as MemberType; + if (mt != null) + return !mt.IsDoubleColon; + else + return type is SimpleType; + } + + // Binary Operators + public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + int precedence = GetPrecedence(binaryOperatorExpression); + if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary); + ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary); + } else { + // ?? is right-associative + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1); + ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); + } + } else { + if (InsertParenthesesForReadability && precedence < Equality) { + // In readable mode, boost the priority of the left-hand side if the operator + // there isn't the same as the operator on this expression. + if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) { + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); + } else { + ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality); + } + ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality); + } else { + // all other binary operators are left-associative + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); + ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence + 1); + } + } + base.VisitBinaryOperatorExpression(binaryOperatorExpression); + } + + BinaryOperatorType? GetBinaryOperatorType(Expression expr) + { + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null) + return boe.Operator; + else + return null; + } + + public override void VisitIsExpression(IsExpression isExpression) + { + if (InsertParenthesesForReadability) { + // few people know the precedence of 'is', so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(isExpression.Expression, Primary); + } else { + ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting); + } + base.VisitIsExpression(isExpression); + } + + public override void VisitAsExpression(AsExpression asExpression) + { + if (InsertParenthesesForReadability) { + // few people know the precedence of 'as', so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(asExpression.Expression, Primary); + } else { + ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting); + } + base.VisitAsExpression(asExpression); + } + + // Conditional operator + public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + // Associativity here is a bit tricky: + // (a ? b : c ? d : e) == (a ? b : (c ? d : e)) + // (a ? b ? c : d : e) == (a ? (b ? c : d) : e) + // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses + if (InsertParenthesesForReadability) { + // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(conditionalExpression.Condition, Primary); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary); + } else { + ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, Conditional); + } + base.VisitConditionalExpression(conditionalExpression); + } + + public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + // assignment is right-associative + ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1); + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1); + } else { + ParenthesizeIfRequired(assignmentExpression.Right, Assignment); + } + base.VisitAssignmentExpression(assignmentExpression); + } + + // don't need to handle lambdas, they have lowest precedence and unambiguous associativity + + public override void VisitQueryExpression(QueryExpression queryExpression) + { + // Query expressions are strange beasts: + // "var a = -from b in c select d;" is valid, so queries bind stricter than unary expressions. + // However, the end of the query is greedy. So their start sort of has a high precedence, + // while their end has a very low precedence. We handle this by checking whether a query is used + // as left part of a binary operator, and parenthesize it if required. + if (queryExpression.Role == BinaryOperatorExpression.LeftRole) + Parenthesize(queryExpression); + if (queryExpression.Parent is IsExpression || queryExpression.Parent is AsExpression) + Parenthesize(queryExpression); + if (InsertParenthesesForReadability) { + // when readability is desired, always parenthesize query expressions within unary or binary operators + if (queryExpression.Parent is UnaryOperatorExpression || queryExpression.Parent is BinaryOperatorExpression) + Parenthesize(queryExpression); + } + base.VisitQueryExpression(queryExpression); + } + + public override void VisitNamedExpression (NamedExpression namedExpression) + { + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(namedExpression.Expression, RelationalAndTypeTesting + 1); + } + base.VisitNamedExpression (namedExpression); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs new file mode 100644 index 000000000..e9aca4bf5 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertRequiredSpacesDecorator : DecoratingTokenWriter + { + /// + /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written. + /// + LastWritten lastWritten; + + enum LastWritten + { + Whitespace, + Other, + KeywordOrIdentifier, + Plus, + Minus, + Ampersand, + QuestionMark, + Division + } + + public InsertRequiredSpacesDecorator(TokenWriter writer) + : base(writer) + { + } + + public override void WriteIdentifier(Identifier identifier) + { + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + // this space is not strictly required, so we call Space() + Space(); + } + } else if (lastWritten == LastWritten.KeywordOrIdentifier) { + // this space is strictly required, so we directly call the formatter + base.Space(); + } + base.WriteIdentifier(identifier); + lastWritten = LastWritten.KeywordOrIdentifier; + } + + public override void WriteKeyword(Role role, string keyword) + { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + Space(); + } + base.WriteKeyword(role, keyword); + lastWritten = LastWritten.KeywordOrIdentifier; + } + + public override void WriteToken(Role role, string token) + { + // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token. + // Note that we don't need to handle tokens like = because there's no valid + // C# program that contains the single token twice in a row. + // (for +, - and &, this can happen with unary operators; + // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0"; + // and for /, this can happen with "1/ *ptr" or "1/ //comment".) + if (lastWritten == LastWritten.Plus && token[0] == '+' || + lastWritten == LastWritten.Minus && token[0] == '-' || + lastWritten == LastWritten.Ampersand && token[0] == '&' || + lastWritten == LastWritten.QuestionMark && token[0] == '?' || + lastWritten == LastWritten.Division && token[0] == '*') { + base.Space(); + } + base.WriteToken(role, token); + if (token == "+") { + lastWritten = LastWritten.Plus; + } else if (token == "-") { + lastWritten = LastWritten.Minus; + } else if (token == "&") { + lastWritten = LastWritten.Ampersand; + } else if (token == "?") { + lastWritten = LastWritten.QuestionMark; + } else if (token == "/") { + lastWritten = LastWritten.Division; + } else { + lastWritten = LastWritten.Other; + } + } + + public override void Space() + { + base.Space(); + lastWritten = LastWritten.Whitespace; + } + + public override void NewLine() + { + base.NewLine(); + lastWritten = LastWritten.Whitespace; + } + + public override void WriteComment(CommentType commentType, string content) + { + if (lastWritten == LastWritten.Division) { + // When there's a comment starting after a division operator + // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. + base.Space(); + } + base.WriteComment(commentType, content); + lastWritten = LastWritten.Whitespace; + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + base.WritePreProcessorDirective(type, argument); + lastWritten = LastWritten.Whitespace; + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + base.WritePrimitiveValue(value, literalValue); + if (value == null || value is bool) + return; + if (value is string) { + lastWritten = LastWritten.Other; + } else if (value is char) { + lastWritten = LastWritten.Other; + } else if (value is decimal) { + lastWritten = LastWritten.Other; + } else if (value is float) { + float f = (float)value; + if (float.IsInfinity(f) || float.IsNaN(f)) return; + lastWritten = LastWritten.Other; + } else if (value is double) { + double f = (double)value; + if (double.IsInfinity(f) || double.IsNaN(f)) return; + // needs space if identifier follows number; + // this avoids mistaking the following identifier as type suffix + lastWritten = LastWritten.KeywordOrIdentifier; + } else if (value is IFormattable) { + // needs space if identifier follows number; + // this avoids mistaking the following identifier as type suffix + lastWritten = LastWritten.KeywordOrIdentifier; + } else { + lastWritten = LastWritten.Other; + } + } + + public override void WritePrimitiveType(string type) + { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + Space(); + } + base.WritePrimitiveType(type); + if (type == "new") { + lastWritten = LastWritten.Other; + } else { + lastWritten = LastWritten.KeywordOrIdentifier; + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertSpecialsDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertSpecialsDecorator.cs new file mode 100644 index 000000000..0fafdeef0 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertSpecialsDecorator.cs @@ -0,0 +1,157 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertSpecialsDecorator : DecoratingTokenWriter + { + readonly Stack positionStack = new Stack(); + int visitorWroteNewLine = 0; + + public InsertSpecialsDecorator(TokenWriter writer) : base(writer) + { + } + + public override void StartNode(AstNode node) + { + if (positionStack.Count > 0) { + WriteSpecialsUpToNode(node); + } + positionStack.Push(node.FirstChild); + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + base.EndNode(node); + AstNode pos = positionStack.Pop(); + Debug.Assert(pos == null || pos.Parent == node); + WriteSpecials(pos, null); + } + + public override void WriteKeyword(Role role, string keyword) + { + if (role != null) { + WriteSpecialsUpToRole(role); + } + base.WriteKeyword(role, keyword); + } + + public override void WriteIdentifier(Identifier identifier) + { + WriteSpecialsUpToRole(identifier.Role ?? Roles.Identifier); + base.WriteIdentifier(identifier); + } + + public override void WriteToken(Role role, string token) + { + WriteSpecialsUpToRole(role); + base.WriteToken(role, token); + } + + public override void NewLine() + { + if (visitorWroteNewLine >= 0) + base.NewLine(); + visitorWroteNewLine++; + } + + #region WriteSpecials + /// + /// Writes all specials from start to end (exclusive). Does not touch the positionStack. + /// + void WriteSpecials(AstNode start, AstNode end) + { + for (AstNode pos = start; pos != end; pos = pos.NextSibling) { + if (pos.Role == Roles.Comment) { + var node = (Comment)pos; + base.WriteComment(node.CommentType, node.Content); + } + // see CSharpOutputVisitor.VisitNewLine() + // if (pos.Role == Roles.NewLine) { + // if (visitorWroteNewLine <= 0) + // base.NewLine(); + // visitorWroteNewLine--; + // } + if (pos.Role == Roles.PreProcessorDirective) { + var node = (PreProcessorDirective)pos; + base.WritePreProcessorDirective(node.Type, node.Argument); + } + } + } + + /// + /// Writes all specials between the current position (in the positionStack) and the next + /// node with the specified role. Advances the current position. + /// + void WriteSpecialsUpToRole(Role role) + { + WriteSpecialsUpToRole(role, null); + } + + void WriteSpecialsUpToRole(Role role, AstNode nextNode) + { + if (positionStack.Count == 0) { + return; + } + // Look for the role between the current position and the nextNode. + for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) { + if (pos.Role == role) { + WriteSpecials(positionStack.Pop(), pos); + // Push the next sibling because the node matching the role is not a special, + // and should be considered to be already handled. + positionStack.Push(pos.NextSibling); + // This is necessary for OptionalComma() to work correctly. + break; + } + } + } + + /// + /// Writes all specials between the current position (in the positionStack) and the specified node. + /// Advances the current position. + /// + void WriteSpecialsUpToNode(AstNode node) + { + if (positionStack.Count == 0) { + return; + } + for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { + if (pos == node) { + WriteSpecials(positionStack.Pop(), pos); + // Push the next sibling because the node itself is not a special, + // and should be considered to be already handled. + positionStack.Push(pos.NextSibling); + // This is necessary for OptionalComma() to work correctly. + break; + } + } + } + #endregion + } +} + + + + diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterOutputFormatter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterOutputFormatter.cs new file mode 100644 index 000000000..277b1c3e9 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterOutputFormatter.cs @@ -0,0 +1,419 @@ +// Copyright (c) 2010-2013 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.Globalization; +using System.IO; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Writes C# code into a TextWriter. + /// + public class TextWriterTokenWriter : TokenWriter, ILocatable + { + readonly TextWriter textWriter; + int indentation; + bool needsIndent = true; + bool isAtStartOfLine = true; + int line, column; + + public int Indentation { + get { return this.indentation; } + set { this.indentation = value; } + } + + public TextLocation Location { + get { return new TextLocation(line, column + (needsIndent ? indentation * IndentationString.Length : 0)); } + } + + public string IndentationString { get; set; } + + public TextWriterTokenWriter(TextWriter textWriter) + { + if (textWriter == null) + throw new ArgumentNullException("textWriter"); + this.textWriter = textWriter; + this.IndentationString = "\t"; + this.line = 1; + this.column = 1; + } + + public override void WriteIdentifier(Identifier identifier) + { + WriteIndentation(); + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + textWriter.Write('@'); + column++; + } + textWriter.Write(identifier.Name); + column += identifier.Name.Length; + isAtStartOfLine = false; + } + + public override void WriteKeyword(Role role, string keyword) + { + WriteIndentation(); + column += keyword.Length; + textWriter.Write(keyword); + isAtStartOfLine = false; + } + + public override void WriteToken(Role role, string token) + { + WriteIndentation(); + column += token.Length; + textWriter.Write(token); + isAtStartOfLine = false; + } + + public override void Space() + { + WriteIndentation(); + column++; + textWriter.Write(' '); + } + + protected void WriteIndentation() + { + if (needsIndent) { + needsIndent = false; + for (int i = 0; i < indentation; i++) { + textWriter.Write(this.IndentationString); + } + column += indentation * IndentationString.Length; + } + } + + public override void NewLine() + { + textWriter.WriteLine(); + column = 1; + line++; + needsIndent = true; + isAtStartOfLine = true; + } + + public override void Indent() + { + indentation++; + } + + public override void Unindent() + { + indentation--; + } + + public override void WriteComment(CommentType commentType, string content) + { + WriteIndentation(); + switch (commentType) { + case CommentType.SingleLine: + textWriter.Write("//"); + textWriter.WriteLine(content); + column += 2 + content.Length; + needsIndent = true; + isAtStartOfLine = true; + break; + case CommentType.MultiLine: + textWriter.Write("/*"); + textWriter.Write(content); + textWriter.Write("*/"); + column += 2; + UpdateEndLocation(content, ref line, ref column); + column += 2; + isAtStartOfLine = false; + break; + case CommentType.Documentation: + textWriter.Write("///"); + textWriter.WriteLine(content); + column += 3 + content.Length; + needsIndent = true; + isAtStartOfLine = true; + break; + case CommentType.MultiLineDocumentation: + textWriter.Write("/**"); + textWriter.Write(content); + textWriter.Write("*/"); + column += 3; + UpdateEndLocation(content, ref line, ref column); + column += 2; + isAtStartOfLine = false; + break; + default: + textWriter.Write(content); + column += content.Length; + break; + } + } + + static void UpdateEndLocation(string content, ref int line, ref int column) + { + if (string.IsNullOrEmpty(content)) + return; + for (int i = 0; i < content.Length; i++) { + char ch = content[i]; + switch (ch) { + case '\r': + if (i + 1 < content.Length && content[i + 1] == '\n') + i++; + goto case '\n'; + case '\n': + line++; + column = 0; + break; + } + column++; + } + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + // pre-processor directive must start on its own line + if (!isAtStartOfLine) + NewLine(); + WriteIndentation(); + textWriter.Write('#'); + string directive = type.ToString().ToLowerInvariant(); + textWriter.Write(directive); + column += 1 + directive.Length; + if (!string.IsNullOrEmpty(argument)) { + textWriter.Write(' '); + textWriter.Write(argument); + column += 1 + argument.Length; + } + NewLine(); + } + + public static string PrintPrimitiveValue(object value) + { + TextWriter writer = new StringWriter(); + TextWriterTokenWriter tokenWriter = new TextWriterTokenWriter(writer); + tokenWriter.WritePrimitiveValue(value); + return writer.ToString(); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + if (literalValue != null) { + textWriter.Write(literalValue); + column += literalValue.Length; + return; + } + + if (value == null) { + // usually NullReferenceExpression should be used for this, but we'll handle it anyways + textWriter.Write("null"); + column += 4; + return; + } + + if (value is bool) { + if ((bool)value) { + textWriter.Write("true"); + column += 4; + } else { + textWriter.Write("false"); + column += 5; + } + return; + } + + if (value is string) { + string tmp = "\"" + ConvertString(value.ToString()) + "\""; + column += tmp.Length; + textWriter.Write(tmp); + } else if (value is char) { + string tmp = "'" + ConvertCharLiteral((char)value) + "'"; + column += tmp.Length; + textWriter.Write(tmp); + } else if (value is decimal) { + string str = ((decimal)value).ToString(NumberFormatInfo.InvariantInfo) + "m"; + column += str.Length; + textWriter.Write(str); + } else if (value is float) { + float f = (float)value; + if (float.IsInfinity(f) || float.IsNaN(f)) { + // Strictly speaking, these aren't PrimitiveExpressions; + // but we still support writing these to make life easier for code generators. + textWriter.Write("float"); + column += 5; + WriteToken(Roles.Dot, "."); + if (float.IsPositiveInfinity(f)) { + textWriter.Write("PositiveInfinity"); + column += "PositiveInfinity".Length; + } else if (float.IsNegativeInfinity(f)) { + textWriter.Write("NegativeInfinity"); + column += "NegativeInfinity".Length; + } else { + textWriter.Write("NaN"); + column += 3; + } + return; + } + if (f == 0 && 1 / f == float.NegativeInfinity) { + // negative zero is a special case + // (again, not a primitive expression, but it's better to handle + // the special case here than to do it in all code generators) + textWriter.Write("-"); + column++; + } + var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f"; + column += str.Length; + textWriter.Write(str); + } else if (value is double) { + double f = (double)value; + if (double.IsInfinity(f) || double.IsNaN(f)) { + // Strictly speaking, these aren't PrimitiveExpressions; + // but we still support writing these to make life easier for code generators. + textWriter.Write("double"); + column += 6; + WriteToken(Roles.Dot, "."); + if (double.IsPositiveInfinity(f)) { + textWriter.Write("PositiveInfinity"); + column += "PositiveInfinity".Length; + } else if (double.IsNegativeInfinity(f)) { + textWriter.Write("NegativeInfinity"); + column += "NegativeInfinity".Length; + } else { + textWriter.Write("NaN"); + column += 3; + } + return; + } + if (f == 0 && 1 / f == double.NegativeInfinity) { + // negative zero is a special case + // (again, not a primitive expression, but it's better to handle + // the special case here than to do it in all code generators) + textWriter.Write("-"); + } + string number = f.ToString("R", NumberFormatInfo.InvariantInfo); + if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) { + number += ".0"; + } + textWriter.Write(number); + } else if (value is IFormattable) { + StringBuilder b = new StringBuilder (); +// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) { +// b.Append("0x"); +// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo)); +// } else { + b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo)); +// } + if (value is uint || value is ulong) { + b.Append("u"); + } + if (value is long || value is ulong) { + b.Append("L"); + } + textWriter.Write(b.ToString()); + column += b.Length; + } else { + textWriter.Write(value.ToString()); + column += value.ToString().Length; + } + } + + /// + /// Gets the escape sequence for the specified character within a char literal. + /// Does not include the single quotes surrounding the char literal. + /// + public static string ConvertCharLiteral(char ch) + { + if (ch == '\'') { + return "\\'"; + } + return ConvertChar(ch); + } + + /// + /// Gets the escape sequence for the specified character. + /// + /// This method does not convert ' or ". + static string ConvertChar(char ch) + { + switch (ch) { + case '\\': + return "\\\\"; + case '\0': + return "\\0"; + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\f': + return "\\f"; + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\v': + return "\\v"; + default: + if (char.IsControl(ch) || char.IsSurrogate(ch) || + // print all uncommon white spaces as numbers + (char.IsWhiteSpace(ch) && ch != ' ')) { + return "\\u" + ((int)ch).ToString("x4"); + } else { + return ch.ToString(); + } + } + } + + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string str) + { + StringBuilder sb = new StringBuilder (); + foreach (char ch in str) { + if (ch == '"') { + sb.Append("\\\""); + } else { + sb.Append(ConvertChar(ch)); + } + } + return sb.ToString(); + } + + public override void WritePrimitiveType(string type) + { + textWriter.Write(type); + column += type.Length; + if (type == "new") { + textWriter.Write("()"); + column += 2; + } + } + + public override void StartNode(AstNode node) + { + // Write out the indentation, so that overrides of this method + // can rely use the current output length to identify the position of the node + // in the output. + WriteIndentation(); + } + + public override void EndNode(AstNode node) + { + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/AliasNamespaceResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/AliasNamespaceResolveResult.cs new file mode 100644 index 000000000..2d6dec1ae --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/AliasNamespaceResolveResult.cs @@ -0,0 +1,52 @@ +// +// AliasResolveResult.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Semantics; +using System.Collections.Generic; +using ICSharpCode.NRefactory.CSharp.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents a namespace resolve result that's resolved using an alias. + /// + public class AliasNamespaceResolveResult : NamespaceResolveResult + { + /// + /// The alias used. + /// + public string Alias { + get; + private set; + } + + public AliasNamespaceResolveResult(string alias, NamespaceResolveResult underlyingResult) : base (underlyingResult.Namespace) + { + this.Alias = alias; + } + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/AliasTypeResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/AliasTypeResolveResult.cs new file mode 100644 index 000000000..f7f639b48 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/AliasTypeResolveResult.cs @@ -0,0 +1,52 @@ +// +// AliasTypeResolveResult.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Semantics; +using System.Collections.Generic; +using ICSharpCode.NRefactory.CSharp.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents a type resolve result that's resolved using an alias. + /// + public class AliasTypeResolveResult : TypeResolveResult + { + /// + /// The alias used. + /// + public string Alias { + get; + private set; + } + + public AliasTypeResolveResult(string alias, TypeResolveResult underlyingResult) : base (underlyingResult.Type) + { + this.Alias = alias; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs new file mode 100644 index 000000000..8eefcfd07 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs @@ -0,0 +1,81 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents the result of an await expression. + /// + public class AwaitResolveResult : ResolveResult + { + /// + /// The method representing the GetAwaiter() call. Can be an or a . + /// + public readonly ResolveResult GetAwaiterInvocation; + + /// + /// Awaiter type. Will not be null (but can be UnknownType). + /// + public readonly IType AwaiterType; + + /// + /// Property representing the IsCompleted property on the awaiter type. Can be null if the awaiter type or the property was not found, or when awaiting a dynamic expression. + /// + public readonly IProperty IsCompletedProperty; + + /// + /// Method representing the OnCompleted method on the awaiter type. Can be null if the awaiter type or the method was not found, or when awaiting a dynamic expression. + /// This can also refer to an UnsafeOnCompleted method, if the awaiter type implements System.Runtime.CompilerServices.ICriticalNotifyCompletion. + /// + public readonly IMethod OnCompletedMethod; + + /// + /// Method representing the GetResult method on the awaiter type. Can be null if the awaiter type or the method was not found, or when awaiting a dynamic expression. + /// + public readonly IMethod GetResultMethod; + + public AwaitResolveResult(IType resultType, ResolveResult getAwaiterInvocation, IType awaiterType, IProperty isCompletedProperty, IMethod onCompletedMethod, IMethod getResultMethod) + : base(resultType) + { + if (awaiterType == null) + throw new ArgumentNullException("awaiterType"); + if (getAwaiterInvocation == null) + throw new ArgumentNullException("getAwaiterInvocation"); + this.GetAwaiterInvocation = getAwaiterInvocation; + this.AwaiterType = awaiterType; + this.IsCompletedProperty = isCompletedProperty; + this.OnCompletedMethod = onCompletedMethod; + this.GetResultMethod = getResultMethod; + } + + public override bool IsError { + get { return this.GetAwaiterInvocation.IsError || (AwaiterType.Kind != TypeKind.Dynamic && (this.IsCompletedProperty == null || this.OnCompletedMethod == null || this.GetResultMethod == null)); } + } + + public override IEnumerable GetChildResults() { + return new[] { GetAwaiterInvocation }; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs new file mode 100644 index 000000000..666e420e7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -0,0 +1,1248 @@ +// Copyright (c) 2010-2013 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.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Contains logic that determines whether an implicit conversion exists between two types. + /// + /// + /// This class is thread-safe. + /// + public sealed class CSharpConversions + { + readonly ConcurrentDictionary implicitConversionCache = new ConcurrentDictionary(); + readonly ICompilation compilation; + readonly IType objectType; + + public CSharpConversions(ICompilation compilation) + { + if (compilation == null) + throw new ArgumentNullException("compilation"); + this.compilation = compilation; + this.objectType = compilation.FindType(KnownTypeCode.Object); + this.dynamicErasure = new DynamicErasure(this); + } + + /// + /// Gets the Conversions instance for the specified . + /// This will make use of the context's cache manager to reuse the Conversions instance. + /// + public static CSharpConversions Get(ICompilation compilation) + { + if (compilation == null) + throw new ArgumentNullException("compilation"); + CacheManager cache = compilation.CacheManager; + CSharpConversions operators = (CSharpConversions)cache.GetShared(typeof(CSharpConversions)); + if (operators == null) { + operators = (CSharpConversions)cache.GetOrAddShared(typeof(CSharpConversions), new CSharpConversions(compilation)); + } + return operators; + } + + #region TypePair (for caching) + struct TypePair : IEquatable + { + public readonly IType FromType; + public readonly IType ToType; + + public TypePair(IType fromType, IType toType) + { + Debug.Assert(fromType != null && toType != null); + this.FromType = fromType; + this.ToType = toType; + } + + public override bool Equals(object obj) + { + return (obj is TypePair) && Equals((TypePair)obj); + } + + public bool Equals(TypePair other) + { + return object.Equals(this.FromType, other.FromType) && object.Equals(this.ToType, other.ToType); + } + + public override int GetHashCode() + { + unchecked { + return 1000000007 * FromType.GetHashCode() + 1000000009 * ToType.GetHashCode(); + } + } + } + #endregion + + #region ImplicitConversion + private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined) + { + Conversion c; + if (resolveResult.IsCompileTimeConstant) { + c = ImplicitEnumerationConversion(resolveResult, toType); + if (c.IsValid) return c; + if (ImplicitConstantExpressionConversion(resolveResult, toType)) + return Conversion.ImplicitConstantExpressionConversion; + c = StandardImplicitConversion(resolveResult.Type, toType); + if (c != Conversion.None) return c; + if (allowUserDefined) { + c = UserDefinedImplicitConversion(resolveResult, resolveResult.Type, toType); + if (c != Conversion.None) return c; + } + } else { + c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined); + if (c != Conversion.None) return c; + } + if (resolveResult.Type.Kind == TypeKind.Dynamic) + return Conversion.ImplicitDynamicConversion; + c = AnonymousFunctionConversion(resolveResult, toType); + if (c != Conversion.None) return c; + c = MethodGroupConversion(resolveResult, toType); + return c; + } + + private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined) + { + // C# 4.0 spec: §6.1 + var c = StandardImplicitConversion(fromType, toType); + if (c == Conversion.None && allowUserDefined) { + c = UserDefinedImplicitConversion(null, fromType, toType); + } + return c; + } + + public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType) + { + if (resolveResult == null) + throw new ArgumentNullException("resolveResult"); + return ImplicitConversion(resolveResult, toType, allowUserDefined: true); + } + + public Conversion ImplicitConversion(IType fromType, IType toType) + { + if (fromType == null) + throw new ArgumentNullException("fromType"); + if (toType == null) + throw new ArgumentNullException("toType"); + + TypePair pair = new TypePair(fromType, toType); + Conversion c; + if (implicitConversionCache.TryGetValue(pair, out c)) + return c; + + c = ImplicitConversion(fromType, toType, allowUserDefined: true); + + implicitConversionCache[pair] = c; + return c; + } + + public Conversion StandardImplicitConversion(IType fromType, IType toType) + { + if (fromType == null) + throw new ArgumentNullException("fromType"); + if (toType == null) + throw new ArgumentNullException("toType"); + // C# 4.0 spec: §6.3.1 + if (IdentityConversion(fromType, toType)) + return Conversion.IdentityConversion; + if (ImplicitNumericConversion(fromType, toType)) + return Conversion.ImplicitNumericConversion; + Conversion c = ImplicitNullableConversion(fromType, toType); + if (c != Conversion.None) + return c; + if (NullLiteralConversion(fromType, toType)) + return Conversion.NullLiteralConversion; + if (ImplicitReferenceConversion(fromType, toType, 0)) + return Conversion.ImplicitReferenceConversion; + if (IsBoxingConversion(fromType, toType)) + return Conversion.BoxingConversion; + if (ImplicitTypeParameterConversion(fromType, toType)) { + // Implicit type parameter conversions that aren't also + // reference conversions are considered to be boxing conversions + return Conversion.BoxingConversion; + } + if (ImplicitPointerConversion(fromType, toType)) + return Conversion.ImplicitPointerConversion; + return Conversion.None; + } + + /// + /// Gets whether the type 'fromType' is convertible to 'toType' + /// using one of the conversions allowed when satisying constraints (§4.4.4) + /// + public bool IsConstraintConvertible(IType fromType, IType toType) + { + if (fromType == null) + throw new ArgumentNullException("fromType"); + if (toType == null) + throw new ArgumentNullException("toType"); + + if (IdentityConversion(fromType, toType)) + return true; + if (ImplicitReferenceConversion(fromType, toType, 0)) + return true; + if (NullableType.IsNullable(fromType)) { + // An 'object' constraint still allows nullable value types + // (object constraints don't exist in C#, but are inserted by DefaultResolvedTypeParameter.DirectBaseTypes) + if (toType.IsKnownType(KnownTypeCode.Object)) + return true; + } else { + if (IsBoxingConversion(fromType, toType)) + return true; + } + if (ImplicitTypeParameterConversion(fromType, toType)) + return true; + return false; + } + #endregion + + #region ExplicitConversion + public Conversion ExplicitConversion(ResolveResult resolveResult, IType toType) + { + if (resolveResult == null) + throw new ArgumentNullException("resolveResult"); + if (toType == null) + throw new ArgumentNullException("toType"); + + if (resolveResult.Type.Kind == TypeKind.Dynamic) + return Conversion.ExplicitDynamicConversion; + Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false); + if (c != Conversion.None) + return c; + c = ExplicitConversionImpl(resolveResult.Type, toType); + if (c != Conversion.None) + return c; + return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType); + } + + public Conversion ExplicitConversion(IType fromType, IType toType) + { + if (fromType == null) + throw new ArgumentNullException("fromType"); + if (toType == null) + throw new ArgumentNullException("toType"); + + Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false); + if (c != Conversion.None) + return c; + c = ExplicitConversionImpl(fromType, toType); + if (c != Conversion.None) + return c; + return UserDefinedExplicitConversion(null, fromType, toType); + } + + Conversion ExplicitConversionImpl(IType fromType, IType toType) + { + // This method is called after we already checked for implicit conversions, + // so any remaining conversions must be explicit. + if (AnyNumericConversion(fromType, toType)) + return Conversion.ExplicitNumericConversion; + if (ExplicitEnumerationConversion(fromType, toType)) + return Conversion.EnumerationConversion(false, false); + Conversion c = ExplicitNullableConversion(fromType, toType); + if (c != Conversion.None) + return c; + if (ExplicitReferenceConversion(fromType, toType)) + return Conversion.ExplicitReferenceConversion; + if (UnboxingConversion(fromType, toType)) + return Conversion.UnboxingConversion; + c = ExplicitTypeParameterConversion(fromType, toType); + if (c != Conversion.None) + return c; + if (ExplicitPointerConversion(fromType, toType)) + return Conversion.ExplicitPointerConversion; + return Conversion.None; + } + #endregion + + #region Identity Conversion + /// + /// Gets whether there is an identity conversion from to + /// + public bool IdentityConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.1 + return fromType.AcceptVisitor(dynamicErasure).Equals(toType.AcceptVisitor(dynamicErasure)); + } + + readonly DynamicErasure dynamicErasure; + + sealed class DynamicErasure : TypeVisitor + { + readonly IType objectType; + + public DynamicErasure(CSharpConversions conversions) + { + this.objectType = conversions.objectType; + } + + public override IType VisitOtherType(IType type) + { + if (type.Kind == TypeKind.Dynamic) + return objectType; + else + return base.VisitOtherType(type); + } + } + #endregion + + #region Numeric Conversions + static readonly bool[,] implicitNumericConversionLookup = { + // to: short ushort int uint long ulong + // from: + /* char */ { false, true , true , true , true , true }, + /* sbyte */ { true , false, true , false, true , false }, + /* byte */ { true , true , true , true , true , true }, + /* short */ { false, false, true , false, true , false }, + /* ushort */ { false, false, true , true , true , true }, + /* int */ { false, false, false, false, true , false }, + /* uint */ { false, false, false, false, true , true }, + }; + + bool ImplicitNumericConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.2 + + TypeCode from = ReflectionHelper.GetTypeCode(fromType); + TypeCode to = ReflectionHelper.GetTypeCode(toType); + if (to >= TypeCode.Single && to <= TypeCode.Decimal) { + // Conversions to float/double/decimal exist from all integral types, + // and there's a conversion from float to double. + return from >= TypeCode.Char && from <= TypeCode.UInt64 + || from == TypeCode.Single && to == TypeCode.Double; + } else { + // Conversions to integral types: look at the table + return from >= TypeCode.Char && from <= TypeCode.UInt32 + && to >= TypeCode.Int16 && to <= TypeCode.UInt64 + && implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16]; + } + } + + bool IsNumericType(IType type) + { + TypeCode c = ReflectionHelper.GetTypeCode(type); + return c >= TypeCode.Char && c <= TypeCode.Decimal; + } + + bool AnyNumericConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.2 + §6.2.1 + return IsNumericType(fromType) && IsNumericType(toType); + } + #endregion + + #region Enumeration Conversions + Conversion ImplicitEnumerationConversion(ResolveResult rr, IType toType) + { + // C# 4.0 spec: §6.1.3 + Debug.Assert(rr.IsCompileTimeConstant); + TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type); + if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDouble(rr.ConstantValue) == 0) { + if (NullableType.GetUnderlyingType(toType).Kind == TypeKind.Enum) { + return Conversion.EnumerationConversion(true, NullableType.IsNullable(toType)); + } + } + return Conversion.None; + } + + bool ExplicitEnumerationConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.2.2 + if (fromType.Kind == TypeKind.Enum) { + return toType.Kind == TypeKind.Enum || IsNumericType(toType); + } else if (IsNumericType(fromType)) { + return toType.Kind == TypeKind.Enum; + } + return false; + } + #endregion + + #region Nullable Conversions + Conversion ImplicitNullableConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.4 + if (NullableType.IsNullable(toType)) { + IType t = NullableType.GetUnderlyingType(toType); + IType s = NullableType.GetUnderlyingType(fromType); // might or might not be nullable + if (IdentityConversion(s, t)) + return Conversion.ImplicitNullableConversion; + if (ImplicitNumericConversion(s, t)) + return Conversion.ImplicitLiftedNumericConversion; + } + return Conversion.None; + } + + Conversion ExplicitNullableConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.4 + if (NullableType.IsNullable(toType) || NullableType.IsNullable(fromType)) { + IType t = NullableType.GetUnderlyingType(toType); + IType s = NullableType.GetUnderlyingType(fromType); + if (IdentityConversion(s, t)) + return Conversion.ExplicitNullableConversion; + if (AnyNumericConversion(s, t)) + return Conversion.ExplicitLiftedNumericConversion; + if (ExplicitEnumerationConversion(s, t)) + return Conversion.EnumerationConversion(false, true); + } + return Conversion.None; + } + #endregion + + #region Null Literal Conversion + bool NullLiteralConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.5 + if (fromType.Kind == TypeKind.Null) { + return NullableType.IsNullable(toType) || toType.IsReferenceType == true; + } else { + return false; + } + } + #endregion + + #region Implicit Reference Conversion + public bool IsImplicitReferenceConversion(IType fromType, IType toType) + { + return ImplicitReferenceConversion(fromType, toType, 0); + } + + bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckNestingDepth) + { + // C# 4.0 spec: §6.1.6 + + // reference conversions are possible: + // - if both types are known to be reference types + // - if both types are type parameters and fromType has a class constraint + // (ImplicitTypeParameterConversionWithClassConstraintOnlyOnT) + if (!(fromType.IsReferenceType == true && toType.IsReferenceType != false)) + return false; + + ArrayType fromArray = fromType as ArrayType; + if (fromArray != null) { + ArrayType toArray = toType as ArrayType; + if (toArray != null) { + // array covariance (the broken kind) + return fromArray.Dimensions == toArray.Dimensions + && ImplicitReferenceConversion(fromArray.ElementType, toArray.ElementType, subtypeCheckNestingDepth); + } + // conversion from single-dimensional array S[] to IList: + IType toTypeArgument = UnpackGenericArrayInterface(toType); + if (fromArray.Dimensions == 1 && toTypeArgument != null) { + // array covariance plays a part here as well (string[] is IList) + return IdentityConversion(fromArray.ElementType, toTypeArgument) + || ImplicitReferenceConversion(fromArray.ElementType, toTypeArgument, subtypeCheckNestingDepth); + } + // conversion from any array to System.Array and the interfaces it implements: + IType systemArray = compilation.FindType(KnownTypeCode.Array); + return ImplicitReferenceConversion(systemArray, toType, subtypeCheckNestingDepth); + } + + // now comes the hard part: traverse the inheritance chain and figure out generics+variance + return IsSubtypeOf(fromType, toType, subtypeCheckNestingDepth); + } + + /// + /// For IList{T}, ICollection{T}, IEnumerable{T} and IReadOnlyList{T}, returns T. + /// Otherwise, returns null. + /// + IType UnpackGenericArrayInterface(IType interfaceType) + { + ParameterizedType pt = interfaceType as ParameterizedType; + if (pt != null) { + KnownTypeCode tc = pt.GetDefinition().KnownTypeCode; + if (tc == KnownTypeCode.IListOfT || tc == KnownTypeCode.ICollectionOfT || tc == KnownTypeCode.IEnumerableOfT || tc == KnownTypeCode.IReadOnlyListOfT) { + return pt.GetTypeArgument(0); + } + } + return null; + } + + // Determines whether s is a subtype of t. + // Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion + + bool IsSubtypeOf(IType s, IType t, int subtypeCheckNestingDepth) + { + // conversion to dynamic + object are always possible + if (t.Kind == TypeKind.Dynamic || t.Equals(objectType)) + return true; + if (subtypeCheckNestingDepth > 10) { + // Subtyping in C# is undecidable + // (see "On Decidability of Nominal Subtyping with Variance" by Andrew J. Kennedy and Benjamin C. Pierce), + // so we'll prevent infinite recursions by putting a limit on the nesting depth of variance conversions. + + // No real C# code should use generics nested more than 10 levels deep, and even if they do, most of + // those nestings should not involve variance. + return false; + } + // let GetAllBaseTypes do the work for us + foreach (IType baseType in s.GetAllBaseTypes()) { + if (IdentityOrVarianceConversion(baseType, t, subtypeCheckNestingDepth + 1)) + return true; + } + return false; + } + + bool IdentityOrVarianceConversion(IType s, IType t, int subtypeCheckNestingDepth) + { + ITypeDefinition def = s.GetDefinition(); + if (def != null) { + if (!def.Equals(t.GetDefinition())) + return false; + ParameterizedType ps = s as ParameterizedType; + ParameterizedType pt = t as ParameterizedType; + if (ps != null && pt != null) { + // C# 4.0 spec: §13.1.3.2 Variance Conversion + for (int i = 0; i < def.TypeParameters.Count; i++) { + IType si = ps.GetTypeArgument(i); + IType ti = pt.GetTypeArgument(i); + if (IdentityConversion(si, ti)) + continue; + ITypeParameter xi = def.TypeParameters[i]; + switch (xi.Variance) { + case VarianceModifier.Covariant: + if (!ImplicitReferenceConversion(si, ti, subtypeCheckNestingDepth)) + return false; + break; + case VarianceModifier.Contravariant: + if (!ImplicitReferenceConversion(ti, si, subtypeCheckNestingDepth)) + return false; + break; + default: + return false; + } + } + } else if (ps != null || pt != null) { + return false; // only of of them is parameterized, or counts don't match? -> not valid conversion + } + return true; + } else { + // not type definitions? we still need to check for equal types (e.g. s and t might be type parameters) + return s.Equals(t); + } + } + #endregion + + #region Explicit Reference Conversion + bool ExplicitReferenceConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.2.4 + + // test that the types are reference types: + if (toType.IsReferenceType != true) + return false; + if (fromType.IsReferenceType != true) { + // special case: + // converting from F to T is a reference conversion where T : class, F + // (because F actually must be a reference type as well, even though C# doesn't treat it as one) + if (fromType.Kind == TypeKind.TypeParameter) + return IsSubtypeOf(toType, fromType, 0); + return false; + } + + if (toType.Kind == TypeKind.Array) { + ArrayType toArray = (ArrayType)toType; + if (fromType.Kind == TypeKind.Array) { + // Array covariance + ArrayType fromArray = (ArrayType)fromType; + if (fromArray.Dimensions != toArray.Dimensions) + return false; + return ExplicitReferenceConversion(fromArray.ElementType, toArray.ElementType); + } + IType fromTypeArgument = UnpackGenericArrayInterface(fromType); + if (fromTypeArgument != null && toArray.Dimensions == 1) { + return ExplicitReferenceConversion(fromTypeArgument, toArray.ElementType) + || IdentityConversion(fromTypeArgument, toArray.ElementType); + } + // Otherwise treat the array like a sealed class - require implicit conversion in the opposite direction + return IsImplicitReferenceConversion(toType, fromType); + } else if (fromType.Kind == TypeKind.Array) { + ArrayType fromArray = (ArrayType)fromType; + IType toTypeArgument = UnpackGenericArrayInterface(toType); + if (toTypeArgument != null && fromArray.Dimensions == 1) { + return ExplicitReferenceConversion(fromArray.ElementType, toTypeArgument); + } + // Otherwise treat the array like a sealed class + return IsImplicitReferenceConversion(fromType, toType); + } else if (fromType.Kind == TypeKind.Delegate && toType.Kind == TypeKind.Delegate) { + ITypeDefinition def = fromType.GetDefinition(); + if (def == null || !def.Equals(toType.GetDefinition())) + return false; + ParameterizedType ps = fromType as ParameterizedType; + ParameterizedType pt = toType as ParameterizedType; + if (ps == null || pt == null) { + // non-generic delegate - return true for the identity conversion + return ps == null && pt == null; + } + for (int i = 0; i < def.TypeParameters.Count; i++) { + IType si = ps.GetTypeArgument(i); + IType ti = pt.GetTypeArgument(i); + if (IdentityConversion(si, ti)) + continue; + ITypeParameter xi = def.TypeParameters[i]; + switch (xi.Variance) { + case VarianceModifier.Covariant: + if (!ExplicitReferenceConversion(si, ti)) + return false; + break; + case VarianceModifier.Contravariant: + if (!(si.IsReferenceType == true && ti.IsReferenceType == true)) + return false; + break; + default: + return false; + } + } + return true; + } else if (IsSealedReferenceType(fromType)) { + // If the source type is sealed, explicit conversions can't do anything more than implicit ones + return IsImplicitReferenceConversion(fromType, toType); + } else if (IsSealedReferenceType(toType)) { + // The the target type is sealed, there must be an implicit conversion in the opposite direction + return IsImplicitReferenceConversion(toType, fromType); + } else { + if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface) + return true; + else + return IsImplicitReferenceConversion(toType, fromType) + || IsImplicitReferenceConversion(fromType, toType); + } + } + + bool IsSealedReferenceType(IType type) + { + TypeKind kind = type.Kind; + return kind == TypeKind.Class && type.GetDefinition().IsSealed + || kind == TypeKind.Delegate || kind == TypeKind.Anonymous; + } + #endregion + + #region Boxing Conversions + public bool IsBoxingConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.7 + fromType = NullableType.GetUnderlyingType(fromType); + if (fromType.IsReferenceType == false && toType.IsReferenceType == true) + return IsSubtypeOf(fromType, toType, 0); + else + return false; + } + + bool UnboxingConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.2.5 + toType = NullableType.GetUnderlyingType(toType); + if (fromType.IsReferenceType == true && toType.IsReferenceType == false) + return IsSubtypeOf(toType, fromType, 0); + else + return false; + } + #endregion + + #region Implicit Constant-Expression Conversion + bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) + { + if (rr == null || !rr.IsCompileTimeConstant) + return false; + // C# 4.0 spec: §6.1.9 + TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); + TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType)); + if (fromTypeCode == TypeCode.Int64) { + long val = (long)rr.ConstantValue; + return val >= 0 && toTypeCode == TypeCode.UInt64; + } else if (fromTypeCode == TypeCode.Int32) { + object cv = rr.ConstantValue; + if (cv == null) + return false; + int val = (int)cv; + switch (toTypeCode) { + case TypeCode.SByte: + return val >= SByte.MinValue && val <= SByte.MaxValue; + case TypeCode.Byte: + return val >= Byte.MinValue && val <= Byte.MaxValue; + case TypeCode.Int16: + return val >= Int16.MinValue && val <= Int16.MaxValue; + case TypeCode.UInt16: + return val >= UInt16.MinValue && val <= UInt16.MaxValue; + case TypeCode.UInt32: + return val >= 0; + case TypeCode.UInt64: + return val >= 0; + } + } + return false; + } + #endregion + + #region Conversions involving type parameters + /// + /// Implicit conversions involving type parameters. + /// + bool ImplicitTypeParameterConversion(IType fromType, IType toType) + { + if (fromType.Kind != TypeKind.TypeParameter) + return false; // not a type parameter + if (fromType.IsReferenceType == true) + return false; // already handled by ImplicitReferenceConversion + return IsSubtypeOf(fromType, toType, 0); + } + + Conversion ExplicitTypeParameterConversion(IType fromType, IType toType) + { + if (toType.Kind == TypeKind.TypeParameter) { + // Explicit type parameter conversions that aren't also + // reference conversions are considered to be unboxing conversions + if (fromType.Kind == TypeKind.Interface || IsSubtypeOf(toType, fromType, 0)) + return Conversion.UnboxingConversion; + } else { + if (fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface) + return Conversion.BoxingConversion; + } + return Conversion.None; + } + #endregion + + #region Pointer Conversions + bool ImplicitPointerConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §18.4 Pointer conversions + if (fromType is PointerType && toType is PointerType && toType.ReflectionName == "System.Void*") + return true; + if (fromType.Kind == TypeKind.Null && toType is PointerType) + return true; + return false; + } + + bool ExplicitPointerConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §18.4 Pointer conversions + if (fromType.Kind == TypeKind.Pointer) { + return toType.Kind == TypeKind.Pointer || IsIntegerType(toType); + } else { + return toType.Kind == TypeKind.Pointer && IsIntegerType(fromType); + } + } + + bool IsIntegerType(IType type) + { + TypeCode c = ReflectionHelper.GetTypeCode(type); + return c >= TypeCode.SByte && c <= TypeCode.UInt64; + } + #endregion + + #region User-Defined Conversions + /// + /// Gets whether type A is encompassed by type B. + /// + bool IsEncompassedBy(IType a, IType b) + { + return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface && StandardImplicitConversion(a, b).IsValid; + } + + bool IsEncompassingOrEncompassedBy(IType a, IType b) + { + return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface + && (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid); + } + + IType FindMostEncompassedType(IEnumerable candidates) + { + IType best = null; + foreach (var current in candidates) { + if (best == null || IsEncompassedBy(current, best)) + best = current; + else if (!IsEncompassedBy(best, current)) + return null; // Ambiguous + } + return best; + } + + IType FindMostEncompassingType(IEnumerable candidates) + { + IType best = null; + foreach (var current in candidates) { + if (best == null || IsEncompassedBy(best, current)) + best = current; + else if (!IsEncompassedBy(current, best)) + return null; // Ambiguous + } + return best; + } + + Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList operators, bool isImplicit, IType source, IType target) + { + var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList(); + if (selected.Count == 0) + return Conversion.None; + + if (selected.Count == 1) + return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target)); + + int nNonLifted = selected.Count(s => !s.IsLifted); + if (nNonLifted == 1) { + var op = selected.First(s => !s.IsLifted); + return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target)); + } + + return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target)); + } + + Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType) + { + // C# 4.0 spec §6.4.4 User-defined implicit conversions + var operators = GetApplicableConversionOperators(fromResult, fromType, toType, false); + + if (operators.Count > 0) { + var mostSpecificSource = operators.Any(op => op.SourceType.Equals(fromType)) ? fromType : FindMostEncompassedType(operators.Select(op => op.SourceType)); + if (mostSpecificSource == null) + return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); + var mostSpecificTarget = operators.Any(op => op.TargetType.Equals(toType)) ? toType : FindMostEncompassingType(operators.Select(op => op.TargetType)); + if (mostSpecificTarget == null) { + if (NullableType.IsNullable(toType)) + return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); + else + return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); + } + + var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, true, fromType, toType); + if (selected != Conversion.None) { + if (selected.IsLifted && NullableType.IsNullable(toType)) { + // Prefer A -> B -> B? over A -> A? -> B? + var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); + if (other != Conversion.None) + return other; + } + return selected; + } + else if (NullableType.IsNullable(toType)) + return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); + else + return Conversion.None; + } + else { + return Conversion.None; + } + } + + Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType) + { + // C# 4.0 spec §6.4.5 User-defined implicit conversions + var operators = GetApplicableConversionOperators(fromResult, fromType, toType, true); + if (operators.Count > 0) { + IType mostSpecificSource; + if (operators.Any(op => op.SourceType.Equals(fromType))) { + mostSpecificSource = fromType; + } else { + var operatorsWithSourceEncompassingFromType = operators.Where(op => IsEncompassedBy(fromType, op.SourceType) || ImplicitConstantExpressionConversion(fromResult, NullableType.GetUnderlyingType(op.SourceType))).ToList(); + if (operatorsWithSourceEncompassingFromType.Any()) + mostSpecificSource = FindMostEncompassedType(operatorsWithSourceEncompassingFromType.Select(op => op.SourceType)); + else + mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType)); + } + if (mostSpecificSource == null) + return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); + + IType mostSpecificTarget; + if (operators.Any(op => op.TargetType.Equals(toType))) + mostSpecificTarget = toType; + else if (operators.Any(op => IsEncompassedBy(op.TargetType, toType))) + mostSpecificTarget = FindMostEncompassingType(operators.Where(op => IsEncompassedBy(op.TargetType, toType)).Select(op => op.TargetType)); + else + mostSpecificTarget = FindMostEncompassedType(operators.Select(op => op.TargetType)); + if (mostSpecificTarget == null) { + if (NullableType.IsNullable(toType)) + return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); + else + return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); + } + + var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, false, fromType, toType); + if (selected != Conversion.None) { + if (selected.IsLifted && NullableType.IsNullable(toType)) { + // Prefer A -> B -> B? over A -> A? -> B? + var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); + if (other != Conversion.None) + return other; + } + return selected; + } + else if (NullableType.IsNullable(toType)) + return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); + else if (NullableType.IsNullable(fromType)) + return UserDefinedExplicitConversion(null, NullableType.GetUnderlyingType(fromType), toType); // A? -> A -> B + else + return Conversion.None; + } + else { + return Conversion.None; + } + } + + class OperatorInfo + { + public readonly IMethod Method; + public readonly IType SourceType; + public readonly IType TargetType; + public readonly bool IsLifted; + + public OperatorInfo(IMethod method, IType sourceType, IType targetType, bool isLifted) + { + this.Method = method; + this.SourceType = sourceType; + this.TargetType = targetType; + this.IsLifted = isLifted; + } + } + + List GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit) + { + // Find the candidate operators: + Predicate opFilter; + if (isExplicit) + opFilter = m => m.IsStatic && m.IsOperator && (m.Name == "op_Explicit" || m.Name == "op_Implicit") && m.Parameters.Count == 1; + else + opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1; + + var operators = NullableType.GetUnderlyingType(fromType).GetMethods(opFilter) + .Concat(NullableType.GetUnderlyingType(toType).GetMethods(opFilter)).Distinct(); + // Determine whether one of them is applicable: + List result = new List(); + foreach (IMethod op in operators) { + IType sourceType = op.Parameters[0].Type; + IType targetType = op.ReturnType; + // Try if the operator is applicable: + bool isApplicable; + if (isExplicit) { + isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType)) + && IsEncompassingOrEncompassedBy(targetType, toType); + } else { + isApplicable = (IsEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType)) + && IsEncompassedBy(targetType, toType); + } + // Try if the operator is applicable in lifted form: + if (isApplicable) { + result.Add(new OperatorInfo(op, sourceType, targetType, false)); + } + if (NullableType.IsNonNullableValueType(sourceType)) { + // An operator can be applicable in both lifted and non-lifted form in case of explicit conversions + IType liftedSourceType = NullableType.Create(compilation, sourceType); + IType liftedTargetType = NullableType.IsNonNullableValueType(targetType) ? NullableType.Create(compilation, targetType) : targetType; + if (isExplicit) { + isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType) + && IsEncompassingOrEncompassedBy(liftedTargetType, toType); + } else { + isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType); + } + + if (isApplicable) { + result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true)); + } + } + } + return result; + } + #endregion + + #region AnonymousFunctionConversion + Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType) + { + // C# 5.0 spec §6.5 Anonymous function conversions + LambdaResolveResult f = resolveResult as LambdaResolveResult; + if (f == null) + return Conversion.None; + if (!f.IsAnonymousMethod) { + // It's a lambda, so conversions to expression trees exist + // (even if the conversion leads to a compile-time error, e.g. for statement lambdas) + toType = UnpackExpressionTreeType(toType); + } + IMethod d = toType.GetDelegateInvokeMethod(); + if (d == null) + return Conversion.None; + + IType[] dParamTypes = new IType[d.Parameters.Count]; + for (int i = 0; i < dParamTypes.Length; i++) { + dParamTypes[i] = d.Parameters[i].Type; + } + IType dReturnType = d.ReturnType; + + if (f.HasParameterList) { + // If F contains an anonymous-function-signature, then D and F have the same number of parameters. + if (d.Parameters.Count != f.Parameters.Count) + return Conversion.None; + + if (f.IsImplicitlyTyped) { + // If F has an implicitly typed parameter list, D has no ref or out parameters. + foreach (IParameter p in d.Parameters) { + if (p.IsOut || p.IsRef) + return Conversion.None; + } + } else { + // If F has an explicitly typed parameter list, each parameter in D has the same type + // and modifiers as the corresponding parameter in F. + for (int i = 0; i < f.Parameters.Count; i++) { + IParameter pD = d.Parameters[i]; + IParameter pF = f.Parameters[i]; + if (pD.IsRef != pF.IsRef || pD.IsOut != pF.IsOut) + return Conversion.None; + if (!IdentityConversion(dParamTypes[i], pF.Type)) + return Conversion.None; + } + } + } else { + // If F does not contain an anonymous-function-signature, then D may have zero or more parameters of any + // type, as long as no parameter of D has the out parameter modifier. + foreach (IParameter p in d.Parameters) { + if (p.IsOut) + return Conversion.None; + } + } + + return f.IsValid(dParamTypes, dReturnType, this); + } + + static IType UnpackExpressionTreeType(IType type) + { + ParameterizedType pt = type as ParameterizedType; + if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" && pt.Namespace == "System.Linq.Expressions") { + return pt.GetTypeArgument(0); + } else { + return type; + } + } + #endregion + + #region MethodGroupConversion + Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType) + { + // C# 4.0 spec §6.6 Method group conversions + MethodGroupResolveResult rr = resolveResult as MethodGroupResolveResult; + if (rr == null) + return Conversion.None; + IMethod invoke = toType.GetDelegateInvokeMethod(); + if (invoke == null) + return Conversion.None; + + ResolveResult[] args = new ResolveResult[invoke.Parameters.Count]; + for (int i = 0; i < args.Length; i++) { + IParameter param = invoke.Parameters[i]; + IType parameterType = param.Type; + if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { + parameterType = ((ByReferenceType)parameterType).ElementType; + args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); + } else { + args[i] = new ResolveResult(parameterType); + } + } + var or = rr.PerformOverloadResolution(compilation, args, allowExpandingParams: false, allowOptionalParameters: false, conversions: this); + if (or.FoundApplicableCandidate) { + IMethod method = (IMethod)or.GetBestCandidateWithSubstitutedTypeArguments(); + var thisRR = rr.TargetResult as ThisResolveResult; + bool isVirtual = method.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation); + bool isValid = !or.IsAmbiguous && IsDelegateCompatible(method, invoke, or.IsExtensionMethodInvocation); + bool delegateCapturesFirstArgument = or.IsExtensionMethodInvocation || !method.IsStatic; + if (isValid) + return Conversion.MethodGroupConversion(method, isVirtual, delegateCapturesFirstArgument); + else + return Conversion.InvalidMethodGroupConversion(method, isVirtual, delegateCapturesFirstArgument); + } else { + return Conversion.None; + } + } + + /// + /// Gets whether a is compatible with a delegate type. + /// §15.2 Delegate compatibility + /// + /// The method to test for compatibility + /// The delegate type + public bool IsDelegateCompatible(IMethod method, IType delegateType) + { + if (method == null) + throw new ArgumentNullException("method"); + if (delegateType == null) + throw new ArgumentNullException("delegateType"); + IMethod invoke = delegateType.GetDelegateInvokeMethod(); + if (invoke == null) + return false; + return IsDelegateCompatible(method, invoke, false); + } + + /// + /// Gets whether a method is compatible with a delegate type. + /// §15.2 Delegate compatibility + /// + /// The method to test for compatibility + /// The invoke method of the delegate + /// Gets whether m is accessed using extension method syntax. + /// If this parameter is true, the first parameter of will be ignored. + bool IsDelegateCompatible(IMethod m, IMethod invoke, bool isExtensionMethodInvocation) + { + if (m == null) + throw new ArgumentNullException("m"); + if (invoke == null) + throw new ArgumentNullException("invoke"); + int firstParameterInM = isExtensionMethodInvocation ? 1 : 0; + if (m.Parameters.Count - firstParameterInM != invoke.Parameters.Count) + return false; + for (int i = 0; i < invoke.Parameters.Count; i++) { + var pm = m.Parameters[firstParameterInM + i]; + var pd = invoke.Parameters[i]; + // ret/out must match + if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut) + return false; + if (pm.IsRef || pm.IsOut) { + // ref/out parameters must have same types + if (!pm.Type.Equals(pd.Type)) + return false; + } else { + // non-ref/out parameters must have an identity or reference conversion from pd to pm + if (!IdentityConversion(pd.Type, pm.Type) && !IsImplicitReferenceConversion(pd.Type, pm.Type)) + return false; + } + } + // check return type compatibility + return IdentityConversion(m.ReturnType, invoke.ReturnType) + || IsImplicitReferenceConversion(m.ReturnType, invoke.ReturnType); + } + #endregion + + #region BetterConversion + /// + /// Gets the better conversion (C# 4.0 spec, §7.5.3.3) + /// + /// 0 = neither is better; 1 = t1 is better; 2 = t2 is better + public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2) + { + LambdaResolveResult lambda = resolveResult as LambdaResolveResult; + if (lambda != null) { + if (!lambda.IsAnonymousMethod) { + t1 = UnpackExpressionTreeType(t1); + t2 = UnpackExpressionTreeType(t2); + } + IMethod m1 = t1.GetDelegateInvokeMethod(); + IMethod m2 = t2.GetDelegateInvokeMethod(); + if (m1 == null || m2 == null) + return 0; + if (m1.Parameters.Count != m2.Parameters.Count) + return 0; + IType[] parameterTypes = new IType[m1.Parameters.Count]; + for (int i = 0; i < parameterTypes.Length; i++) { + parameterTypes[i] = m1.Parameters[i].Type; + if (!parameterTypes[i].Equals(m2.Parameters[i].Type)) + return 0; + } + if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count) + return 0; + + IType ret1 = m1.ReturnType; + IType ret2 = m2.ReturnType; + if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void) + return 2; + if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void) + return 1; + + IType inferredRet = lambda.GetInferredReturnType(parameterTypes); + int r = BetterConversion(inferredRet, ret1, ret2); + if (r == 0 && lambda.IsAsync) { + ret1 = UnpackTask(ret1); + ret2 = UnpackTask(ret2); + inferredRet = UnpackTask(inferredRet); + if (ret1 != null && ret2 != null && inferredRet != null) + r = BetterConversion(inferredRet, ret1, ret2); + } + return r; + } else { + return BetterConversion(resolveResult.Type, t1, t2); + } + } + + /// + /// Unpacks the generic Task[T]. Returns null if the input is not Task[T]. + /// + static IType UnpackTask(IType type) + { + ParameterizedType pt = type as ParameterizedType; + if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Task" && pt.Namespace == "System.Threading.Tasks") { + return pt.GetTypeArgument(0); + } + return null; + } + + /// + /// Gets the better conversion (C# 4.0 spec, §7.5.3.4) + /// + /// 0 = neither is better; 1 = t1 is better; 2 = t2 is better + public int BetterConversion(IType s, IType t1, IType t2) + { + bool ident1 = IdentityConversion(s, t1); + bool ident2 = IdentityConversion(s, t2); + if (ident1 && !ident2) + return 1; + if (ident2 && !ident1) + return 2; + return BetterConversionTarget(t1, t2); + } + + /// + /// Gets the better conversion target (C# 4.0 spec, §7.5.3.5) + /// + /// 0 = neither is better; 1 = t1 is better; 2 = t2 is better + int BetterConversionTarget(IType t1, IType t2) + { + bool t1To2 = ImplicitConversion(t1, t2).IsValid; + bool t2To1 = ImplicitConversion(t2, t1).IsValid; + if (t1To2 && !t2To1) + return 1; + if (t2To1 && !t1To2) + return 2; + TypeCode t1Code = ReflectionHelper.GetTypeCode(t1); + TypeCode t2Code = ReflectionHelper.GetTypeCode(t2); + if (IsBetterIntegralType(t1Code, t2Code)) + return 1; + if (IsBetterIntegralType(t2Code, t1Code)) + return 2; + return 0; + } + + bool IsBetterIntegralType(TypeCode t1, TypeCode t2) + { + // signed types are better than unsigned types + switch (t1) { + case TypeCode.SByte: + return t2 == TypeCode.Byte || t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64; + case TypeCode.Int16: + return t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64; + case TypeCode.Int32: + return t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64; + case TypeCode.Int64: + return t2 == TypeCode.UInt64; + default: + return false; + } + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs new file mode 100644 index 000000000..3117fee76 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -0,0 +1,145 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents the result of a method, constructor or indexer invocation. + /// Provides additional C#-specific information for InvocationResolveResult. + /// + public class CSharpInvocationResolveResult : InvocationResolveResult + { + public readonly OverloadResolutionErrors OverloadResolutionErrors; + + /// + /// Gets whether this invocation is calling an extension method using extension method syntax. + /// + public readonly bool IsExtensionMethodInvocation; + + /// + /// Gets whether this invocation is calling a delegate (without explicitly calling ".Invoke()"). + /// + public readonly bool IsDelegateInvocation; + + /// + /// Gets whether a params-Array is being used in its expanded form. + /// + public readonly bool IsExpandedForm; + + readonly IList argumentToParameterMap; + + /// + /// If IsExtensionMethodInvocation is true this property holds the reduced method. + /// + IMethod reducedMethod; + public IMethod ReducedMethod { + get { + if (!IsExtensionMethodInvocation) + return null; + if (reducedMethod == null && Member is IMethod) + reducedMethod = new ReducedExtensionMethod ((IMethod)Member); + return reducedMethod; + } + } + + public CSharpInvocationResolveResult( + ResolveResult targetResult, IParameterizedMember member, + IList arguments, + OverloadResolutionErrors overloadResolutionErrors = OverloadResolutionErrors.None, + bool isExtensionMethodInvocation = false, + bool isExpandedForm = false, + bool isDelegateInvocation = false, + IList argumentToParameterMap = null, + IList initializerStatements = null, + IType returnTypeOverride = null + ) + : base(targetResult, member, arguments, initializerStatements, returnTypeOverride) + { + this.OverloadResolutionErrors = overloadResolutionErrors; + this.IsExtensionMethodInvocation = isExtensionMethodInvocation; + this.IsExpandedForm = isExpandedForm; + this.IsDelegateInvocation = isDelegateInvocation; + this.argumentToParameterMap = argumentToParameterMap; + } + + public override bool IsError { + get { return this.OverloadResolutionErrors != OverloadResolutionErrors.None; } + } + + /// + /// Gets an array that maps argument indices to parameter indices. + /// For arguments that could not be mapped to any parameter, the value will be -1. + /// + /// parameterIndex = ArgumentToParameterMap[argumentIndex] + /// + public IList GetArgumentToParameterMap() + { + return argumentToParameterMap; + } + + public override IList GetArgumentsForCall() + { + ResolveResult[] results = new ResolveResult[Member.Parameters.Count]; + List paramsArguments = IsExpandedForm ? new List() : null; + // map arguments to parameters: + for (int i = 0; i < Arguments.Count; i++) { + int mappedTo; + if (argumentToParameterMap != null) + mappedTo = argumentToParameterMap[i]; + else + mappedTo = IsExpandedForm ? Math.Min(i, results.Length - 1) : i; + + if (mappedTo >= 0 && mappedTo < results.Length) { + if (IsExpandedForm && mappedTo == results.Length - 1) { + paramsArguments.Add(Arguments[i]); + } else { + var narr = Arguments[i] as NamedArgumentResolveResult; + if (narr != null) + results[mappedTo] = narr.Argument; + else + results[mappedTo] = Arguments[i]; + } + } + } + if (IsExpandedForm){ + IType arrayType = Member.Parameters.Last().Type; + IType int32 = Member.Compilation.FindType(KnownTypeCode.Int32); + ResolveResult[] sizeArguments = { new ConstantResolveResult(int32, paramsArguments.Count) }; + results[results.Length - 1] = new ArrayCreateResolveResult(arrayType, sizeArguments, paramsArguments); + } + + for (int i = 0; i < results.Length; i++) { + if (results[i] == null) { + if (Member.Parameters[i].IsOptional) { + results[i] = new ConstantResolveResult(Member.Parameters[i].Type, Member.Parameters[i].ConstantValue); + } else { + results[i] = ErrorResolveResult.UnknownError; + } + } + } + + return results; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs new file mode 100644 index 000000000..64df3a118 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs @@ -0,0 +1,1059 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + sealed class CSharpOperators + { + readonly ICompilation compilation; + + private CSharpOperators(ICompilation compilation) + { + this.compilation = compilation; + InitParameterArrays(); + } + + /// + /// Gets the CSharpOperators instance for the specified . + /// This will make use of the context's cache manager (if available) to reuse the CSharpOperators instance. + /// + public static CSharpOperators Get(ICompilation compilation) + { + CacheManager cache = compilation.CacheManager; + CSharpOperators operators = (CSharpOperators)cache.GetShared(typeof(CSharpOperators)); + if (operators == null) { + operators = (CSharpOperators)cache.GetOrAddShared(typeof(CSharpOperators), new CSharpOperators(compilation)); + } + return operators; + } + + #region class OperatorMethod + OperatorMethod[] Lift(params OperatorMethod[] methods) + { + List result = new List(methods); + foreach (OperatorMethod method in methods) { + OperatorMethod lifted = method.Lift(this); + if (lifted != null) + result.Add(lifted); + } + return result.ToArray(); + } + + IParameter[] normalParameters = new IParameter[(int)(TypeCode.String + 1 - TypeCode.Object)]; + IParameter[] nullableParameters = new IParameter[(int)(TypeCode.Decimal + 1 - TypeCode.Boolean)]; + + void InitParameterArrays() + { + for (TypeCode i = TypeCode.Object; i <= TypeCode.String; i++) { + normalParameters[i - TypeCode.Object] = new DefaultParameter(compilation.FindType(i), string.Empty); + } + for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) { + IType type = NullableType.Create(compilation, compilation.FindType(i)); + nullableParameters[i - TypeCode.Boolean] = new DefaultParameter(type, string.Empty); + } + } + + IParameter MakeParameter(TypeCode code) + { + return normalParameters[code - TypeCode.Object]; + } + + IParameter MakeNullableParameter(IParameter normalParameter) + { + for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) { + if (normalParameter == normalParameters[i - TypeCode.Object]) + return nullableParameters[i - TypeCode.Boolean]; + } + throw new ArgumentException(); + } + + internal class OperatorMethod : IParameterizedMember + { + readonly ICompilation compilation; + readonly IList parameters = new List(); + + protected OperatorMethod(ICompilation compilation) + { + this.compilation = compilation; + } + + public IList Parameters { + get { return parameters; } + } + + public IType ReturnType { get; internal set; } + + public ICompilation Compilation { + get { return compilation; } + } + + public virtual OperatorMethod Lift(CSharpOperators operators) + { + return null; + } + + ITypeDefinition IEntity.DeclaringTypeDefinition { + get { return null; } + } + + IType IEntity.DeclaringType { + get { return null; } + } + + IMember IMember.MemberDefinition { + get { return this; } + } + + IUnresolvedMember IMember.UnresolvedMember { + get { return null; } + } + + IList IMember.ImplementedInterfaceMembers { + get { return EmptyList.Instance; } + } + + bool IMember.IsVirtual { + get { return false; } + } + + bool IMember.IsOverride { + get { return false; } + } + + bool IMember.IsOverridable { + get { return false; } + } + + SymbolKind ISymbol.SymbolKind { + get { return SymbolKind.Operator; } + } + + [Obsolete("Use the SymbolKind property instead.")] + EntityType IEntity.EntityType { + get { return EntityType.Operator; } + } + + DomRegion IEntity.Region { + get { return DomRegion.Empty; } + } + + DomRegion IEntity.BodyRegion { + get { return DomRegion.Empty; } + } + + IList IEntity.Attributes { + get { return EmptyList.Instance; } + } + + Documentation.DocumentationComment IEntity.Documentation { + get { return null; } + } + + Accessibility IHasAccessibility.Accessibility { + get { return Accessibility.Public; } + } + + bool IEntity.IsStatic { + get { return true; } + } + + bool IEntity.IsAbstract { + get { return false; } + } + + bool IEntity.IsSealed { + get { return false; } + } + + bool IEntity.IsShadowing { + get { return false; } + } + + bool IEntity.IsSynthetic { + get { return true; } + } + + bool IHasAccessibility.IsPrivate { + get { return false; } + } + + bool IHasAccessibility.IsPublic { + get { return true; } + } + + bool IHasAccessibility.IsProtected { + get { return false; } + } + + bool IHasAccessibility.IsInternal { + get { return false; } + } + + bool IHasAccessibility.IsProtectedOrInternal { + get { return false; } + } + + bool IHasAccessibility.IsProtectedAndInternal { + get { return false; } + } + + bool IMember.IsExplicitInterfaceImplementation { + get { return false; } + } + + IAssembly IEntity.ParentAssembly { + get { return compilation.MainAssembly; } + } + + IMemberReference IMember.ToMemberReference() + { + throw new NotSupportedException(); + } + + ISymbolReference ISymbol.ToReference() + { + throw new NotSupportedException(); + } + + IMemberReference IMember.ToReference() + { + throw new NotSupportedException(); + } + + TypeParameterSubstitution IMember.Substitution { + get { + return TypeParameterSubstitution.Identity; + } + } + + IMember IMember.Specialize(TypeParameterSubstitution substitution) + { + if (TypeParameterSubstitution.Identity.Equals(substitution)) + return this; + throw new NotSupportedException(); + } + + string INamedElement.FullName { + get { return "operator"; } + } + + public string Name { + get { return "operator"; } + } + + string INamedElement.Namespace { + get { return string.Empty; } + } + + string INamedElement.ReflectionName { + get { return "operator"; } + } + + public override string ToString() + { + StringBuilder b = new StringBuilder(); + b.Append(ReturnType + " operator("); + for (int i = 0; i < parameters.Count; i++) { + if (i > 0) + b.Append(", "); + b.Append(parameters[i].Type); + } + b.Append(')'); + return b.ToString(); + } + } + #endregion + + #region Unary operator class definitions + internal class UnaryOperatorMethod : OperatorMethod + { + public virtual bool CanEvaluateAtCompileTime { get { return false; } } + + public virtual object Invoke(CSharpResolver resolver, object input) + { + throw new NotSupportedException(); + } + + public UnaryOperatorMethod(ICompilation compilaton) : base(compilaton) + { + } + } + + sealed class LambdaUnaryOperatorMethod : UnaryOperatorMethod + { + readonly Func func; + + public LambdaUnaryOperatorMethod(CSharpOperators operators, Func func) + : base(operators.compilation) + { + TypeCode typeCode = Type.GetTypeCode(typeof(T)); + this.ReturnType = operators.compilation.FindType(typeCode); + this.Parameters.Add(operators.MakeParameter(typeCode)); + this.func = func; + } + + public override bool CanEvaluateAtCompileTime { + get { return true; } + } + + public override object Invoke(CSharpResolver resolver, object input) + { + if (input == null) + return null; + return func((T)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T)), input)); + } + + public override OperatorMethod Lift(CSharpOperators operators) + { + return new LiftedUnaryOperatorMethod(operators, this); + } + } + + sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod, OverloadResolution.ILiftedOperator + { + UnaryOperatorMethod baseMethod; + + public LiftedUnaryOperatorMethod(CSharpOperators operators, UnaryOperatorMethod baseMethod) : base(operators.compilation) + { + this.baseMethod = baseMethod; + this.ReturnType = NullableType.Create(baseMethod.Compilation, baseMethod.ReturnType); + this.Parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); + } + + public IList NonLiftedParameters { + get { return baseMethod.Parameters; } + } + } + #endregion + + #region Unary operator definitions + // C# 4.0 spec: §7.7.1 Unary plus operator + OperatorMethod[] unaryPlusOperators; + + public OperatorMethod[] UnaryPlusOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref unaryPlusOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref unaryPlusOperators, Lift( + new LambdaUnaryOperatorMethod (this, i => +i), + new LambdaUnaryOperatorMethod (this, i => +i), + new LambdaUnaryOperatorMethod (this, i => +i), + new LambdaUnaryOperatorMethod (this, i => +i), + new LambdaUnaryOperatorMethod (this, i => +i), + new LambdaUnaryOperatorMethod (this, i => +i), + new LambdaUnaryOperatorMethod(this, i => +i) + )); + } + } + } + + // C# 4.0 spec: §7.7.2 Unary minus operator + OperatorMethod[] uncheckedUnaryMinusOperators; + + public OperatorMethod[] UncheckedUnaryMinusOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref uncheckedUnaryMinusOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref uncheckedUnaryMinusOperators, Lift( + new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), + new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), + new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), + new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), + new LambdaUnaryOperatorMethod(this, i => unchecked(-i)) + )); + } + } + } + + OperatorMethod[] checkedUnaryMinusOperators; + + public OperatorMethod[] CheckedUnaryMinusOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref checkedUnaryMinusOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref checkedUnaryMinusOperators, Lift( + new LambdaUnaryOperatorMethod (this, i => checked(-i)), + new LambdaUnaryOperatorMethod (this, i => checked(-i)), + new LambdaUnaryOperatorMethod (this, i => checked(-i)), + new LambdaUnaryOperatorMethod (this, i => checked(-i)), + new LambdaUnaryOperatorMethod(this, i => checked(-i)) + )); + } + } + } + + // C# 4.0 spec: §7.7.3 Logical negation operator + OperatorMethod[] logicalNegationOperators; + + public OperatorMethod[] LogicalNegationOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref logicalNegationOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref logicalNegationOperators, Lift( + new LambdaUnaryOperatorMethod(this, b => !b) + )); + } + } + } + + // C# 4.0 spec: §7.7.4 Bitwise complement operator + OperatorMethod[] bitwiseComplementOperators; + + public OperatorMethod[] BitwiseComplementOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseComplementOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref bitwiseComplementOperators, Lift( + new LambdaUnaryOperatorMethod (this, i => ~i), + new LambdaUnaryOperatorMethod (this, i => ~i), + new LambdaUnaryOperatorMethod (this, i => ~i), + new LambdaUnaryOperatorMethod(this, i => ~i) + )); + } + } + } + #endregion + + #region Binary operator class definitions + internal class BinaryOperatorMethod : OperatorMethod + { + public virtual bool CanEvaluateAtCompileTime { get { return false; } } + public virtual object Invoke(CSharpResolver resolver, object lhs, object rhs) { + throw new NotSupportedException(); + } + + public BinaryOperatorMethod(ICompilation compilation) : base(compilation) {} + } + + sealed class LambdaBinaryOperatorMethod : BinaryOperatorMethod + { + readonly Func checkedFunc; + readonly Func uncheckedFunc; + + public LambdaBinaryOperatorMethod(CSharpOperators operators, Func func) + : this(operators, func, func) + { + } + + public LambdaBinaryOperatorMethod(CSharpOperators operators, Func checkedFunc, Func uncheckedFunc) + : base(operators.compilation) + { + TypeCode t1 = Type.GetTypeCode(typeof(T1)); + this.ReturnType = operators.compilation.FindType(t1); + this.Parameters.Add(operators.MakeParameter(t1)); + this.Parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2)))); + this.checkedFunc = checkedFunc; + this.uncheckedFunc = uncheckedFunc; + } + + public override bool CanEvaluateAtCompileTime { + get { return true; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + if (lhs == null || rhs == null) + return null; + Func func = resolver.CheckForOverflow ? checkedFunc : uncheckedFunc; + return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs), + (T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs)); + } + + public override OperatorMethod Lift(CSharpOperators operators) + { + return new LiftedBinaryOperatorMethod(operators, this); + } + } + + sealed class LiftedBinaryOperatorMethod : BinaryOperatorMethod, OverloadResolution.ILiftedOperator + { + readonly BinaryOperatorMethod baseMethod; + + public LiftedBinaryOperatorMethod(CSharpOperators operators, BinaryOperatorMethod baseMethod) + : base(operators.compilation) + { + this.baseMethod = baseMethod; + this.ReturnType = NullableType.Create(operators.compilation, baseMethod.ReturnType); + this.Parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); + this.Parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[1])); + } + + public IList NonLiftedParameters { + get { return baseMethod.Parameters; } + } + } + #endregion + + #region Arithmetic operators + // C# 4.0 spec: §7.8.1 Multiplication operator + + OperatorMethod[] multiplicationOperators; + + public OperatorMethod[] MultiplicationOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref multiplicationOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref multiplicationOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)) + )); + } + } + } + + // C# 4.0 spec: §7.8.2 Division operator + OperatorMethod[] divisionOperators; + + public OperatorMethod[] DivisionOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref divisionOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref divisionOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)) + )); + } + } + } + + // C# 4.0 spec: §7.8.3 Remainder operator + OperatorMethod[] remainderOperators; + + public OperatorMethod[] RemainderOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref remainderOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref remainderOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)) + )); + } + } + } + + // C# 4.0 spec: §7.8.3 Addition operator + OperatorMethod[] additionOperators; + + public OperatorMethod[] AdditionOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref additionOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref additionOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), + new StringConcatenation(this, TypeCode.String, TypeCode.String), + new StringConcatenation(this, TypeCode.String, TypeCode.Object), + new StringConcatenation(this, TypeCode.Object, TypeCode.String) + )); + } + } + } + + // not in this list, but handled manually: enum addition, delegate combination + sealed class StringConcatenation : BinaryOperatorMethod + { + bool canEvaluateAtCompileTime; + + public StringConcatenation(CSharpOperators operators, TypeCode p1, TypeCode p2) + : base(operators.compilation) + { + this.canEvaluateAtCompileTime = p1 == TypeCode.String && p2 == TypeCode.String; + this.ReturnType = operators.compilation.FindType(KnownTypeCode.String); + this.Parameters.Add(operators.MakeParameter(p1)); + this.Parameters.Add(operators.MakeParameter(p2)); + } + + public override bool CanEvaluateAtCompileTime { + get { return canEvaluateAtCompileTime; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + return string.Concat(lhs, rhs); + } + } + + // C# 4.0 spec: §7.8.4 Subtraction operator + OperatorMethod[] subtractionOperators; + + public OperatorMethod[] SubtractionOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref subtractionOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref subtractionOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), + new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), + new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)) + )); + } + } + } + + // C# 4.0 spec: §7.8.5 Shift operators + OperatorMethod[] shiftLeftOperators; + + public OperatorMethod[] ShiftLeftOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref shiftLeftOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref shiftLeftOperators, Lift( + new LambdaBinaryOperatorMethod(this, (a, b) => a << b), + new LambdaBinaryOperatorMethod(this, (a, b) => a << b), + new LambdaBinaryOperatorMethod(this, (a, b) => a << b), + new LambdaBinaryOperatorMethod(this, (a, b) => a << b) + )); + } + } + } + + OperatorMethod[] shiftRightOperators; + + public OperatorMethod[] ShiftRightOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref shiftRightOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref shiftRightOperators, Lift( + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b) + )); + } + } + } + #endregion + + #region Equality operators + sealed class EqualityOperatorMethod : BinaryOperatorMethod + { + public readonly TypeCode Type; + public readonly bool Negate; + + public EqualityOperatorMethod(CSharpOperators operators, TypeCode type, bool negate) + : base(operators.compilation) + { + this.Negate = negate; + this.Type = type; + this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); + this.Parameters.Add(operators.MakeParameter(type)); + this.Parameters.Add(operators.MakeParameter(type)); + } + + public override bool CanEvaluateAtCompileTime { + get { return Type != TypeCode.Object; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + if (lhs == null && rhs == null) + return !Negate; // ==: true; !=: false + if (lhs == null || rhs == null) + return Negate; // ==: false; !=: true + lhs = resolver.CSharpPrimitiveCast(Type, lhs); + rhs = resolver.CSharpPrimitiveCast(Type, rhs); + bool equal; + if (Type == TypeCode.Single) { + equal = (float)lhs == (float)rhs; + } else if (Type == TypeCode.Double) { + equal = (double)lhs == (double)rhs; + } else { + equal = object.Equals(lhs, rhs); + } + return equal ^ Negate; + } + + public override OperatorMethod Lift(CSharpOperators operators) + { + if (Type == TypeCode.Object || Type == TypeCode.String) + return null; + else + return new LiftedEqualityOperatorMethod(operators, this); + } + } + + sealed class LiftedEqualityOperatorMethod : BinaryOperatorMethod, OverloadResolution.ILiftedOperator + { + readonly EqualityOperatorMethod baseMethod; + + public LiftedEqualityOperatorMethod(CSharpOperators operators, EqualityOperatorMethod baseMethod) + : base(operators.compilation) + { + this.baseMethod = baseMethod; + this.ReturnType = baseMethod.ReturnType; + IParameter p = operators.MakeNullableParameter(baseMethod.Parameters[0]); + this.Parameters.Add(p); + this.Parameters.Add(p); + } + + public override bool CanEvaluateAtCompileTime { + get { return baseMethod.CanEvaluateAtCompileTime; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + return baseMethod.Invoke(resolver, lhs, rhs); + } + + public IList NonLiftedParameters { + get { return baseMethod.Parameters; } + } + } + + // C# 4.0 spec: §7.10 Relational and type-testing operators + static readonly TypeCode[] valueEqualityOperatorsFor = { + TypeCode.Int32, TypeCode.UInt32, + TypeCode.Int64, TypeCode.UInt64, + TypeCode.Single, TypeCode.Double, + TypeCode.Decimal, + TypeCode.Boolean + }; + + OperatorMethod[] valueEqualityOperators; + + public OperatorMethod[] ValueEqualityOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref valueEqualityOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref valueEqualityOperators, Lift( + valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, false)).ToArray() + )); + } + } + } + + OperatorMethod[] valueInequalityOperators; + + public OperatorMethod[] ValueInequalityOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref valueInequalityOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref valueInequalityOperators, Lift( + valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, true)).ToArray() + )); + } + } + } + + OperatorMethod[] referenceEqualityOperators; + + public OperatorMethod[] ReferenceEqualityOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref referenceEqualityOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref referenceEqualityOperators, Lift( + new EqualityOperatorMethod(this, TypeCode.Object, false), + new EqualityOperatorMethod(this, TypeCode.String, false) + )); + } + } + } + + OperatorMethod[] referenceInequalityOperators; + + public OperatorMethod[] ReferenceInequalityOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref referenceInequalityOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref referenceInequalityOperators, Lift( + new EqualityOperatorMethod(this, TypeCode.Object, true), + new EqualityOperatorMethod(this, TypeCode.String, true) + )); + } + } + } + #endregion + + #region Relational Operators + sealed class RelationalOperatorMethod : BinaryOperatorMethod + { + readonly Func func; + + public RelationalOperatorMethod(CSharpOperators operators, Func func) + : base(operators.compilation) + { + this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); + this.Parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T1)))); + this.Parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2)))); + this.func = func; + } + + public override bool CanEvaluateAtCompileTime { + get { return true; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + if (lhs == null || rhs == null) + return null; + return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs), + (T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs)); + } + + public override OperatorMethod Lift(CSharpOperators operators) + { + var lifted = new LiftedBinaryOperatorMethod(operators, this); + lifted.ReturnType = this.ReturnType; // don't lift the return type for relational operators + return lifted; + } + } + + OperatorMethod[] lessThanOperators; + + public OperatorMethod[] LessThanOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref lessThanOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref lessThanOperators, Lift( + new RelationalOperatorMethod (this, (a, b) => a < b), + new RelationalOperatorMethod (this, (a, b) => a < b), + new RelationalOperatorMethod (this, (a, b) => a < b), + new RelationalOperatorMethod (this, (a, b) => a < b), + new RelationalOperatorMethod (this, (a, b) => a < b), + new RelationalOperatorMethod (this, (a, b) => a < b), + new RelationalOperatorMethod(this, (a, b) => a < b) + )); + } + } + } + + OperatorMethod[] lessThanOrEqualOperators; + + public OperatorMethod[] LessThanOrEqualOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref lessThanOrEqualOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref lessThanOrEqualOperators, Lift( + new RelationalOperatorMethod (this, (a, b) => a <= b), + new RelationalOperatorMethod (this, (a, b) => a <= b), + new RelationalOperatorMethod (this, (a, b) => a <= b), + new RelationalOperatorMethod (this, (a, b) => a <= b), + new RelationalOperatorMethod (this, (a, b) => a <= b), + new RelationalOperatorMethod (this, (a, b) => a <= b), + new RelationalOperatorMethod(this, (a, b) => a <= b) + )); + } + } + } + + OperatorMethod[] greaterThanOperators; + + public OperatorMethod[] GreaterThanOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref greaterThanOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref greaterThanOperators, Lift( + new RelationalOperatorMethod (this, (a, b) => a > b), + new RelationalOperatorMethod (this, (a, b) => a > b), + new RelationalOperatorMethod (this, (a, b) => a > b), + new RelationalOperatorMethod (this, (a, b) => a > b), + new RelationalOperatorMethod (this, (a, b) => a > b), + new RelationalOperatorMethod (this, (a, b) => a > b), + new RelationalOperatorMethod(this, (a, b) => a > b) + )); + } + } + } + + OperatorMethod[] greaterThanOrEqualOperators; + + public OperatorMethod[] GreaterThanOrEqualOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref greaterThanOrEqualOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref greaterThanOrEqualOperators, Lift( + new RelationalOperatorMethod (this, (a, b) => a >= b), + new RelationalOperatorMethod (this, (a, b) => a >= b), + new RelationalOperatorMethod (this, (a, b) => a >= b), + new RelationalOperatorMethod (this, (a, b) => a >= b), + new RelationalOperatorMethod (this, (a, b) => a >= b), + new RelationalOperatorMethod (this, (a, b) => a >= b), + new RelationalOperatorMethod(this, (a, b) => a >= b) + )); + } + } + } + #endregion + + #region Bitwise operators + OperatorMethod[] logicalAndOperators; + + public OperatorMethod[] LogicalAndOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref logicalAndOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref logicalAndOperators, new OperatorMethod[] { + new LambdaBinaryOperatorMethod(this, (a, b) => a & b) + }); + } + } + } + + + OperatorMethod[] bitwiseAndOperators; + + public OperatorMethod[] BitwiseAndOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseAndOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref bitwiseAndOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => a & b), + new LambdaBinaryOperatorMethod (this, (a, b) => a & b), + new LambdaBinaryOperatorMethod (this, (a, b) => a & b), + new LambdaBinaryOperatorMethod(this, (a, b) => a & b), + this.LogicalAndOperators[0] + )); + } + } + } + + + OperatorMethod[] logicalOrOperators; + + public OperatorMethod[] LogicalOrOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref logicalOrOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref logicalOrOperators, new OperatorMethod[] { + new LambdaBinaryOperatorMethod(this, (a, b) => a | b) + }); + } + } + } + + OperatorMethod[] bitwiseOrOperators; + + public OperatorMethod[] BitwiseOrOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseOrOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref bitwiseOrOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => a | b), + new LambdaBinaryOperatorMethod (this, (a, b) => a | b), + new LambdaBinaryOperatorMethod (this, (a, b) => a | b), + new LambdaBinaryOperatorMethod(this, (a, b) => a | b), + this.LogicalOrOperators[0] + )); + } + } + } + + // Note: the logic for the lifted bool? bitwise operators is wrong; + // we produce "true | null" = "null" when it should be true. However, this is irrelevant + // because bool? cannot be a compile-time type. + + OperatorMethod[] bitwiseXorOperators; + + public OperatorMethod[] BitwiseXorOperators { + get { + OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseXorOperators); + if (ops != null) { + return ops; + } else { + return LazyInit.GetOrSet(ref bitwiseXorOperators, Lift( + new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b), + new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b), + new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b), + new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b), + new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b) + )); + } + } + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs new file mode 100644 index 000000000..4072f276b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -0,0 +1,2614 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Contains the main resolver logic. + /// + /// + /// This class is thread-safe. + /// + public class CSharpResolver : ICodeContext + { + static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError; + readonly ICompilation compilation; + internal readonly CSharpConversions conversions; + readonly CSharpTypeResolveContext context; + readonly bool checkForOverflow; + readonly bool isWithinLambdaExpression; + + #region Constructor + public CSharpResolver(ICompilation compilation) + { + if (compilation == null) + throw new ArgumentNullException("compilation"); + this.compilation = compilation; + this.conversions = CSharpConversions.Get(compilation); + this.context = new CSharpTypeResolveContext(compilation.MainAssembly); + } + + public CSharpResolver(CSharpTypeResolveContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.compilation = context.Compilation; + this.conversions = CSharpConversions.Get(compilation); + this.context = context; + if (context.CurrentTypeDefinition != null) + currentTypeDefinitionCache = new TypeDefinitionCache(context.CurrentTypeDefinition); + } + + private CSharpResolver(ICompilation compilation, CSharpConversions conversions, CSharpTypeResolveContext context, bool checkForOverflow, bool isWithinLambdaExpression, TypeDefinitionCache currentTypeDefinitionCache, ImmutableStack localVariableStack, ObjectInitializerContext objectInitializerStack) + { + this.compilation = compilation; + this.conversions = conversions; + this.context = context; + this.checkForOverflow = checkForOverflow; + this.isWithinLambdaExpression = isWithinLambdaExpression; + this.currentTypeDefinitionCache = currentTypeDefinitionCache; + this.localVariableStack = localVariableStack; + this.objectInitializerStack = objectInitializerStack; + } + #endregion + + #region Properties + /// + /// Gets the compilation used by the resolver. + /// + public ICompilation Compilation { + get { return compilation; } + } + + /// + /// Gets the current type resolve context. + /// + public CSharpTypeResolveContext CurrentTypeResolveContext { + get { return context; } + } + + IAssembly ITypeResolveContext.CurrentAssembly { + get { return context.CurrentAssembly; } + } + + CSharpResolver WithContext(CSharpTypeResolveContext newContext) + { + return new CSharpResolver(compilation, conversions, newContext, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack); + } + + /// + /// Gets whether the current context is checked. + /// + public bool CheckForOverflow { + get { return checkForOverflow; } + } + + /// + /// Sets whether the current context is checked. + /// + public CSharpResolver WithCheckForOverflow(bool checkForOverflow) + { + if (checkForOverflow == this.checkForOverflow) + return this; + return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack); + } + + /// + /// Gets whether the resolver is currently within a lambda expression or anonymous method. + /// + public bool IsWithinLambdaExpression { + get { return isWithinLambdaExpression; } + } + + /// + /// Sets whether the resolver is currently within a lambda expression. + /// + public CSharpResolver WithIsWithinLambdaExpression(bool isWithinLambdaExpression) + { + return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack); + } + + /// + /// Gets the current member definition that is used to look up identifiers as parameters + /// or type parameters. + /// + public IMember CurrentMember { + get { return context.CurrentMember; } + } + + /// + /// Sets the current member definition. + /// + /// Don't forget to also set CurrentTypeDefinition when setting CurrentMember; + /// setting one of the properties does not automatically set the other. + public CSharpResolver WithCurrentMember(IMember member) + { + return WithContext(context.WithCurrentMember(member)); + } + + ITypeResolveContext ITypeResolveContext.WithCurrentMember(IMember member) + { + return WithCurrentMember(member); + } + + /// + /// Gets the current using scope that is used to look up identifiers as class names. + /// + public ResolvedUsingScope CurrentUsingScope { + get { return context.CurrentUsingScope; } + } + + /// + /// Sets the current using scope that is used to look up identifiers as class names. + /// + public CSharpResolver WithCurrentUsingScope(ResolvedUsingScope usingScope) + { + return WithContext(context.WithUsingScope(usingScope)); + } + #endregion + + #region Per-CurrentTypeDefinition Cache + readonly TypeDefinitionCache currentTypeDefinitionCache; + + /// + /// Gets the current type definition. + /// + public ITypeDefinition CurrentTypeDefinition { + get { return context.CurrentTypeDefinition; } + } + + /// + /// Sets the current type definition. + /// + public CSharpResolver WithCurrentTypeDefinition(ITypeDefinition typeDefinition) + { + if (this.CurrentTypeDefinition == typeDefinition) + return this; + + TypeDefinitionCache newTypeDefinitionCache; + if (typeDefinition != null) + newTypeDefinitionCache = new TypeDefinitionCache(typeDefinition); + else + newTypeDefinitionCache = null; + + return new CSharpResolver(compilation, conversions, context.WithCurrentTypeDefinition(typeDefinition), + checkForOverflow, isWithinLambdaExpression, newTypeDefinitionCache, localVariableStack, objectInitializerStack); + } + + ITypeResolveContext ITypeResolveContext.WithCurrentTypeDefinition(ITypeDefinition typeDefinition) + { + return WithCurrentTypeDefinition(typeDefinition); + } + + sealed class TypeDefinitionCache + { + public readonly ITypeDefinition TypeDefinition; + public readonly Dictionary SimpleNameLookupCacheExpression = new Dictionary(); + public readonly Dictionary SimpleNameLookupCacheInvocationTarget = new Dictionary(); + public readonly Dictionary SimpleTypeLookupCache = new Dictionary(); + + public TypeDefinitionCache(ITypeDefinition typeDefinition) + { + this.TypeDefinition = typeDefinition; + } + } + #endregion + + #region Local Variable Management + + // We store the local variables in an immutable stack. + // The beginning of a block is marked by a null entry. + + // This data structure is used to allow efficient cloning of the resolver with its local variable context. + readonly ImmutableStack localVariableStack = ImmutableStack.Empty; + + CSharpResolver WithLocalVariableStack(ImmutableStack stack) + { + return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, stack, objectInitializerStack); + } + + /// + /// Opens a new scope for local variables. + /// + public CSharpResolver PushBlock() + { + return WithLocalVariableStack(localVariableStack.Push(null)); + } + + /// + /// Closes the current scope for local variables; removing all variables in that scope. + /// + public CSharpResolver PopBlock() + { + var stack = localVariableStack; + IVariable removedVar; + do { + removedVar = stack.Peek(); + stack = stack.Pop(); + } while (removedVar != null); + return WithLocalVariableStack(stack); + } + + /// + /// Adds a new variable or lambda parameter to the current block. + /// + public CSharpResolver AddVariable(IVariable variable) + { + if (variable == null) + throw new ArgumentNullException("variable"); + return WithLocalVariableStack(localVariableStack.Push(variable)); + } + + /// + /// Removes the variable that was just added. + /// + public CSharpResolver PopLastVariable() + { + if (localVariableStack.Peek() == null) + throw new InvalidOperationException("There is no variable within the current block."); + return WithLocalVariableStack(localVariableStack.Pop()); + } + + /// + /// Gets all currently visible local variables and lambda parameters. + /// Does not include method parameters. + /// + public IEnumerable LocalVariables { + get { + return localVariableStack.Where(v => v != null); + } + } + #endregion + + #region Object Initializer Context + sealed class ObjectInitializerContext + { + internal readonly ResolveResult initializedObject; + internal readonly ObjectInitializerContext prev; + + public ObjectInitializerContext(ResolveResult initializedObject, CSharpResolver.ObjectInitializerContext prev) + { + this.initializedObject = initializedObject; + this.prev = prev; + } + } + + readonly ObjectInitializerContext objectInitializerStack; + + CSharpResolver WithObjectInitializerStack(ObjectInitializerContext stack) + { + return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, stack); + } + + /// + /// Pushes the type of the object that is currently being initialized. + /// + public CSharpResolver PushObjectInitializer(ResolveResult initializedObject) + { + if (initializedObject == null) + throw new ArgumentNullException("initializedObject"); + return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack)); + } + + public CSharpResolver PopObjectInitializer() + { + if (objectInitializerStack == null) + throw new InvalidOperationException(); + return WithObjectInitializerStack(objectInitializerStack.prev); + } + + /// + /// Gets whether this context is within an object initializer. + /// + public bool IsInObjectInitializer { + get { return objectInitializerStack != null; } + } + + /// + /// Gets the current object initializer. This usually is an + /// or (for nested initializers) a semantic tree based on an . + /// Returns ErrorResolveResult if there is no object initializer. + /// + public ResolveResult CurrentObjectInitializer { + get { + return objectInitializerStack != null ? objectInitializerStack.initializedObject : ErrorResult; + } + } + + /// + /// Gets the type of the object currently being initialized. + /// Returns SharedTypes.Unknown if no object initializer is currently open (or if the object initializer + /// has unknown type). + /// + public IType CurrentObjectInitializerType { + get { return CurrentObjectInitializer.Type; } + } + #endregion + + #region Clone + /// + /// Creates a copy of this CSharp resolver. + /// + [Obsolete("CSharpResolver is immutable, cloning is no longer necessary")] + public CSharpResolver Clone() + { + return this; + } + #endregion + + #region ResolveUnaryOperator + #region ResolveUnaryOperator method + public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression) + { + if (expression.Type.Kind == TypeKind.Dynamic) { + if (op == UnaryOperatorType.Await) { + return new AwaitResolveResult(SpecialType.Dynamic, new DynamicInvocationResolveResult(new DynamicMemberResolveResult(expression, "GetAwaiter"), DynamicInvocationType.Invocation, EmptyList.Instance), SpecialType.Dynamic, null, null, null); + } + else { + return UnaryOperatorResolveResult(SpecialType.Dynamic, op, expression); + } + } + + // C# 4.0 spec: §7.3.3 Unary operator overload resolution + string overloadableOperatorName = GetOverloadableOperatorName(op); + if (overloadableOperatorName == null) { + switch (op) { + case UnaryOperatorType.Dereference: + PointerType p = expression.Type as PointerType; + if (p != null) + return UnaryOperatorResolveResult(p.ElementType, op, expression); + else + return ErrorResult; + case UnaryOperatorType.AddressOf: + return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression); + case UnaryOperatorType.Await: { + ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList.Instance, NameLookupMode.InvocationTarget); + ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, new ResolveResult[0], argumentNames: null, allowOptionalParameters: false); + + var lookup = CreateMemberLookup(); + IMethod getResultMethod; + IType awaitResultType; + var getResultMethodGroup = lookup.Lookup(getAwaiterInvocation, "GetResult", EmptyList.Instance, true) as MethodGroupResolveResult; + if (getResultMethodGroup != null) { + var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, new ResolveResult[0], allowExtensionMethods: false, conversions: conversions); + getResultMethod = getResultOR.FoundApplicableCandidate ? getResultOR.GetBestCandidateWithSubstitutedTypeArguments() as IMethod : null; + awaitResultType = getResultMethod != null ? getResultMethod.ReturnType : SpecialType.UnknownType; + } + else { + getResultMethod = null; + awaitResultType = SpecialType.UnknownType; + } + + var isCompletedRR = lookup.Lookup(getAwaiterInvocation, "IsCompleted", EmptyList.Instance, false); + var isCompletedProperty = (isCompletedRR is MemberResolveResult ? ((MemberResolveResult)isCompletedRR).Member as IProperty : null); + if (isCompletedProperty != null && (!isCompletedProperty.ReturnType.IsKnownType(KnownTypeCode.Boolean) || !isCompletedProperty.CanGet)) + isCompletedProperty = null; + + var interfaceOnCompleted = compilation.FindType(KnownTypeCode.INotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "OnCompleted"); + var interfaceUnsafeOnCompleted = compilation.FindType(KnownTypeCode.ICriticalNotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "UnsafeOnCompleted"); + + IMethod onCompletedMethod = null; + var candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceUnsafeOnCompleted)).ToList(); + if (candidates.Count == 0) { + candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceOnCompleted)).ToList(); + if (candidates.Count == 1) + onCompletedMethod = candidates[0]; + } + else if (candidates.Count == 1) { + onCompletedMethod = candidates[0]; + } + + return new AwaitResolveResult(awaitResultType, getAwaiterInvocation, getAwaiterInvocation.Type, isCompletedProperty, onCompletedMethod, getResultMethod); + } + + default: + return ErrorResolveResult.UnknownError; + } + } + // If the type is nullable, get the underlying type: + IType type = NullableType.GetUnderlyingType(expression.Type); + bool isNullable = NullableType.IsNullable(expression.Type); + + // the operator is overloadable: + OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { expression }); + foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) { + userDefinedOperatorOR.AddCandidate(candidate); + } + if (userDefinedOperatorOR.FoundApplicableCandidate) { + return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); + } + + expression = UnaryNumericPromotion(op, ref type, isNullable, expression); + CSharpOperators.OperatorMethod[] methodGroup; + CSharpOperators operators = CSharpOperators.Get(compilation); + switch (op) { + case UnaryOperatorType.Increment: + case UnaryOperatorType.Decrement: + case UnaryOperatorType.PostIncrement: + case UnaryOperatorType.PostDecrement: + // C# 4.0 spec: §7.6.9 Postfix increment and decrement operators + // C# 4.0 spec: §7.7.5 Prefix increment and decrement operators + TypeCode code = ReflectionHelper.GetTypeCode(type); + if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer) + return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); + else + return new ErrorResolveResult(expression.Type); + case UnaryOperatorType.Plus: + methodGroup = operators.UnaryPlusOperators; + break; + case UnaryOperatorType.Minus: + methodGroup = CheckForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators; + break; + case UnaryOperatorType.Not: + methodGroup = operators.LogicalNegationOperators; + break; + case UnaryOperatorType.BitNot: + if (type.Kind == TypeKind.Enum) { + if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null) { + // evaluate as (E)(~(U)x); + var U = compilation.FindType(expression.ConstantValue.GetType()); + var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue); + var rr = ResolveUnaryOperator(op, unpackedEnum); + rr = WithCheckForOverflow(false).ResolveCast(type, rr); + if (rr.IsCompileTimeConstant) + return rr; + } + return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); + } else { + methodGroup = operators.BitwiseComplementOperators; + break; + } + default: + throw new InvalidOperationException(); + } + OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { expression }); + foreach (var candidate in methodGroup) { + builtinOperatorOR.AddCandidate(candidate); + } + CSharpOperators.UnaryOperatorMethod m = (CSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate; + IType resultType = m.ReturnType; + if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) { + if (userDefinedOperatorOR.BestCandidate != null) { + // If there are any user-defined operators, prefer those over the built-in operators. + // It'll be a more informative error. + return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); + } else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) { + // If the best candidate is ambiguous, just use the input type instead + // of picking one of the ambiguous overloads. + return new ErrorResolveResult(expression.Type); + } else { + return new ErrorResolveResult(resultType); + } + } else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) { + object val; + try { + val = m.Invoke(this, expression.ConstantValue); + } catch (ArithmeticException) { + return new ErrorResolveResult(resultType); + } + return new ConstantResolveResult(resultType, val); + } else { + expression = Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]); + return UnaryOperatorResolveResult(resultType, op, expression, + builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator); + } + } + + OperatorResolveResult UnaryOperatorResolveResult(IType resultType, UnaryOperatorType op, ResolveResult expression, bool isLifted = false) + { + return new OperatorResolveResult( + resultType, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow), + null, isLifted, new[] { expression }); + } + #endregion + + #region UnaryNumericPromotion + ResolveResult UnaryNumericPromotion(UnaryOperatorType op, ref IType type, bool isNullable, ResolveResult expression) + { + // C# 4.0 spec: §7.3.6.1 + TypeCode code = ReflectionHelper.GetTypeCode(type); + if (isNullable && type.Kind == TypeKind.Null) + code = TypeCode.SByte; // cause promotion of null to int32 + switch (op) { + case UnaryOperatorType.Minus: + if (code == TypeCode.UInt32) { + type = compilation.FindType(KnownTypeCode.Int64); + return Convert(expression, MakeNullable(type, isNullable), + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); + } + goto case UnaryOperatorType.Plus; + case UnaryOperatorType.Plus: + case UnaryOperatorType.BitNot: + if (code >= TypeCode.Char && code <= TypeCode.UInt16) { + type = compilation.FindType(KnownTypeCode.Int32); + return Convert(expression, MakeNullable(type, isNullable), + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); + } + break; + } + return expression; + } + #endregion + + #region GetOverloadableOperatorName + static string GetOverloadableOperatorName(UnaryOperatorType op) + { + switch (op) { + case UnaryOperatorType.Not: + return "op_LogicalNot"; + case UnaryOperatorType.BitNot: + return "op_OnesComplement"; + case UnaryOperatorType.Minus: + return "op_UnaryNegation"; + case UnaryOperatorType.Plus: + return "op_UnaryPlus"; + case UnaryOperatorType.Increment: + case UnaryOperatorType.PostIncrement: + return "op_Increment"; + case UnaryOperatorType.Decrement: + case UnaryOperatorType.PostDecrement: + return "op_Decrement"; + default: + return null; + } + } + #endregion + #endregion + + #region ResolveBinaryOperator + #region ResolveBinaryOperator method + public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs) + { + if (lhs.Type.Kind == TypeKind.Dynamic || rhs.Type.Kind == TypeKind.Dynamic) { + lhs = Convert(lhs, SpecialType.Dynamic); + rhs = Convert(rhs, SpecialType.Dynamic); + return BinaryOperatorResolveResult(SpecialType.Dynamic, lhs, op, rhs); + } + + // C# 4.0 spec: §7.3.4 Binary operator overload resolution + string overloadableOperatorName = GetOverloadableOperatorName(op); + if (overloadableOperatorName == null) { + + // Handle logical and/or exactly as bitwise and/or: + // - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator. + // - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit. + // - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit + if (op == BinaryOperatorType.ConditionalAnd) { + overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd); + } else if (op == BinaryOperatorType.ConditionalOr) { + overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr); + } else if (op == BinaryOperatorType.NullCoalescing) { + // null coalescing operator is not overloadable and needs to be handled separately + return ResolveNullCoalescingOperator(lhs, rhs); + } else { + return ErrorResolveResult.UnknownError; + } + } + + // If the type is nullable, get the underlying type: + bool isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type); + IType lhsType = NullableType.GetUnderlyingType(lhs.Type); + IType rhsType = NullableType.GetUnderlyingType(rhs.Type); + + // the operator is overloadable: + OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { lhs, rhs }); + HashSet userOperatorCandidates = new HashSet(); + userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName)); + userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName)); + foreach (var candidate in userOperatorCandidates) { + userDefinedOperatorOR.AddCandidate(candidate); + } + if (userDefinedOperatorOR.FoundApplicableCandidate) { + return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); + } + + if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false + || lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null) + { + isNullable = true; + } + if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) { + // special case: the shift operators allow "var x = null << null", producing int?. + if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) + isNullable = true; + // for shift operators, do unary promotion independently on both arguments + lhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref lhsType, isNullable, lhs); + rhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref rhsType, isNullable, rhs); + } else { + bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality; + if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs, allowNullableConstants)) + return new ErrorResolveResult(lhs.Type); + } + // re-read underlying types after numeric promotion + lhsType = NullableType.GetUnderlyingType(lhs.Type); + rhsType = NullableType.GetUnderlyingType(rhs.Type); + + IEnumerable methodGroup; + CSharpOperators operators = CSharpOperators.Get(compilation); + switch (op) { + case BinaryOperatorType.Multiply: + methodGroup = operators.MultiplicationOperators; + break; + case BinaryOperatorType.Divide: + methodGroup = operators.DivisionOperators; + break; + case BinaryOperatorType.Modulus: + methodGroup = operators.RemainderOperators; + break; + case BinaryOperatorType.Add: + methodGroup = operators.AdditionOperators; + { + if (lhsType.Kind == TypeKind.Enum) { + // E operator +(E x, U y); + IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable); + if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) { + return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); + } + } + if (rhsType.Kind == TypeKind.Enum) { + // E operator +(U x, E y); + IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable); + if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) { + return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); + } + } + + if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) { + return BinaryOperatorResolveResult(lhsType, lhs, op, rhs); + } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) { + return BinaryOperatorResolveResult(rhsType, lhs, op, rhs); + } + + if (lhsType is PointerType) { + methodGroup = new [] { + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32), + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32), + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64), + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64) + }; + } else if (rhsType is PointerType) { + methodGroup = new [] { + PointerArithmeticOperator(rhsType, KnownTypeCode.Int32, rhsType), + PointerArithmeticOperator(rhsType, KnownTypeCode.UInt32, rhsType), + PointerArithmeticOperator(rhsType, KnownTypeCode.Int64, rhsType), + PointerArithmeticOperator(rhsType, KnownTypeCode.UInt64, rhsType) + }; + } + if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) + return new ErrorResolveResult(SpecialType.NullType); + } + break; + case BinaryOperatorType.Subtract: + methodGroup = operators.SubtractionOperators; + { + if (lhsType.Kind == TypeKind.Enum) { + // U operator –(E x, E y); + if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs, allowConversionFromConstantZero: false)) { + return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs); + } + + // E operator –(E x, U y); + IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable); + if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) { + return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); + } + } + if (rhsType.Kind == TypeKind.Enum) { + // U operator –(E x, E y); + if (TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs)) { + return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs); + } + + // E operator -(U x, E y); + IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable); + if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) { + return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); + } + } + + if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) { + return BinaryOperatorResolveResult(lhsType, lhs, op, rhs); + } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) { + return BinaryOperatorResolveResult(rhsType, lhs, op, rhs); + } + + if (lhsType is PointerType) { + if (rhsType is PointerType) { + IType int64 = compilation.FindType(KnownTypeCode.Int64); + if (lhsType.Equals(rhsType)) { + return BinaryOperatorResolveResult(int64, lhs, op, rhs); + } else { + return new ErrorResolveResult(int64); + } + } + methodGroup = new [] { + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32), + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32), + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64), + PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64) + }; + } + + if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) + return new ErrorResolveResult(SpecialType.NullType); + } + break; + case BinaryOperatorType.ShiftLeft: + methodGroup = operators.ShiftLeftOperators; + break; + case BinaryOperatorType.ShiftRight: + methodGroup = operators.ShiftRightOperators; + break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.GreaterThanOrEqual: + { + if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) { + // bool operator op(E x, E y); + return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs); + } else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) { + // bool operator op(E x, E y); + return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs); + } else if (lhsType is PointerType && rhsType is PointerType) { + return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs); + } + if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) { + if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true) { + // If it's a reference comparison + if (op == BinaryOperatorType.Equality) + methodGroup = operators.ReferenceEqualityOperators; + else + methodGroup = operators.ReferenceInequalityOperators; + break; + } else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type) + || IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null) { + // compare type parameter or nullable type with the null literal + return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs); + } + } + switch (op) { + case BinaryOperatorType.Equality: + methodGroup = operators.ValueEqualityOperators; + break; + case BinaryOperatorType.InEquality: + methodGroup = operators.ValueInequalityOperators; + break; + case BinaryOperatorType.LessThan: + methodGroup = operators.LessThanOperators; + break; + case BinaryOperatorType.GreaterThan: + methodGroup = operators.GreaterThanOperators; + break; + case BinaryOperatorType.LessThanOrEqual: + methodGroup = operators.LessThanOrEqualOperators; + break; + case BinaryOperatorType.GreaterThanOrEqual: + methodGroup = operators.GreaterThanOrEqualOperators; + break; + default: + throw new InvalidOperationException(); + } + } + break; + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + { + if (lhsType.Kind == TypeKind.Enum) { + // bool operator op(E x, E y); + if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs)) { + return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); + } + } + + if (rhsType.Kind == TypeKind.Enum) { + // bool operator op(E x, E y); + if (TryConvertEnum (ref lhs, rhs.Type, ref isNullable, ref rhs)) { + return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); + } + } + + switch (op) { + case BinaryOperatorType.BitwiseAnd: + methodGroup = operators.BitwiseAndOperators; + break; + case BinaryOperatorType.BitwiseOr: + methodGroup = operators.BitwiseOrOperators; + break; + case BinaryOperatorType.ExclusiveOr: + methodGroup = operators.BitwiseXorOperators; + break; + default: + throw new InvalidOperationException(); + } + } + break; + case BinaryOperatorType.ConditionalAnd: + methodGroup = operators.LogicalAndOperators; + break; + case BinaryOperatorType.ConditionalOr: + methodGroup = operators.LogicalOrOperators; + break; + default: + throw new InvalidOperationException(); + } + OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs }); + foreach (var candidate in methodGroup) { + builtinOperatorOR.AddCandidate(candidate); + } + CSharpOperators.BinaryOperatorMethod m = (CSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate; + IType resultType = m.ReturnType; + if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) { + // If there are any user-defined operators, prefer those over the built-in operators. + // It'll be a more informative error. + if (userDefinedOperatorOR.BestCandidate != null) + return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); + else + return new ErrorResolveResult(resultType); + } else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) { + object val; + try { + val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue); + } catch (ArithmeticException) { + return new ErrorResolveResult(resultType); + } + return new ConstantResolveResult(resultType, val); + } else { + lhs = Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]); + rhs = Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]); + return BinaryOperatorResolveResult(resultType, lhs, op, rhs, + builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator); + } + } + + bool IsNullableTypeOrNonValueType(IType type) + { + return NullableType.IsNullable(type) || type.IsReferenceType != false; + } + + ResolveResult BinaryOperatorResolveResult(IType resultType, ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, bool isLifted = false) + { + return new OperatorResolveResult( + resultType, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow), + null, isLifted, new[] { lhs, rhs }); + } + #endregion + + #region Pointer arithmetic + CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, KnownTypeCode inputType2) + { + return PointerArithmeticOperator(resultType, inputType1, compilation.FindType(inputType2)); + } + + CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, KnownTypeCode inputType1, IType inputType2) + { + return PointerArithmeticOperator(resultType, compilation.FindType(inputType1), inputType2); + } + + CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, IType inputType2) + { + return new CSharpOperators.BinaryOperatorMethod(compilation) { + ReturnType = resultType, + Parameters = { + new DefaultParameter(inputType1, string.Empty), + new DefaultParameter(inputType2, string.Empty) + } + }; + } + #endregion + + #region Enum helper methods + IType GetEnumUnderlyingType(IType enumType) + { + ITypeDefinition def = enumType.GetDefinition(); + return def != null ? def.EnumUnderlyingType : SpecialType.UnknownType; + } + + /// + /// Handle the case where an enum value is compared with another enum value + /// bool operator op(E x, E y); + /// + ResolveResult HandleEnumComparison(BinaryOperatorType op, IType enumType, bool isNullable, ResolveResult lhs, ResolveResult rhs) + { + // evaluate as ((U)x op (U)y) + IType elementType = GetEnumUnderlyingType(enumType); + if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum) { + var rr = ResolveBinaryOperator(op, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs)); + if (rr.IsCompileTimeConstant) + return rr; + } + IType resultType = compilation.FindType(KnownTypeCode.Boolean); + return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable); + } + + /// + /// Handle the case where an enum value is subtracted from another enum value + /// U operator –(E x, E y); + /// + ResolveResult HandleEnumSubtraction(bool isNullable, IType enumType, ResolveResult lhs, ResolveResult rhs) + { + // evaluate as (U)((U)x – (U)y) + IType elementType = GetEnumUnderlyingType(enumType); + if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum) { + var rr = ResolveBinaryOperator(BinaryOperatorType.Subtract, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs)); + rr = WithCheckForOverflow(false).ResolveCast(elementType, rr); + if (rr.IsCompileTimeConstant) + return rr; + } + IType resultType = MakeNullable(elementType, isNullable); + return BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs, isNullable); + } + + /// + /// Handle the following enum operators: + /// E operator +(E x, U y); + /// E operator +(U x, E y); + /// E operator –(E x, U y); + /// E operator &(E x, E y); + /// E operator |(E x, E y); + /// E operator ^(E x, E y); + /// + ResolveResult HandleEnumOperator(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs) + { + // evaluate as (E)((U)x op (U)y) + if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) { + IType elementType = GetEnumUnderlyingType(enumType); + if (elementType.Kind != TypeKind.Enum) { + var rr = ResolveBinaryOperator(op, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs)); + rr = WithCheckForOverflow(false).ResolveCast(enumType, rr); + if (rr.IsCompileTimeConstant) // only report result if it's a constant; use the regular OperatorResolveResult codepath otherwise + return rr; + } + } + IType resultType = MakeNullable(enumType, isNullable); + return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable); + } + + IType MakeNullable(IType type, bool isNullable) + { + if (isNullable) + return NullableType.Create(compilation, type); + else + return type; + } + #endregion + + #region BinaryNumericPromotion + bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs, bool allowNullableConstants) + { + // C# 4.0 spec: §7.3.6.2 + TypeCode lhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(lhs.Type)); + TypeCode rhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rhs.Type)); + // if one of the inputs is the null literal, promote that to the type of the other operand + if (isNullable && lhs.Type.Kind == TypeKind.Null && rhsCode >= TypeCode.Boolean && rhsCode <= TypeCode.Decimal) { + lhs = CastTo(rhsCode, isNullable, lhs, allowNullableConstants); + lhsCode = rhsCode; + } else if (isNullable && rhs.Type.Kind == TypeKind.Null && lhsCode >= TypeCode.Boolean && lhsCode <= TypeCode.Decimal) { + rhs = CastTo(lhsCode, isNullable, rhs, allowNullableConstants); + rhsCode = lhsCode; + } + bool bindingError = false; + if (lhsCode >= TypeCode.Char && lhsCode <= TypeCode.Decimal + && rhsCode >= TypeCode.Char && rhsCode <= TypeCode.Decimal) + { + TypeCode targetType; + if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) { + targetType = TypeCode.Decimal; + bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double + || rhsCode == TypeCode.Single || rhsCode == TypeCode.Double); + } else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) { + targetType = TypeCode.Double; + } else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) { + targetType = TypeCode.Single; + } else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) { + targetType = TypeCode.UInt64; + bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs); + } else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) { + targetType = TypeCode.Int64; + } else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) { + targetType = (IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs)) ? TypeCode.Int64 : TypeCode.UInt32; + } else { + targetType = TypeCode.Int32; + } + lhs = CastTo(targetType, isNullable, lhs, allowNullableConstants); + rhs = CastTo(targetType, isNullable, rhs, allowNullableConstants); + } + return !bindingError; + } + + bool IsSigned(TypeCode code, ResolveResult rr) + { + // Determine whether the rr with code==ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rr.Type)) + // is a signed primitive type. + switch (code) { + case TypeCode.SByte: + case TypeCode.Int16: + return true; + case TypeCode.Int32: + // for int, consider implicit constant expression conversion + if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (int)rr.ConstantValue >= 0) + return false; + else + return true; + case TypeCode.Int64: + // for long, consider implicit constant expression conversion + if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (long)rr.ConstantValue >= 0) + return false; + else + return true; + default: + return false; + } + } + + ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants) + { + IType elementType = compilation.FindType(targetType); + IType nullableType = MakeNullable(elementType, isNullable); + if (nullableType.Equals(expression.Type)) + return expression; + if (allowNullableConstants && expression.IsCompileTimeConstant) { + if (expression.ConstantValue == null) + return new ConstantResolveResult(nullableType, null); + ResolveResult rr = ResolveCast(elementType, expression); + if (rr.IsError) + return rr; + Debug.Assert(rr.IsCompileTimeConstant); + return new ConstantResolveResult(nullableType, rr.ConstantValue); + } else { + return Convert(expression, nullableType, + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); + } + } + #endregion + + #region GetOverloadableOperatorName + static string GetOverloadableOperatorName(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.Add: + return "op_Addition"; + case BinaryOperatorType.Subtract: + return "op_Subtraction"; + case BinaryOperatorType.Multiply: + return "op_Multiply"; + case BinaryOperatorType.Divide: + return "op_Division"; + case BinaryOperatorType.Modulus: + return "op_Modulus"; + case BinaryOperatorType.BitwiseAnd: + return "op_BitwiseAnd"; + case BinaryOperatorType.BitwiseOr: + return "op_BitwiseOr"; + case BinaryOperatorType.ExclusiveOr: + return "op_ExclusiveOr"; + case BinaryOperatorType.ShiftLeft: + return "op_LeftShift"; + case BinaryOperatorType.ShiftRight: + return "op_RightShift"; + case BinaryOperatorType.Equality: + return "op_Equality"; + case BinaryOperatorType.InEquality: + return "op_Inequality"; + case BinaryOperatorType.GreaterThan: + return "op_GreaterThan"; + case BinaryOperatorType.LessThan: + return "op_LessThan"; + case BinaryOperatorType.GreaterThanOrEqual: + return "op_GreaterThanOrEqual"; + case BinaryOperatorType.LessThanOrEqual: + return "op_LessThanOrEqual"; + default: + return null; + } + } + #endregion + + #region Null coalescing operator + ResolveResult ResolveNullCoalescingOperator(ResolveResult lhs, ResolveResult rhs) + { + if (NullableType.IsNullable(lhs.Type)) { + IType a0 = NullableType.GetUnderlyingType(lhs.Type); + if (TryConvert(ref rhs, a0)) { + return BinaryOperatorResolveResult(a0, lhs, BinaryOperatorType.NullCoalescing, rhs); + } + } + if (TryConvert(ref rhs, lhs.Type)) { + return BinaryOperatorResolveResult(lhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs); + } + if (TryConvert(ref lhs, rhs.Type)) { + return BinaryOperatorResolveResult(rhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs); + } else { + return new ErrorResolveResult(lhs.Type); + } + } + #endregion + #endregion + + #region Get user-defined operator candidates + IEnumerable GetUserDefinedOperatorCandidates(IType type, string operatorName) + { + if (operatorName == null) + return EmptyList.Instance; + TypeCode c = ReflectionHelper.GetTypeCode(type); + if (TypeCode.Boolean <= c && c <= TypeCode.Decimal || c == TypeCode.String) { + // The .NET framework contains some of C#'s built-in operators as user-defined operators. + // However, we must not use those as user-defined operators (we would skip numeric promotion). + return EmptyList.Instance; + } + // C# 4.0 spec: §7.3.5 Candidate user-defined operators + var operators = type.GetMethods(m => m.IsOperator && m.Name == operatorName).ToList(); + LiftUserDefinedOperators(operators); + return operators; + } + + void LiftUserDefinedOperators(List operators) + { + int nonLiftedMethodCount = operators.Count; + // Construct lifted operators + for (int i = 0; i < nonLiftedMethodCount; i++) { + var liftedMethod = LiftUserDefinedOperator(operators[i]); + if (liftedMethod != null) + operators.Add(liftedMethod); + } + } + + LiftedUserDefinedOperator LiftUserDefinedOperator(IMethod m) + { + if (IsComparisonOperator(m)) { + if (!m.ReturnType.Equals(compilation.FindType(KnownTypeCode.Boolean))) + return null; // cannot lift this operator + } else { + if (!NullableType.IsNonNullableValueType(m.ReturnType)) + return null; // cannot lift this operator + } + for (int i = 0; i < m.Parameters.Count; i++) { + if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type)) + return null; // cannot lift this operator + } + return new LiftedUserDefinedOperator(m); + } + + static bool IsComparisonOperator(IMethod m) + { + var type = OperatorDeclaration.GetOperatorType(m.Name); + return type.HasValue && type.Value.IsComparisonOperator(); + } + + sealed class LiftedUserDefinedOperator : SpecializedMethod, OverloadResolution.ILiftedOperator + { + internal readonly IParameterizedMember nonLiftedOperator; + + public LiftedUserDefinedOperator(IMethod nonLiftedMethod) + : base((IMethod)nonLiftedMethod.MemberDefinition, nonLiftedMethod.Substitution) + { + this.nonLiftedOperator = nonLiftedMethod; + var substitution = new MakeNullableVisitor(nonLiftedMethod.Compilation, nonLiftedMethod.Substitution); + this.Parameters = base.CreateParameters(substitution); + // Comparison operators keep the 'bool' return type even when lifted. + if (IsComparisonOperator(nonLiftedMethod)) + this.ReturnType = nonLiftedMethod.ReturnType; + else + this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution); + } + + public IList NonLiftedParameters { + get { return nonLiftedOperator.Parameters; } + } + + public override bool Equals(object obj) + { + LiftedUserDefinedOperator op = obj as LiftedUserDefinedOperator; + return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator); + } + + public override int GetHashCode() + { + return nonLiftedOperator.GetHashCode() ^ 0x7191254; + } + } + + sealed class MakeNullableVisitor : TypeVisitor + { + readonly ICompilation compilation; + readonly TypeParameterSubstitution typeParameterSubstitution; + + public MakeNullableVisitor(ICompilation compilation, TypeParameterSubstitution typeParameterSubstitution) + { + this.compilation = compilation; + this.typeParameterSubstitution = typeParameterSubstitution; + } + + public override IType VisitTypeDefinition(ITypeDefinition type) + { + return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution)); + } + + public override IType VisitTypeParameter(ITypeParameter type) + { + return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution)); + } + + public override IType VisitParameterizedType(ParameterizedType type) + { + return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution)); + } + + public override IType VisitOtherType(IType type) + { + return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution)); + } + } + + ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r, System.Linq.Expressions.ExpressionType operatorType) + { + if (r.BestCandidateErrors != OverloadResolutionErrors.None) + return r.CreateResolveResult(null); + IMethod method = (IMethod)r.BestCandidate; + return new OperatorResolveResult(method.ReturnType, operatorType, method, + isLiftedOperator: method is OverloadResolution.ILiftedOperator, + operands: r.GetArgumentsWithConversions()); + } + #endregion + + #region ResolveCast + bool TryConvert(ref ResolveResult rr, IType targetType) + { + Conversion c = conversions.ImplicitConversion(rr, targetType); + if (c.IsValid) { + rr = Convert(rr, targetType, c); + return true; + } else { + return false; + } + } + + /// + /// + /// + /// The input resolve result that should be converted. + /// If a conversion exists, it is applied to the resolve result + /// The target type that we should convert to + /// Whether we are dealing with a lifted operator + /// The resolve result that is enum-typed. + /// If necessary, a nullable conversion is applied. + /// + /// Whether the conversion from the constant zero is allowed. + /// + /// True if the conversion is successful; false otherwise. + /// If the conversion is not successful, the ref parameters will not be modified. + bool TryConvertEnum(ref ResolveResult rr, IType targetType, ref bool isNullable, ref ResolveResult enumRR, bool allowConversionFromConstantZero = true) + { + Conversion c; + if (!isNullable) { + // Try non-nullable + c = conversions.ImplicitConversion(rr, targetType); + if (c.IsValid && (allowConversionFromConstantZero || !c.IsEnumerationConversion)) { + rr = Convert(rr, targetType, c); + return true; + } + } + // make targetType nullable if it isn't already: + if (!targetType.IsKnownType(KnownTypeCode.NullableOfT)) + targetType = NullableType.Create(compilation, targetType); + + c = conversions.ImplicitConversion(rr, targetType); + if (c.IsValid && (allowConversionFromConstantZero || !c.IsEnumerationConversion)) { + rr = Convert(rr, targetType, c); + isNullable = true; + // Also convert the enum-typed RR to nullable, if it isn't already + if (!enumRR.Type.IsKnownType(KnownTypeCode.NullableOfT)) { + var nullableType = NullableType.Create(compilation, enumRR.Type); + enumRR = new ConversionResolveResult(nullableType, enumRR, Conversion.ImplicitNullableConversion); + } + return true; + } + return false; + } + + ResolveResult Convert(ResolveResult rr, IType targetType) + { + return Convert(rr, targetType, conversions.ImplicitConversion(rr, targetType)); + } + + ResolveResult Convert(ResolveResult rr, IType targetType, Conversion c) + { + if (c == Conversion.IdentityConversion) + return rr; + else if (rr.IsCompileTimeConstant && c != Conversion.None && !c.IsUserDefined) + return ResolveCast(targetType, rr); + else + return new ConversionResolveResult(targetType, rr, c, checkForOverflow); + } + + public ResolveResult ResolveCast(IType targetType, ResolveResult expression) + { + // C# 4.0 spec: §7.7.6 Cast expressions + Conversion c = conversions.ExplicitConversion(expression, targetType); + if (expression.IsCompileTimeConstant && !c.IsUserDefined) { + TypeCode code = ReflectionHelper.GetTypeCode(targetType); + if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) { + try { + return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue)); + } catch (OverflowException) { + return new ErrorResolveResult(targetType); + } catch (InvalidCastException) { + return new ErrorResolveResult(targetType); + } + } else if (code == TypeCode.String) { + if (expression.ConstantValue == null || expression.ConstantValue is string) + return new ConstantResolveResult(targetType, expression.ConstantValue); + else + return new ErrorResolveResult(targetType); + } else if (targetType.Kind == TypeKind.Enum) { + code = ReflectionHelper.GetTypeCode(GetEnumUnderlyingType(targetType)); + if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expression.ConstantValue != null) { + try { + return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue)); + } catch (OverflowException) { + return new ErrorResolveResult(targetType); + } catch (InvalidCastException) { + return new ErrorResolveResult(targetType); + } + } + } + } + return new ConversionResolveResult(targetType, expression, c, checkForOverflow); + } + + internal object CSharpPrimitiveCast(TypeCode targetType, object input) + { + return Utils.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow); + } + #endregion + + #region ResolveSimpleName + public ResolveResult ResolveSimpleName(string identifier, IList typeArguments, bool isInvocationTarget = false) + { + // C# 4.0 spec: §7.6.2 Simple Names + + return LookupSimpleNameOrTypeName( + identifier, typeArguments, + isInvocationTarget ? NameLookupMode.InvocationTarget : NameLookupMode.Expression); + } + + public ResolveResult LookupSimpleNameOrTypeName(string identifier, IList typeArguments, NameLookupMode lookupMode) + { + // C# 4.0 spec: §3.8 Namespace and type names; §7.6.2 Simple Names + + if (identifier == null) + throw new ArgumentNullException("identifier"); + if (typeArguments == null) + throw new ArgumentNullException("typeArguments"); + + int k = typeArguments.Count; + + if (k == 0) { + if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) { + // Look in local variables + foreach (IVariable v in this.LocalVariables) { + if (v.Name == identifier) { + return new LocalResolveResult(v); + } + } + // Look in parameters of current method + IParameterizedMember parameterizedMember = this.CurrentMember as IParameterizedMember; + if (parameterizedMember != null) { + foreach (IParameter p in parameterizedMember.Parameters) { + if (p.Name == identifier) { + return new LocalResolveResult(p); + } + } + } + } + + // look in type parameters of current method + IMethod m = this.CurrentMember as IMethod; + if (m != null) { + foreach (ITypeParameter tp in m.TypeParameters) { + if (tp.Name == identifier) + return new TypeResolveResult(tp); + } + } + } + + bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument)); + + ResolveResult r = null; + if (currentTypeDefinitionCache != null) { + Dictionary cache = null; + bool foundInCache = false; + if (k == 0) { + switch (lookupMode) { + case NameLookupMode.Expression: + cache = currentTypeDefinitionCache.SimpleNameLookupCacheExpression; + break; + case NameLookupMode.InvocationTarget: + cache = currentTypeDefinitionCache.SimpleNameLookupCacheInvocationTarget; + break; + case NameLookupMode.Type: + cache = currentTypeDefinitionCache.SimpleTypeLookupCache; + break; + } + if (cache != null) { + lock (cache) + foundInCache = cache.TryGetValue(identifier, out r); + } + } + if (foundInCache) { + r = (r != null ? r.ShallowClone() : null); + } else { + r = LookInCurrentType(identifier, typeArguments, lookupMode, parameterizeResultType); + if (cache != null) { + // also cache missing members (r==null) + lock (cache) + cache[identifier] = r; + } + } + if (r != null) + return r; + } + + if (context.CurrentUsingScope == null) { + // If no using scope was specified, we still need to look in the global namespace: + r = LookInUsingScopeNamespace(null, compilation.RootNamespace, identifier, typeArguments, parameterizeResultType); + } else { + if (k == 0 && lookupMode != NameLookupMode.TypeInUsingDeclaration) { + if (context.CurrentUsingScope.ResolveCache.TryGetValue(identifier, out r)) { + r = (r != null ? r.ShallowClone() : null); + } else { + r = LookInCurrentUsingScope(identifier, typeArguments, false, false); + context.CurrentUsingScope.ResolveCache.TryAdd(identifier, r); + } + } else { + r = LookInCurrentUsingScope(identifier, typeArguments, lookupMode == NameLookupMode.TypeInUsingDeclaration, parameterizeResultType); + } + } + if (r != null) + return r; + + if (typeArguments.Count == 0 && identifier == "dynamic") { + return new TypeResolveResult(SpecialType.Dynamic); + } else { + return new UnknownIdentifierResolveResult(identifier, typeArguments.Count); + } + } + + public bool IsVariableReferenceWithSameType (ResolveResult rr, string identifier, out TypeResolveResult trr) + { + if (!(rr is MemberResolveResult || rr is LocalResolveResult)) { + trr = null; + return false; + } + trr = LookupSimpleNameOrTypeName (identifier, EmptyList.Instance, NameLookupMode.Type) as TypeResolveResult; + return trr != null && trr.Type.Equals (rr.Type); + } + + ResolveResult LookInCurrentType(string identifier, IList typeArguments, NameLookupMode lookupMode, bool parameterizeResultType) + { + int k = typeArguments.Count; + MemberLookup lookup = CreateMemberLookup(lookupMode); + // look in current type definitions + for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { + if (k == 0) { + // Look for type parameter with that name + var typeParameters = t.TypeParameters; + // Look at all type parameters, including those copied from outer classes, + // so that we can fetch the version with the correct owner. + for (int i = 0; i < typeParameters.Count; i++) { + if (typeParameters[i].Name == identifier) + return new TypeResolveResult(typeParameters[i]); + } + } + + if (lookupMode == NameLookupMode.BaseTypeReference && t == this.CurrentTypeDefinition) { + // don't look in current type when resolving a base type reference + continue; + } + + ResolveResult r; + if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) { + var targetResolveResult = (t == this.CurrentTypeDefinition ? ResolveThisReference() : new TypeResolveResult(t)); + r = lookup.Lookup(targetResolveResult, identifier, typeArguments, lookupMode == NameLookupMode.InvocationTarget); + } else { + r = lookup.LookupType(t, identifier, typeArguments, parameterizeResultType); + } + if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult + return r; + } + return null; + } + + ResolveResult LookInCurrentUsingScope(string identifier, IList typeArguments, bool isInUsingDeclaration, bool parameterizeResultType) + { + // look in current namespace definitions + ResolvedUsingScope currentUsingScope = this.CurrentUsingScope; + for (ResolvedUsingScope u = currentUsingScope; u != null; u = u.Parent) { + var resultInNamespace = LookInUsingScopeNamespace(u, u.Namespace, identifier, typeArguments, parameterizeResultType); + if (resultInNamespace != null) + return resultInNamespace; + // then look for aliases: + if (typeArguments.Count == 0) { + if (u.ExternAliases.Contains(identifier)) { + return ResolveExternAlias(identifier); + } + if (!(isInUsingDeclaration && u == currentUsingScope)) { + foreach (var pair in u.UsingAliases) { + if (pair.Key == identifier) { + return pair.Value.ShallowClone(); + } + } + } + } + // finally, look in the imported namespaces: + if (!(isInUsingDeclaration && u == currentUsingScope)) { + IType firstResult = null; + foreach (var importedNamespace in u.Usings) { + ITypeDefinition def = importedNamespace.GetTypeDefinition(identifier, typeArguments.Count); + if (def != null) { + IType resultType; + if (parameterizeResultType && typeArguments.Count > 0) + resultType = new ParameterizedType(def, typeArguments); + else + resultType = def; + + if (firstResult == null || !TopLevelTypeDefinitionIsAccessible(firstResult.GetDefinition())) { + if (TopLevelTypeDefinitionIsAccessible(resultType.GetDefinition())) + firstResult = resultType; + } else if (TopLevelTypeDefinitionIsAccessible(def)) { + return new AmbiguousTypeResolveResult(firstResult); + } + } + } + if (firstResult != null) + return new TypeResolveResult(firstResult); + } + // if we didn't find anything: repeat lookup with parent namespace + } + return null; + } + + ResolveResult LookInUsingScopeNamespace(ResolvedUsingScope usingScope, INamespace n, string identifier, IList typeArguments, bool parameterizeResultType) + { + if (n == null) + return null; + // first look for a namespace + int k = typeArguments.Count; + if (k == 0) { + INamespace childNamespace = n.GetChildNamespace(identifier); + if (childNamespace != null) { + if (usingScope != null && usingScope.HasAlias(identifier)) + return new AmbiguousTypeResolveResult(new UnknownType(null, identifier)); + return new NamespaceResolveResult(childNamespace); + } + } + // then look for a type + ITypeDefinition def = n.GetTypeDefinition(identifier, k); + if (def != null) { + IType result = def; + if (parameterizeResultType && k > 0) { + result = new ParameterizedType(def, typeArguments); + } + if (usingScope != null && usingScope.HasAlias(identifier)) + return new AmbiguousTypeResolveResult(result); + else + return new TypeResolveResult(result); + } + return null; + } + + bool TopLevelTypeDefinitionIsAccessible(ITypeDefinition typeDef) + { + if (typeDef.IsInternal) { + return typeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly); + } + return true; + } + + /// + /// Looks up an alias (identifier in front of :: operator) + /// + public ResolveResult ResolveAlias(string identifier) + { + if (identifier == "global") + return new NamespaceResolveResult(compilation.RootNamespace); + + for (ResolvedUsingScope n = this.CurrentUsingScope; n != null; n = n.Parent) { + if (n.ExternAliases.Contains(identifier)) { + return ResolveExternAlias(identifier); + } + foreach (var pair in n.UsingAliases) { + if (pair.Key == identifier) { + return (pair.Value as NamespaceResolveResult) ?? ErrorResult; + } + } + } + return ErrorResult; + } + + ResolveResult ResolveExternAlias(string alias) + { + INamespace ns = compilation.GetNamespaceForExternAlias(alias); + if (ns != null) + return new NamespaceResolveResult(ns); + else + return ErrorResult; + } + #endregion + + #region ResolveMemberAccess + public ResolveResult ResolveMemberAccess(ResolveResult target, string identifier, IList typeArguments, NameLookupMode lookupMode = NameLookupMode.Expression) + { + // C# 4.0 spec: §7.6.4 + + bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument)); + NamespaceResolveResult nrr = target as NamespaceResolveResult; + if (nrr != null) { + return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments, parameterizeResultType); + } + + if (target.Type.Kind == TypeKind.Dynamic) + return new DynamicMemberResolveResult(target, identifier); + + MemberLookup lookup = CreateMemberLookup(lookupMode); + ResolveResult result; + switch (lookupMode) { + case NameLookupMode.Expression: + result = lookup.Lookup(target, identifier, typeArguments, isInvocation: false); + break; + case NameLookupMode.InvocationTarget: + result = lookup.Lookup(target, identifier, typeArguments, isInvocation: true); + break; + case NameLookupMode.Type: + case NameLookupMode.TypeInUsingDeclaration: + case NameLookupMode.BaseTypeReference: + // Don't do the UnknownMemberResolveResult/MethodGroupResolveResult processing, + // it's only relevant for expressions. + return lookup.LookupType(target.Type, identifier, typeArguments, parameterizeResultType); + default: + throw new NotSupportedException("Invalid value for NameLookupMode"); + } + if (result is UnknownMemberResolveResult) { + // We intentionally use all extension methods here, not just the eligible ones. + // Proper eligibility checking is only possible for the full invocation + // (after we know the remaining arguments). + // The eligibility check in GetExtensionMethods is only intended for code completion. + var extensionMethods = GetExtensionMethods(identifier, typeArguments); + if (extensionMethods.Count > 0) { + return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { + extensionMethods = extensionMethods + }; + } + } else { + MethodGroupResolveResult mgrr = result as MethodGroupResolveResult; + if (mgrr != null) { + Debug.Assert(mgrr.extensionMethods == null); + // set the values that are necessary to make MethodGroupResolveResult.GetExtensionMethods() work + mgrr.resolver = this; + } + } + return result; + } + + [Obsolete("Use ResolveMemberAccess() with NameLookupMode.Type instead")] + public ResolveResult ResolveMemberType(ResolveResult target, string identifier, IList typeArguments) + { + return ResolveMemberAccess(target, identifier, typeArguments, NameLookupMode.Type); + } + + ResolveResult ResolveMemberAccessOnNamespace(NamespaceResolveResult nrr, string identifier, IList typeArguments, bool parameterizeResultType) + { + if (typeArguments.Count == 0) { + INamespace childNamespace = nrr.Namespace.GetChildNamespace(identifier); + if (childNamespace != null) + return new NamespaceResolveResult(childNamespace); + } + ITypeDefinition def = nrr.Namespace.GetTypeDefinition(identifier, typeArguments.Count); + if (def != null) { + if (parameterizeResultType && typeArguments.Count > 0) + return new TypeResolveResult(new ParameterizedType(def, typeArguments)); + else + return new TypeResolveResult(def); + } + return ErrorResult; + } + + /// + /// Creates a MemberLookup instance using this resolver's settings. + /// + public MemberLookup CreateMemberLookup() + { + ITypeDefinition currentTypeDefinition = this.CurrentTypeDefinition; + bool isInEnumMemberInitializer = this.CurrentMember != null && this.CurrentMember.SymbolKind == SymbolKind.Field + && currentTypeDefinition != null && currentTypeDefinition.Kind == TypeKind.Enum; + return new MemberLookup(currentTypeDefinition, this.Compilation.MainAssembly, isInEnumMemberInitializer); + } + + /// + /// Creates a MemberLookup instance using this resolver's settings. + /// + public MemberLookup CreateMemberLookup(NameLookupMode lookupMode) + { + if (lookupMode == NameLookupMode.BaseTypeReference && this.CurrentTypeDefinition != null) { + // When looking up a base type reference, treat us as being outside the current type definition + // for accessibility purposes. + // This avoids a stack overflow when referencing a protected class nested inside the base class + // of a parent class. (NameLookupTests.InnerClassInheritingFromProtectedBaseInnerClassShouldNotCauseStackOverflow) + return new MemberLookup(this.CurrentTypeDefinition.DeclaringTypeDefinition, this.Compilation.MainAssembly, false); + } else { + return CreateMemberLookup(); + } + } + #endregion + + #region ResolveIdentifierInObjectInitializer + public ResolveResult ResolveIdentifierInObjectInitializer(string identifier) + { + MemberLookup memberLookup = CreateMemberLookup(); + return memberLookup.Lookup(this.CurrentObjectInitializer, identifier, EmptyList.Instance, false); + } + #endregion + + #region GetExtensionMethods + /// + /// Gets all extension methods that are available in the current context. + /// + /// Name of the extension method. Pass null to retrieve all extension methods. + /// Explicitly provided type arguments. + /// An empty list will return all matching extension method definitions; + /// a non-empty list will return s for all extension methods + /// with the matching number of type parameters. + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// + public List> GetExtensionMethods(string name = null, IList typeArguments = null) + { + return GetExtensionMethods(null, name, typeArguments); + } + + /// + /// Gets the extension methods that are called 'name' + /// and are applicable with a first argument type of 'targetType'. + /// + /// Type of the 'this' argument + /// Name of the extension method. Pass null to retrieve all extension methods. + /// Explicitly provided type arguments. + /// An empty list will return all matching extension method definitions; + /// a non-empty list will return s for all extension methods + /// with the matching number of type parameters. + /// + /// Specifies whether to produce a + /// when type arguments could be inferred from . This parameter + /// is only used for inferred types and has no effect if is non-empty. + /// + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// + public List> GetExtensionMethods(IType targetType, string name = null, IList typeArguments = null, bool substituteInferredTypes = false) + { + var lookup = CreateMemberLookup(); + List> extensionMethodGroups = new List>(); + foreach (var inputGroup in GetAllExtensionMethods(lookup)) { + List outputGroup = new List(); + foreach (var method in inputGroup) { + if (name != null && method.Name != name) + continue; + if (!lookup.IsAccessible(method, false)) + continue; + IType[] inferredTypes; + if (typeArguments != null && typeArguments.Count > 0) { + if (method.TypeParameters.Count != typeArguments.Count) + continue; + var sm = method.Specialize(new TypeParameterSubstitution(null, typeArguments)); + if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes)) + outputGroup.Add(sm); + } else { + if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) { + if (substituteInferredTypes && inferredTypes != null) { + outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes))); + } else { + outputGroup.Add(method); + } + } + } + } + if (outputGroup.Count > 0) + extensionMethodGroups.Add(outputGroup); + } + return extensionMethodGroups; + } + + /// + /// Checks whether the specified extension method is eligible on the target type. + /// + /// Target type that is passed as first argument to the extension method. + /// The extension method. + /// Whether to perform type inference for the method. + /// Use false if is already parameterized (e.g. when type arguments were given explicitly). + /// Otherwise, use true. + /// + /// If the method is generic and is true, + /// and at least some of the type arguments could be inferred, this parameter receives the inferred type arguments. + /// Since only the type for the first parameter is considered, not all type arguments may be inferred. + /// If an array is returned, any slot with an uninferred type argument will be set to the method's + /// corresponding type parameter. + /// + public static bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes) + { + if (targetType == null) + throw new ArgumentNullException("targetType"); + if (method == null) + throw new ArgumentNullException("method"); + var compilation = method.Compilation; + return IsEligibleExtensionMethod(compilation, CSharpConversions.Get(compilation), targetType, method, useTypeInference, out outInferredTypes); + } + + static bool IsEligibleExtensionMethod(ICompilation compilation, CSharpConversions conversions, IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes) + { + outInferredTypes = null; + if (targetType == null) + return true; + if (method.Parameters.Count == 0) + return false; + IType thisParameterType = method.Parameters[0].Type; + if (useTypeInference && method.TypeParameters.Count > 0) { + // We need to infer type arguments from targetType: + TypeInference ti = new TypeInference(compilation, conversions); + ResolveResult[] arguments = { new ResolveResult(targetType) }; + IType[] parameterTypes = { method.Parameters[0].Type }; + bool success; + var inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success); + var substitution = new TypeParameterSubstitution(null, inferredTypes); + // Validate that the types that could be inferred (aren't unknown) satisfy the constraints: + bool hasInferredTypes = false; + for (int i = 0; i < inferredTypes.Length; i++) { + if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) { + hasInferredTypes = true; + if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions)) + return false; + } else { + inferredTypes[i] = method.TypeParameters[i]; // do not substitute types that could not be inferred + } + } + if (hasInferredTypes) + outInferredTypes = inferredTypes; + thisParameterType = thisParameterType.AcceptVisitor(substitution); + } + Conversion c = conversions.ImplicitConversion(targetType, thisParameterType); + return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion); + } + + /// + /// Gets all extension methods available in the current using scope. + /// This list includes inaccessible methods. + /// + IList> GetAllExtensionMethods(MemberLookup lookup) + { + var currentUsingScope = context.CurrentUsingScope; + if (currentUsingScope == null) + return EmptyList>.Instance; + List> extensionMethodGroups = LazyInit.VolatileRead(ref currentUsingScope.AllExtensionMethods); + if (extensionMethodGroups != null) { + return extensionMethodGroups; + } + extensionMethodGroups = new List>(); + List m; + for (ResolvedUsingScope scope = currentUsingScope; scope != null; scope = scope.Parent) { + INamespace ns = scope.Namespace; + if (ns != null) { + m = GetExtensionMethods(lookup, ns).ToList(); + if (m.Count > 0) + extensionMethodGroups.Add(m); + } + + m = scope.Usings + .Distinct() + .SelectMany(importedNamespace => GetExtensionMethods(lookup, importedNamespace)) + .ToList(); + if (m.Count > 0) + extensionMethodGroups.Add(m); + } + return LazyInit.GetOrSet(ref currentUsingScope.AllExtensionMethods, extensionMethodGroups); + } + + IEnumerable GetExtensionMethods(MemberLookup lookup, INamespace ns) + { + // TODO: maybe make this a property on INamespace? + return + from c in ns.Types + where c.IsStatic && c.HasExtensionMethods && c.TypeParameters.Count == 0 && lookup.IsAccessible(c, false) + from m in c.Methods + where m.IsExtensionMethod + select m; + } + #endregion + + #region ResolveInvocation + + IList AddArgumentNamesIfNecessary(ResolveResult[] arguments, string[] argumentNames) { + if (argumentNames == null) { + return arguments; + } + else { + var result = new ResolveResult[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) { + result[i] = (argumentNames[i] != null ? new NamedArgumentResolveResult(argumentNames[i], arguments[i]) : arguments[i]); + } + return result; + } + } + + private ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames, bool allowOptionalParameters) + { + // C# 4.0 spec: §7.6.5 + + if (target.Type.Kind == TypeKind.Dynamic) { + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); + } + + bool isDynamic = arguments.Any(a => a.Type.Kind == TypeKind.Dynamic); + MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; + if (mgrr != null) { + if (isDynamic) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. + var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray()); + var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList(); + + if (applicableMethods.Count > 1) { + ResolveResult actualTarget; + if (applicableMethods.All(x => x.Method.IsStatic) && !(mgrr.TargetResult is TypeResolveResult)) + actualTarget = new TypeResolveResult(mgrr.TargetType); + else + actualTarget = mgrr.TargetResult; + + var l = new List(); + foreach (var m in applicableMethods) { + if (l.Count == 0 || l[l.Count - 1].DeclaringType != m.DeclaringType) + l.Add(new MethodListWithDeclaringType(m.DeclaringType)); + l[l.Count - 1].Add(m.Method); + } + return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); + } + } + + OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters); + if (or.BestCandidate != null) { + if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) + return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); + else + return or.CreateResolveResult(mgrr.TargetResult, returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); + } else { + // No candidate found at all (not even an inapplicable one). + // This can happen with empty method groups (as sometimes used with extension methods) + return new UnknownMethodResolveResult( + mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames)); + } + } + UnknownMemberResolveResult umrr = target as UnknownMemberResolveResult; + if (umrr != null) { + return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames)); + } + UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult; + if (uirr != null && CurrentTypeDefinition != null) { + return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList.Instance, CreateParameters(arguments, argumentNames)); + } + IMethod invokeMethod = target.Type.GetDelegateInvokeMethod(); + if (invokeMethod != null) { + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); + or.AddCandidate(invokeMethod); + return new CSharpInvocationResolveResult( + target, invokeMethod, //invokeMethod.ReturnType.Resolve(context), + or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors, + isExpandedForm: or.BestCandidateIsExpandedForm, + isDelegateInvocation: true, + argumentToParameterMap: or.GetArgumentToParameterMap(), + returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); + } + return ErrorResult; + } + + /// + /// Resolves an invocation. + /// + /// The target of the invocation. Usually a MethodGroupResolveResult. + /// + /// Arguments passed to the method. + /// The resolver may mutate this array to wrap elements in s! + /// + /// + /// The argument names. Pass the null string for positional arguments. + /// + /// InvocationResolveResult or UnknownMethodResolveResult + public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null) + { + return ResolveInvocation(target, arguments, argumentNames, allowOptionalParameters: true); + } + + List CreateParameters(ResolveResult[] arguments, string[] argumentNames) + { + List list = new List(); + if (argumentNames == null) { + argumentNames = new string[arguments.Length]; + } else { + if (argumentNames.Length != arguments.Length) + throw new ArgumentException(); + argumentNames = (string[])argumentNames.Clone(); + } + for (int i = 0; i < arguments.Length; i++) { + // invent argument names where necessary: + if (argumentNames[i] == null) { + string newArgumentName = GuessParameterName(arguments[i]); + if (argumentNames.Contains(newArgumentName)) { + // disambiguate argument name (e.g. add a number) + int num = 1; + string newName; + do { + newName = newArgumentName + num.ToString(); + num++; + } while(argumentNames.Contains(newName)); + newArgumentName = newName; + } + argumentNames[i] = newArgumentName; + } + + // create the parameter: + ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; + if (brrr != null) { + list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut)); + } else { + // argument might be a lambda or delegate type, so we have to try to guess the delegate type + IType type = arguments[i].Type; + if (type.Kind == TypeKind.Null || type.Kind == TypeKind.Unknown) { + list.Add(new DefaultParameter(compilation.FindType(KnownTypeCode.Object), argumentNames[i])); + } else { + list.Add(new DefaultParameter(type, argumentNames[i])); + } + } + } + return list; + } + + static string GuessParameterName(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + if (mrr != null) + return mrr.Member.Name; + + UnknownMemberResolveResult umrr = rr as UnknownMemberResolveResult; + if (umrr != null) + return umrr.MemberName; + + MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; + if (mgrr != null) + return mgrr.MethodName; + + LocalResolveResult vrr = rr as LocalResolveResult; + if (vrr != null) + return MakeParameterName(vrr.Variable.Name); + + if (rr.Type.Kind != TypeKind.Unknown && !string.IsNullOrEmpty(rr.Type.Name)) { + return MakeParameterName(rr.Type.Name); + } else { + return "parameter"; + } + } + + static string MakeParameterName(string variableName) + { + if (string.IsNullOrEmpty(variableName)) + return "parameter"; + if (variableName.Length > 1 && variableName[0] == '_') + variableName = variableName.Substring(1); + return char.ToLower(variableName[0]) + variableName.Substring(1); + } + + OverloadResolution CreateOverloadResolution(ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null) + { + var or = new OverloadResolution(compilation, arguments, argumentNames, typeArguments, conversions); + or.CheckForOverflow = checkForOverflow; + return or; + } + #endregion + + #region ResolveIndexer + /// + /// Resolves an indexer access. + /// + /// Target expression. + /// + /// Arguments passed to the indexer. + /// The resolver may mutate this array to wrap elements in s! + /// + /// + /// The argument names. Pass the null string for positional arguments. + /// + /// ArrayAccessResolveResult, InvocationResolveResult, or ErrorResolveResult + public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null) + { + switch (target.Type.Kind) { + case TypeKind.Dynamic: + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames)); + + case TypeKind.Array: + case TypeKind.Pointer: + // §7.6.6.1 Array access / §18.5.3 Pointer element access + AdjustArrayAccessArguments(arguments); + return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments); + } + + // §7.6.6.2 Indexer access + + MemberLookup lookup = CreateMemberLookup(); + var indexers = lookup.LookupIndexers(target); + + if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable indexer. + var or2 = CreateOverloadResolution(arguments, argumentNames, null); + var applicableIndexers = indexers.SelectMany(x => x).Where(m => OverloadResolution.IsApplicable(or2.AddCandidate(m))).ToList(); + + if (applicableIndexers.Count > 1) { + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames)); + } + } + + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); + or.AddMethodLists(indexers); + if (or.BestCandidate != null) { + return or.CreateResolveResult(target); + } else { + return ErrorResult; + } + } + + /// + /// Converts all arguments to int,uint,long or ulong. + /// + void AdjustArrayAccessArguments(ResolveResult[] arguments) + { + for (int i = 0; i < arguments.Length; i++) { + if (!(TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int32)) || + TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt32)) || + TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int64)) || + TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt64)))) + { + // conversion failed + arguments[i] = Convert(arguments[i], compilation.FindType(KnownTypeCode.Int32), Conversion.None); + } + } + } + #endregion + + #region ResolveObjectCreation + /// + /// Resolves an object creation. + /// + /// Type of the object to create. + /// + /// Arguments passed to the constructor. + /// The resolver may mutate this array to wrap elements in s! + /// + /// + /// The argument names. Pass the null string for positional arguments. + /// + /// + /// Whether to allow calling protected constructors. + /// This should be false except when resolving constructor initializers. + /// + /// + /// Statements for Objects/Collections initializer. + /// + /// + /// InvocationResolveResult or ErrorResolveResult + public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList initializerStatements = null) + { + if (type.Kind == TypeKind.Delegate && arguments.Length == 1) { + ResolveResult input = arguments[0]; + IMethod invoke = input.Type.GetDelegateInvokeMethod(); + if (invoke != null) { + input = new MethodGroupResolveResult( + input, invoke.Name, + methods: new[] { new MethodListWithDeclaringType(input.Type) { invoke } }, + typeArguments: EmptyList.Instance + ); + } + return Convert(input, type); + } + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); + MemberLookup lookup = CreateMemberLookup(); + var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List() : null); + foreach (IMethod ctor in type.GetConstructors()) { + if (lookup.IsAccessible(ctor, allowProtectedAccess)) { + var orErrors = or.AddCandidate(ctor); + if (allApplicable != null && OverloadResolution.IsApplicable(orErrors)) + allApplicable.Add(ctor); + } + else + or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible); + } + + if (allApplicable != null && allApplicable.Count > 1) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor. + return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, AddArgumentNamesIfNecessary(arguments, argumentNames), initializerStatements); + } + + if (or.BestCandidate != null) { + return or.CreateResolveResult(null, initializerStatements); + } else { + return new ErrorResolveResult(type); + } + } + #endregion + + #region ResolveSizeOf + /// + /// Resolves 'sizeof(type)'. + /// + public ResolveResult ResolveSizeOf(IType type) + { + IType int32 = compilation.FindType(KnownTypeCode.Int32); + int? size = null; + var typeForConstant = (type.Kind == TypeKind.Enum) ? type.GetDefinition().EnumUnderlyingType : type; + + switch (ReflectionHelper.GetTypeCode(typeForConstant)) { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Byte: + size = 1; + break; + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + size = 2; + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + size = 4; + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + size = 8; + break; + } + return new SizeOfResolveResult(int32, type, size); + } + #endregion + + #region Resolve This/Base Reference + /// + /// Resolves 'this'. + /// + public ResolveResult ResolveThisReference() + { + ITypeDefinition t = CurrentTypeDefinition; + if (t != null) { + if (t.TypeParameterCount != 0) { + // Self-parameterize the type + return new ThisResolveResult(new ParameterizedType(t, t.TypeParameters)); + } else { + return new ThisResolveResult(t); + } + } + return ErrorResult; + } + + /// + /// Resolves 'base'. + /// + public ResolveResult ResolveBaseReference() + { + ITypeDefinition t = CurrentTypeDefinition; + if (t != null) { + foreach (IType baseType in t.DirectBaseTypes) { + if (baseType.Kind != TypeKind.Unknown && baseType.Kind != TypeKind.Interface) { + return new ThisResolveResult(baseType, causesNonVirtualInvocation: true); + } + } + } + return ErrorResult; + } + #endregion + + #region ResolveConditional + /// + /// Converts the input to bool using the rules for boolean expressions. + /// That is, operator true is used if a regular conversion to bool is not possible. + /// + public ResolveResult ResolveCondition(ResolveResult input) + { + if (input == null) + throw new ArgumentNullException("input"); + IType boolean = compilation.FindType(KnownTypeCode.Boolean); + Conversion c = conversions.ImplicitConversion(input, boolean); + if (!c.IsValid) { + var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault(); + if (opTrue != null) { + c = Conversion.UserDefinedConversion(opTrue, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); + } + } + return Convert(input, boolean, c); + } + + /// + /// Converts the negated input to bool using the rules for boolean expressions. + /// Computes !(bool)input if the implicit cast to bool is valid; otherwise + /// computes input.operator false(). + /// + public ResolveResult ResolveConditionFalse(ResolveResult input) + { + if (input == null) + throw new ArgumentNullException("input"); + IType boolean = compilation.FindType(KnownTypeCode.Boolean); + Conversion c = conversions.ImplicitConversion(input, boolean); + if (!c.IsValid) { + var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault(); + if (opFalse != null) { + c = Conversion.UserDefinedConversion(opFalse, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); + return Convert(input, boolean, c); + } + } + return ResolveUnaryOperator(UnaryOperatorType.Not, Convert(input, boolean, c)); + } + + public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression) + { + // C# 4.0 spec §7.14: Conditional operator + + bool isValid; + IType resultType; + if (trueExpression.Type.Kind == TypeKind.Dynamic || falseExpression.Type.Kind == TypeKind.Dynamic) { + resultType = SpecialType.Dynamic; + isValid = TryConvert(ref trueExpression, resultType) & TryConvert(ref falseExpression, resultType); + } else if (HasType(trueExpression) && HasType(falseExpression)) { + Conversion t2f = conversions.ImplicitConversion(trueExpression, falseExpression.Type); + Conversion f2t = conversions.ImplicitConversion(falseExpression, trueExpression.Type); + // The operator is valid: + // a) if there's a conversion in one direction but not the other + // b) if there are conversions in both directions, and the types are equivalent + if (IsBetterConditionalConversion(t2f, f2t)) { + resultType = falseExpression.Type; + isValid = true; + trueExpression = Convert(trueExpression, resultType, t2f); + } else if (IsBetterConditionalConversion(f2t, t2f)) { + resultType = trueExpression.Type; + isValid = true; + falseExpression = Convert(falseExpression, resultType, f2t); + } else { + resultType = trueExpression.Type; + isValid = trueExpression.Type.Equals(falseExpression.Type); + } + } else if (HasType(trueExpression)) { + resultType = trueExpression.Type; + isValid = TryConvert(ref falseExpression, resultType); + } else if (HasType(falseExpression)) { + resultType = falseExpression.Type; + isValid = TryConvert(ref trueExpression, resultType); + } else { + return ErrorResult; + } + condition = ResolveCondition(condition); + if (isValid) { + if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) { + bool? val = condition.ConstantValue as bool?; + if (val == true) + return trueExpression; + else if (val == false) + return falseExpression; + } + return new OperatorResolveResult(resultType, System.Linq.Expressions.ExpressionType.Conditional, + condition, trueExpression, falseExpression); + } else { + return new ErrorResolveResult(resultType); + } + } + + bool IsBetterConditionalConversion(Conversion c1, Conversion c2) + { + // Valid is better than ImplicitConstantExpressionConversion is better than invalid + if (!c1.IsValid) + return false; + if (c1 != Conversion.ImplicitConstantExpressionConversion && c2 == Conversion.ImplicitConstantExpressionConversion) + return true; + return !c2.IsValid; + } + + bool HasType(ResolveResult r) + { + return r.Type.Kind != TypeKind.Unknown && r.Type.Kind != TypeKind.Null; + } + #endregion + + #region ResolvePrimitive + public ResolveResult ResolvePrimitive(object value) + { + if (value == null) { + return new ResolveResult(SpecialType.NullType); + } else { + TypeCode typeCode = Type.GetTypeCode(value.GetType()); + IType type = compilation.FindType(typeCode); + return new ConstantResolveResult(type, value); + } + } + #endregion + + #region ResolveDefaultValue + public ResolveResult ResolveDefaultValue(IType type) + { + return new ConstantResolveResult(type, GetDefaultValue(type)); + } + + public static object GetDefaultValue(IType type) + { + ITypeDefinition typeDef = type.GetDefinition(); + if (typeDef == null) + return null; + if (typeDef.Kind == TypeKind.Enum) { + typeDef = typeDef.EnumUnderlyingType.GetDefinition(); + if (typeDef == null) + return null; + } + switch (typeDef.KnownTypeCode) { + case KnownTypeCode.Boolean: + return false; + case KnownTypeCode.Char: + return '\0'; + case KnownTypeCode.SByte: + return (sbyte)0; + case KnownTypeCode.Byte: + return (byte)0; + case KnownTypeCode.Int16: + return (short)0; + case KnownTypeCode.UInt16: + return (ushort)0; + case KnownTypeCode.Int32: + return 0; + case KnownTypeCode.UInt32: + return 0U; + case KnownTypeCode.Int64: + return 0L; + case KnownTypeCode.UInt64: + return 0UL; + case KnownTypeCode.Single: + return 0f; + case KnownTypeCode.Double: + return 0.0; + case KnownTypeCode.Decimal: + return 0m; + default: + return null; + } + } + #endregion + + #region ResolveArrayCreation + /// + /// Resolves an array creation. + /// + /// + /// The array element type. + /// Pass null to resolve an implicitly-typed array creation. + /// + /// + /// The size arguments. + /// The length of this array will be used as the number of dimensions of the array type. + /// Negative values will be treated as errors. + /// + /// + /// The initializer elements. May be null if no array initializer was specified. + /// The resolver may mutate this array to wrap elements in s! + /// + public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, int[] sizeArguments, ResolveResult[] initializerElements = null) + { + ResolveResult[] sizeArgResults = new ResolveResult[sizeArguments.Length]; + for (int i = 0; i < sizeArguments.Length; i++) { + if (sizeArguments[i] < 0) + sizeArgResults[i] = ErrorResolveResult.UnknownError; + else + sizeArgResults[i] = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), sizeArguments[i]); + } + return ResolveArrayCreation(elementType, sizeArgResults, initializerElements); + } + + /// + /// Resolves an array creation. + /// + /// + /// The array element type. + /// Pass null to resolve an implicitly-typed array creation. + /// + /// + /// The size arguments. + /// The length of this array will be used as the number of dimensions of the array type. + /// The resolver may mutate this array to wrap elements in s! + /// + /// + /// The initializer elements. May be null if no array initializer was specified. + /// The resolver may mutate this array to wrap elements in s! + /// + public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, ResolveResult[] sizeArguments, ResolveResult[] initializerElements = null) + { + int dimensions = sizeArguments.Length; + if (dimensions == 0) + throw new ArgumentException("sizeArguments.Length must not be 0"); + if (elementType == null) { + TypeInference typeInference = new TypeInference(compilation, conversions); + bool success; + elementType = typeInference.GetBestCommonType(initializerElements, out success); + } + IType arrayType = new ArrayType(compilation, elementType, dimensions); + + AdjustArrayAccessArguments(sizeArguments); + + if (initializerElements != null) { + for (int i = 0; i < initializerElements.Length; i++) { + initializerElements[i] = Convert(initializerElements[i], elementType); + } + } + return new ArrayCreateResolveResult(arrayType, sizeArguments, initializerElements); + } + #endregion + + public ResolveResult ResolveTypeOf(IType referencedType) + { + return new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), referencedType); + } + + #region ResolveAssignment + public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult lhs, ResolveResult rhs) + { + var linqOp = AssignmentExpression.GetLinqNodeType(op, this.CheckForOverflow); + var bop = AssignmentExpression.GetCorrespondingBinaryOperator(op); + if (bop == null) { + return new OperatorResolveResult(lhs.Type, linqOp, lhs, this.Convert(rhs, lhs.Type)); + } + ResolveResult bopResult = ResolveBinaryOperator(bop.Value, lhs, rhs); + OperatorResolveResult opResult = bopResult as OperatorResolveResult; + if (opResult == null || opResult.Operands.Count != 2) + return bopResult; + return new OperatorResolveResult(lhs.Type, linqOp, opResult.UserDefinedOperatorMethod, opResult.IsLiftedOperator, + new [] { lhs, opResult.Operands[1] }); + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CastResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CastResolveResult.cs new file mode 100644 index 000000000..7d27e4e0e --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CastResolveResult.cs @@ -0,0 +1,63 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents an explicitly applied conversion (CastExpression or AsExpression) + /// (a result belonging to an AST node; not implicitly inserted 'between' nodes). + /// + class CastResolveResult : ConversionResolveResult + { + // The reason this class exists is that for code like this: + // int i = ...; + // long n = 0; + // n = n + (long)i; + // The resolver will produce (and process) an CastResolveResult for the cast, + // (with Conversion = implicit numeric conversion) + // and then pass it into CSharpResolver.ResolveBinaryOperator(). + // That method normally wraps input arguments into another conversion + // (the implicit conversion applied by the operator). + // However, identity conversions do not cause the creation of ConversionResolveResult instances, + // so the OperatorResolveResult's argument will be the CastResolveResult + // of the cast. + // Without this class (and instead using ConversionResolveResult for both purposes), + // it would be hard for the conversion-processing code + // in the ResolveVisitor to distinguish the existing conversion from the CastExpression + // from an implicit conversion introduced by the binary operator. + // This would cause the conversion to be processed yet again. + // The following unit tests would fail without this class: + // * CastTests.ExplicitConversion_In_Assignment + // * FindReferencesTest.FindReferencesForOpImplicitInAssignment_ExplicitCast + // * CS0029InvalidConversionIssueTests.ExplicitConversionFromUnknownType + + public CastResolveResult(ConversionResolveResult rr) + : base(rr.Type, rr.Input, rr.Conversion, rr.CheckForOverflow) + { + } + + public CastResolveResult(IType targetType, ResolveResult input, Conversion conversion, bool checkForOverflow) + : base(targetType, input, conversion, checkForOverflow) + { + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CompositeResolveVisitorNavigator.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CompositeResolveVisitorNavigator.cs new file mode 100644 index 000000000..a9cc22e26 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CompositeResolveVisitorNavigator.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + public sealed class CompositeResolveVisitorNavigator : IResolveVisitorNavigator + { + IResolveVisitorNavigator[] navigators; + + public CompositeResolveVisitorNavigator(params IResolveVisitorNavigator[] navigators) + { + if (navigators == null) + throw new ArgumentNullException("navigators"); + this.navigators = navigators; + foreach (var n in navigators) { + if (n == null) + throw new ArgumentException("Array must not contain nulls."); + } + } + + public ResolveVisitorNavigationMode Scan(AstNode node) + { + bool needsScan = false; + foreach (var navigator in navigators) { + ResolveVisitorNavigationMode mode = navigator.Scan(node); + if (mode == ResolveVisitorNavigationMode.Resolve) + return mode; // resolve has highest priority + else if (mode == ResolveVisitorNavigationMode.Scan) + needsScan = true; + } + return needsScan ? ResolveVisitorNavigationMode.Scan : ResolveVisitorNavigationMode.Skip; + } + + public void Resolved(AstNode node, ResolveResult result) + { + foreach (var navigator in navigators) { + navigator.Resolved(node, result); + } + } + + public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + { + foreach (var navigator in navigators) { + navigator.ProcessConversion(expression, result, conversion, targetType); + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/DetectSkippableNodesNavigator.cs b/ICSharpCode.Decompiler/CSharp/Resolver/DetectSkippableNodesNavigator.cs new file mode 100644 index 000000000..a8c04f1f9 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/DetectSkippableNodesNavigator.cs @@ -0,0 +1,88 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// When an is searching for specific nodes + /// (e.g. all IdentifierExpressions), it has to scan the whole syntax tree for those nodes. + /// However, scanning in the ResolveVisitor is expensive (e.g. any lambda that is scanned must be resolved), + /// so it makes sense to detect when a whole subtree is scan-only, and skip that tree instead. + /// + /// The DetectSkippableNodesNavigator performs this job by running the input IResolveVisitorNavigator + /// over the whole AST, and detecting subtrees that are scan-only, and replaces them with Skip. + /// + public sealed class DetectSkippableNodesNavigator : IResolveVisitorNavigator + { + readonly Dictionary dict = new Dictionary(); + IResolveVisitorNavigator navigator; + + public DetectSkippableNodesNavigator(IResolveVisitorNavigator navigator, AstNode root) + { + this.navigator = navigator; + Init(root); + } + + bool Init(AstNode node) + { + var mode = navigator.Scan(node); + if (mode == ResolveVisitorNavigationMode.Skip) + return false; + + bool needsResolve = (mode != ResolveVisitorNavigationMode.Scan); + + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + needsResolve |= Init(child); + } + + if (needsResolve) { + // If this node or any child node needs resolving, store the mode in the dictionary. + dict.Add(node, mode); + } + return needsResolve; + } + + /// + public ResolveVisitorNavigationMode Scan(AstNode node) + { + ResolveVisitorNavigationMode mode; + if (dict.TryGetValue(node, out mode)) { + return mode; + } else { + return ResolveVisitorNavigationMode.Skip; + } + } + + /// + public void Resolved(AstNode node, ResolveResult result) + { + navigator.Resolved(node, result); + } + + /// + public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + { + navigator.ProcessConversion(expression, result, conversion, targetType); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/DynamicInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/DynamicInvocationResolveResult.cs new file mode 100644 index 000000000..e21c5b30d --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/DynamicInvocationResolveResult.cs @@ -0,0 +1,86 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + public enum DynamicInvocationType { + /// + /// The invocation is a normal invocation ( 'a(b)' ). + /// + Invocation, + + /// + /// The invocation is an indexing ( 'a[b]' ). + /// + Indexing, + + /// + /// The invocation is an object creation ( 'new a(b)' ). Also used when invoking a base constructor ( ' : base(a) ' ) and chaining constructors ( ' : this(a) '). + /// + ObjectCreation, + } + + /// + /// Represents the result of an invocation of a member of a dynamic object. + /// + public class DynamicInvocationResolveResult : ResolveResult + { + /// + /// Target of the invocation. Can be a dynamic expression or a . + /// + public readonly ResolveResult Target; + + /// + /// Type of the invocation. + /// + public readonly DynamicInvocationType InvocationType; + + /// + /// Arguments for the call. Named arguments will be instances of . + /// + public readonly IList Arguments; + + /// + /// Gets the list of initializer statements that are appplied to the result of this invocation. + /// This is used to represent object and collection initializers. + /// With the initializer statements, the is used + /// to refer to the result of this invocation. + /// Initializer statements can only exist if the is . + /// + public readonly IList InitializerStatements; + + public DynamicInvocationResolveResult(ResolveResult target, DynamicInvocationType invocationType, IList arguments, IList initializerStatements = null) : base(SpecialType.Dynamic) { + this.Target = target; + this.InvocationType = invocationType; + this.Arguments = arguments ?? EmptyList.Instance; + this.InitializerStatements = initializerStatements ?? EmptyList.Instance; + } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "[Dynamic invocation ]"); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/DynamicMemberResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/DynamicMemberResolveResult.cs new file mode 100644 index 000000000..746931b6a --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/DynamicMemberResolveResult.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents the result of an access to a member of a dynamic object. + /// + public class DynamicMemberResolveResult : ResolveResult + { + /// + /// Target of the member access (a dynamic object). + /// + public readonly ResolveResult Target; + + /// + /// Name of the accessed member. + /// + public readonly string Member; + + public DynamicMemberResolveResult(ResolveResult target, string member) : base(SpecialType.Dynamic) { + this.Target = target; + this.Member = member; + } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "[Dynamic member '{0}']", Member); + } + + public override IEnumerable GetChildResults() { + return new[] { Target }; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/FindReferencedEntities.cs b/ICSharpCode.Decompiler/CSharp/Resolver/FindReferencedEntities.cs new file mode 100644 index 000000000..df82588c4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/FindReferencedEntities.cs @@ -0,0 +1,100 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Find all entities that are referenced in the scanned AST. + /// + public sealed class FindReferencedEntities : IResolveVisitorNavigator + { + readonly Action memberReferenceFound; + readonly Action typeReferenceFound; + + /// + /// Creates a new FindReferencedEntities instance that + /// looks for entity definitions. + /// The visitor will report type definitions and member definitions (not specialized members). + /// + public FindReferencedEntities(Action referenceFound) + { + if (referenceFound == null) + throw new ArgumentNullException("referenceFound"); + this.memberReferenceFound = (node, member) => referenceFound(node, member.MemberDefinition); + this.typeReferenceFound = (node, type) => { + var def = type.GetDefinition(); + if (def != null) + referenceFound(node, def); + }; + } + + /// + /// Creates a new FindReferencedEntities instance that + /// looks for types and members. + /// The visitor will report parameterized types and potentially specialized members. + /// + public FindReferencedEntities(Action typeReferenceFound, Action memberReferenceFound) + { + if (typeReferenceFound == null) + throw new ArgumentNullException("typeReferenceFound"); + if (memberReferenceFound == null) + throw new ArgumentNullException("memberReferenceFound"); + this.typeReferenceFound = typeReferenceFound; + this.memberReferenceFound = memberReferenceFound; + } + + public ResolveVisitorNavigationMode Scan(AstNode node) + { + return ResolveVisitorNavigationMode.Resolve; + } + + public void Resolved(AstNode node, ResolveResult result) + { + if (ParenthesizedExpression.ActsAsParenthesizedExpression(node)) + return; + + MemberResolveResult mrr = result as MemberResolveResult; + if (mrr != null) { + memberReferenceFound(node, mrr.Member); + } + TypeResolveResult trr = result as TypeResolveResult; + if (trr != null) { + typeReferenceFound(node, trr.Type); + } + ForEachResolveResult ferr = result as ForEachResolveResult; + if (ferr != null) { + Resolved(node, ferr.GetEnumeratorCall); + if (ferr.CurrentProperty != null) + memberReferenceFound(node, ferr.CurrentProperty); + if (ferr.MoveNextMethod != null) + memberReferenceFound(node, ferr.MoveNextMethod); + } + } + + public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + { + if (conversion.IsUserDefined || conversion.IsMethodGroupConversion) { + memberReferenceFound(expression, conversion.Method); + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/IResolveVisitorNavigator.cs b/ICSharpCode.Decompiler/CSharp/Resolver/IResolveVisitorNavigator.cs new file mode 100644 index 000000000..75c9603fc --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/IResolveVisitorNavigator.cs @@ -0,0 +1,103 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Allows controlling which nodes are resolved by the resolve visitor. + /// + /// + public interface IResolveVisitorNavigator + { + /// + /// Asks the navigator whether to scan, skip, or resolve a node. + /// + ResolveVisitorNavigationMode Scan(AstNode node); + + /// + /// Notifies the navigator that a node was resolved. + /// + /// The node that was resolved + /// Resolve result + void Resolved(AstNode node, ResolveResult result); + + /// + /// Notifies the navigator that an implicit conversion was applied. + /// + /// The expression that was resolved. + /// The resolve result of the expression. + /// The conversion applied to the expressed. + /// The target type of the conversion. + void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType); + } + + /// + /// Represents the operation mode of the resolve visitor. + /// + /// + public enum ResolveVisitorNavigationMode + { + /// + /// Scan into the children of the current node, without resolving the current node. + /// + Scan, + /// + /// Skip the current node - do not scan into it; do not resolve it. + /// + Skip, + /// + /// Resolve the current node. + /// Subnodes which are not required for resolving the current node + /// will ask the navigator again whether they should be resolved. + /// + Resolve + } + + sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator + { + readonly ResolveVisitorNavigationMode mode; + readonly IResolveVisitorNavigator targetForResolveCalls; + + public ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode mode, IResolveVisitorNavigator targetForResolveCalls) + { + this.mode = mode; + this.targetForResolveCalls = targetForResolveCalls; + } + + ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) + { + return mode; + } + + void IResolveVisitorNavigator.Resolved(AstNode node, ResolveResult result) + { + if (targetForResolveCalls != null) + targetForResolveCalls.Resolved(node, result); + } + + void IResolveVisitorNavigator.ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + { + if (targetForResolveCalls != null) + targetForResolveCalls.ProcessConversion(expression, result, conversion, targetType); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs new file mode 100644 index 000000000..850fc6514 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs @@ -0,0 +1,101 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents an anonymous method or lambda expression. + /// Note: the lambda has no type. + /// To retrieve the delegate type, look at the anonymous function conversion. + /// + public abstract class LambdaResolveResult : ResolveResult + { + protected LambdaResolveResult() : base(SpecialType.UnknownType) + { + } + + /// + /// Gets whether there is a parameter list. + /// This property always returns true for C# 3.0-lambdas, but may return false + /// for C# 2.0 anonymous methods. + /// + public abstract bool HasParameterList { get; } + + /// + /// Gets whether this lambda is using the C# 2.0 anonymous method syntax. + /// + public abstract bool IsAnonymousMethod { get; } + + /// + /// Gets whether the lambda parameters are implicitly typed. + /// + /// This property returns false for anonymous methods without parameter list. + public abstract bool IsImplicitlyTyped { get; } + + /// + /// Gets whether the lambda is async. + /// + public abstract bool IsAsync { get; } + + /// + /// Gets the return type inferred when the parameter types are inferred to be + /// + /// + /// This method determines the return type inferred from the lambda body, which is used as part of C# type inference. + /// Use the property to retrieve the actual return type as determined by the target delegate type. + /// + public abstract IType GetInferredReturnType(IType[] parameterTypes); + + /// + /// Gets the list of parameters. + /// + public abstract IList Parameters { get; } + + /// + /// Gets the return type of the lambda. + /// + /// If the lambda is async, the return type includes Task<T> + /// + public abstract IType ReturnType { get; } + + /// + /// Gets whether the lambda body is valid for the given parameter types and return type. + /// + /// + /// Produces a conversion with =true if the lambda is valid; + /// otherwise returns . + /// + public abstract Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions); + + /// + /// Gets the resolve result for the lambda body. + /// Returns a resolve result for 'void' for statement lambdas. + /// + public abstract ResolveResult Body { get; } + + public override IEnumerable GetChildResults() + { + return new [] { this.Body }; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs b/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs new file mode 100644 index 000000000..f5ea9395b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs @@ -0,0 +1,94 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Resolver logging helper. + /// Wraps System.Diagnostics.Debug so that resolver-specific logging can be enabled/disabled on demand. + /// (it's a huge amount of debug spew and slows down the resolver quite a bit) + /// + static class Log + { + const bool logEnabled = false; +#if __MonoCS__ + [Conditional("MCS_DEBUG")] +#else + [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] +#endif + internal static void WriteLine(string text) + { + Debug.WriteLine(text); + } + +#if __MonoCS__ + [Conditional("MCS_DEBUG")] +#else + [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] +#endif + internal static void WriteLine(string format, params object[] args) + { + Debug.WriteLine(format, args); + } + +#if __MonoCS__ + [Conditional("MCS_DEBUG")] +#else + [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] +#endif + internal static void WriteCollection(string text, IEnumerable lines) + { + #if DEBUG + T[] arr = lines.ToArray(); + if (arr.Length == 0) { + Debug.WriteLine(text + ""); + } else { + Debug.WriteLine(text + (arr[0] != null ? arr[0].ToString() : "")); + for (int i = 1; i < arr.Length; i++) { + Debug.WriteLine(new string(' ', text.Length) + (arr[i] != null ? arr[i].ToString() : "")); + } + } + #endif + } + +#if __MonoCS__ + [Conditional("MCS_DEBUG")] +#else + [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] +#endif + public static void Indent() + { + Debug.Indent(); + } + +#if __MonoCS__ + [Conditional("MCS_DEBUG")] +#else + [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] +#endif + public static void Unindent() + { + Debug.Unindent(); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs new file mode 100644 index 000000000..0bb00cd26 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs @@ -0,0 +1,696 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Implementation of member lookup (C# 4.0 spec, §7.4). + /// + public class MemberLookup + { + #region Static helper methods + /// + /// Gets whether the member is considered to be invocable. + /// + public static bool IsInvocable(IMember member) + { + if (member == null) + throw new ArgumentNullException("member"); + // C# 4.0 spec, §7.4 member lookup + if (member is IEvent || member is IMethod) + return true; + IType returnType = member.ReturnType; + return returnType.Kind == TypeKind.Dynamic || returnType.Kind == TypeKind.Delegate; + } + #endregion + + readonly ITypeDefinition currentTypeDefinition; + readonly IAssembly currentAssembly; + readonly bool isInEnumMemberInitializer; + + public MemberLookup(ITypeDefinition currentTypeDefinition, IAssembly currentAssembly, bool isInEnumMemberInitializer = false) + { + this.currentTypeDefinition = currentTypeDefinition; + this.currentAssembly = currentAssembly; + this.isInEnumMemberInitializer = isInEnumMemberInitializer; + } + + #region IsAccessible + /// + /// Gets whether access to protected instance members of the target expression is possible. + /// + public bool IsProtectedAccessAllowed(ResolveResult targetResolveResult) + { + return targetResolveResult is ThisResolveResult || IsProtectedAccessAllowed(targetResolveResult.Type); + } + + /// + /// Gets whether access to protected instance members of the target type is possible. + /// + /// + /// This method does not consider the special case of the 'base' reference. If possible, use the + /// IsProtectedAccessAllowed(ResolveResult) overload instead. + /// + public bool IsProtectedAccessAllowed(IType targetType) + { + if (targetType.Kind == TypeKind.TypeParameter) + targetType = ((ITypeParameter)targetType).EffectiveBaseClass; + ITypeDefinition typeDef = targetType.GetDefinition(); + if (typeDef == null) + return false; + for (ITypeDefinition c = currentTypeDefinition; c != null; c = c.DeclaringTypeDefinition) { + if (typeDef.IsDerivedFrom(c)) + return true; + } + return false; + } + + /// + /// Gets whether is accessible in the current class. + /// + /// The entity to test + /// + /// Whether protected access to instance members is allowed. + /// True if the type of the reference is derived from the current class. + /// Protected static members may be accessible even if false is passed for this parameter. + /// + public bool IsAccessible(IEntity entity, bool allowProtectedAccess) + { + if (entity == null) + throw new ArgumentNullException("entity"); + // C# 4.0 spec, §3.5.2 Accessiblity domains + switch (entity.Accessibility) { + case Accessibility.None: + return false; + case Accessibility.Private: + // check for members of outer classes (private members of outer classes can be accessed) + for (var t = currentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { + if (t.Equals(entity.DeclaringTypeDefinition)) + return true; + } + return false; + case Accessibility.Public: + return true; + case Accessibility.Protected: + return IsProtectedAccessible(allowProtectedAccess, entity); + case Accessibility.Internal: + return IsInternalAccessible(entity.ParentAssembly); + case Accessibility.ProtectedOrInternal: + return IsInternalAccessible(entity.ParentAssembly) || IsProtectedAccessible(allowProtectedAccess, entity); + case Accessibility.ProtectedAndInternal: + return IsInternalAccessible(entity.ParentAssembly) && IsProtectedAccessible(allowProtectedAccess, entity); + default: + throw new Exception("Invalid value for Accessibility"); + } + } + + bool IsInternalAccessible(IAssembly assembly) + { + return assembly != null && currentAssembly != null && assembly.InternalsVisibleTo(currentAssembly); + } + + bool IsProtectedAccessible(bool allowProtectedAccess, IEntity entity) + { + // For static members and type definitions, we do not require the qualifying reference + // to be derived from the current class (allowProtectedAccess). + if (entity.IsStatic || entity.SymbolKind == SymbolKind.TypeDefinition) + allowProtectedAccess = true; + + for (var t = currentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { + if (t.Equals(entity.DeclaringTypeDefinition)) + return true; + + // PERF: this might hurt performance as this method is called several times (once for each member) + // make sure resolving base types is cheap (caches?) or cache within the MemberLookup instance + if (allowProtectedAccess && t.IsDerivedFrom(entity.DeclaringTypeDefinition)) + return true; + } + return false; + } + #endregion + + #region GetAccessibleMembers + /// + /// Retrieves all members that are accessible and not hidden (by being overridden or shadowed). + /// Returns both members and nested type definitions. Does not include extension methods. + /// + public IEnumerable GetAccessibleMembers(ResolveResult targetResolveResult) + { + if (targetResolveResult == null) + throw new ArgumentNullException("targetResolveResult"); + + bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; + bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); + + // maps the member name to the list of lookup groups + var lookupGroupDict = new Dictionary>(); + + // This loop will handle base types before derived types. + // The loop performs three jobs: + // 1) It marks entries in lookup groups from base classes as removed when those members + // are hidden by a derived class. + // 2) It adds a new lookup group with the members from a declaring type. + // 3) It replaces virtual members with the overridden version, placing the override in the + // lookup group belonging to the base class. + foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes()) { + + List entities = new List(); + entities.AddRange(type.GetMembers(options: GetMemberOptions.IgnoreInheritedMembers)); + if (!targetIsTypeParameter) { + var nestedTypes = type.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions); + // GetDefinition() might return null if some IType has a strange implementation of GetNestedTypes. + entities.AddRange(nestedTypes.Select(t => t.GetDefinition()).Where(td => td != null)); + } + + foreach (var entityGroup in entities.GroupBy(e => e.Name)) { + + List lookupGroups = new List(); + if (!lookupGroupDict.TryGetValue(entityGroup.Key, out lookupGroups)) + lookupGroupDict.Add(entityGroup.Key, lookupGroups = new List()); + + List newNestedTypes = null; + List newMethods = null; + IMember newNonMethod = null; + + IEnumerable typeBaseTypes = null; + + if (!targetIsTypeParameter) { + AddNestedTypes(type, entityGroup.OfType(), 0, lookupGroups, ref typeBaseTypes, ref newNestedTypes); + } + AddMembers(type, entityGroup.OfType(), allowProtectedAccess, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod); + + if (newNestedTypes != null || newMethods != null || newNonMethod != null) + lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod)); + } + } + + foreach (List lookupGroups in lookupGroupDict.Values) { + // Remove interface members hidden by class members. + if (targetIsTypeParameter) { + // This can happen only with type parameters. + RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); + } + + // Now report the results: + foreach (LookupGroup lookupGroup in lookupGroups) { + if (!lookupGroup.MethodsAreHidden) { + foreach (IMethod method in lookupGroup.Methods) { + yield return method; + } + } + if (!lookupGroup.NonMethodIsHidden) { + yield return lookupGroup.NonMethod; + } + if (lookupGroup.NestedTypes != null) { + foreach (IType type in lookupGroup.NestedTypes) { + ITypeDefinition typeDef = type.GetDefinition(); + if (typeDef != null) + yield return typeDef; + } + } + } + } + } + #endregion + + #region class LookupGroup + sealed class LookupGroup + { + public readonly IType DeclaringType; + + // When a nested type is hidden, it is simply removed from the list. + public List NestedTypes; + + // When members are hidden, they are merely marked as hidden. + // We still need to store the hidden methods so that the 'override' processing can + // find them, so that it won't introduce the override as a new method. + public readonly List Methods; + public bool MethodsAreHidden; + + public IMember NonMethod; + public bool NonMethodIsHidden; + + public LookupGroup(IType declaringType, List nestedTypes, List methods, IMember nonMethod) + { + this.DeclaringType = declaringType; + this.NestedTypes = nestedTypes; + this.Methods = methods; + this.NonMethod = nonMethod; + this.MethodsAreHidden = (methods == null || methods.Count == 0); + this.NonMethodIsHidden = (nonMethod == null); + } + + public bool AllHidden { + get { + if (NestedTypes != null && NestedTypes.Count > 0) + return false; + return NonMethodIsHidden && MethodsAreHidden; + } + } + } + #endregion + + #region LookupType + public ResolveResult LookupType(IType declaringType, string name, IList typeArguments, bool parameterizeResultType = true) + { + if (declaringType == null) + throw new ArgumentNullException("declaringType"); + if (name == null) + throw new ArgumentNullException("name"); + if (typeArguments == null) + throw new ArgumentNullException("typeArguments"); + + int typeArgumentCount = typeArguments.Count; + Predicate filter = delegate (ITypeDefinition d) { + return InnerTypeParameterCount(d) == typeArgumentCount && d.Name == name && IsAccessible(d, true); + }; + + List lookupGroups = new List(); + if (declaringType.Kind != TypeKind.TypeParameter) { + foreach (IType type in declaringType.GetNonInterfaceBaseTypes()) { + List newNestedTypes = null; + IEnumerable typeBaseTypes = null; + + IEnumerable nestedTypes; + if (parameterizeResultType) { + nestedTypes = type.GetNestedTypes(typeArguments, filter, GetMemberOptions.IgnoreInheritedMembers); + } else { + nestedTypes = type.GetNestedTypes(filter, GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions); + } + AddNestedTypes(type, nestedTypes, typeArgumentCount, lookupGroups, ref typeBaseTypes, ref newNestedTypes); + + if (newNestedTypes != null) + lookupGroups.Add(new LookupGroup(type, newNestedTypes, null, null)); + } + } + + lookupGroups.RemoveAll(g => g.AllHidden); + Debug.Assert(lookupGroups.All(g => g.NestedTypes != null && g.NestedTypes.Count > 0)); + + if (lookupGroups.Count == 0) { + return new UnknownMemberResolveResult(declaringType, name, typeArguments); + } + + LookupGroup resultGroup = lookupGroups[lookupGroups.Count - 1]; + + if (resultGroup.NestedTypes.Count > 1 || lookupGroups.Count > 1) + return new AmbiguousTypeResolveResult(resultGroup.NestedTypes[0]); + else + return new TypeResolveResult(resultGroup.NestedTypes[0]); + } + + static int InnerTypeParameterCount(IType type) + { + // inner types contain the type parameters of outer types. therefore this count has to been adjusted. + return type.TypeParameterCount - (type.DeclaringType != null ? type.DeclaringType.TypeParameterCount : 0); + } + #endregion + + #region Lookup + /// + /// Performs a member lookup. + /// + public ResolveResult Lookup(ResolveResult targetResolveResult, string name, IList typeArguments, bool isInvocation) + { + if (targetResolveResult == null) + throw new ArgumentNullException("targetResolveResult"); + if (name == null) + throw new ArgumentNullException("name"); + if (typeArguments == null) + throw new ArgumentNullException("typeArguments"); + + bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; + + bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); + Predicate nestedTypeFilter = delegate(ITypeDefinition entity) { + return entity.Name == name && IsAccessible(entity, allowProtectedAccess); + }; + Predicate memberFilter = delegate(IUnresolvedMember entity) { + // NOTE: Atm destructors can be looked up with 'Finalize' + return entity.SymbolKind != SymbolKind.Indexer && + entity.SymbolKind != SymbolKind.Operator && + entity.Name == name; + }; + + List lookupGroups = new List(); + // This loop will handle base types before derived types. + // The loop performs three jobs: + // 1) It marks entries in lookup groups from base classes as removed when those members + // are hidden by a derived class. + // 2) It adds a new lookup group with the members from a declaring type. + // 3) It replaces virtual members with the overridden version, placing the override in the + // lookup group belonging to the base class. + foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes()) { + + List newNestedTypes = null; + List newMethods = null; + IMember newNonMethod = null; + + IEnumerable typeBaseTypes = null; + + if (!isInvocation && !targetIsTypeParameter) { + // Consider nested types only if it's not an invocation. + // type.GetNestedTypes() is checking the type parameter count for an exact match, + // so we don't need to do that in our filter. + var nestedTypes = type.GetNestedTypes(typeArguments, nestedTypeFilter, GetMemberOptions.IgnoreInheritedMembers); + AddNestedTypes(type, nestedTypes, typeArguments.Count, lookupGroups, ref typeBaseTypes, ref newNestedTypes); + } + + IEnumerable members; + if (typeArguments.Count == 0) { + // Note: IsInvocable-checking cannot be done as part of the filter; + // because it must be done after type substitution. + members = type.GetMembers(memberFilter, GetMemberOptions.IgnoreInheritedMembers); + if (isInvocation) + members = members.Where(m => IsInvocable(m)); + } else { + // No need to check for isInvocation/isInvocable here: + // we only fetch methods + members = type.GetMethods(typeArguments, memberFilter, GetMemberOptions.IgnoreInheritedMembers); + } + AddMembers(type, members, allowProtectedAccess, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod); + + if (newNestedTypes != null || newMethods != null || newNonMethod != null) + lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod)); + } + + // Remove interface members hidden by class members. + if (targetIsTypeParameter) { + // This can happen only with type parameters. + RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); + } + + return CreateResult(targetResolveResult, lookupGroups, name, typeArguments); + } + #endregion + + #region Lookup Indexer + /// + /// Looks up the indexers on the target type. + /// + public IList LookupIndexers(ResolveResult targetResolveResult) + { + if (targetResolveResult == null) + throw new ArgumentNullException("targetResolveResult"); + + IType targetType = targetResolveResult.Type; + bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); + Predicate filter = p => p.IsIndexer; + + List lookupGroups = new List(); + foreach (IType type in targetType.GetNonInterfaceBaseTypes()) { + List newMethods = null; + IMember newNonMethod = null; + + IEnumerable typeBaseTypes = null; + + var members = type.GetProperties(filter, GetMemberOptions.IgnoreInheritedMembers); + AddMembers(type, members, allowProtectedAccess, lookupGroups, true, ref typeBaseTypes, ref newMethods, ref newNonMethod); + + if (newMethods != null || newNonMethod != null) + lookupGroups.Add(new LookupGroup(type, null, newMethods, newNonMethod)); + } + + // Remove interface members hidden by class members. + if (targetType.Kind == TypeKind.TypeParameter) { + // This can happen only with type parameters. + RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); + } + + // Remove all hidden groups + lookupGroups.RemoveAll(g => g.MethodsAreHidden || g.Methods.Count == 0); + + MethodListWithDeclaringType[] methodLists = new MethodListWithDeclaringType[lookupGroups.Count]; + for (int i = 0; i < methodLists.Length; i++) { + methodLists[i] = new MethodListWithDeclaringType(lookupGroups[i].DeclaringType, lookupGroups[i].Methods); + } + return methodLists; + } + #endregion + + #region AddNestedTypes + /// + /// Adds the nested types to 'newNestedTypes' and removes any hidden members from the existing lookup groups. + /// + /// Declaring type of the nested types + /// List of nested types to add. + /// The number of type arguments - used for hiding types from the base class + /// List of existing lookup groups + /// The base types of 'type' (initialized on demand) + /// The target list (created on demand). + void AddNestedTypes(IType type, IEnumerable nestedTypes, int typeArgumentCount, + List lookupGroups, + ref IEnumerable typeBaseTypes, + ref List newNestedTypes) + { + foreach (IType nestedType in nestedTypes) { + // Remove all non-types declared in a base type of 'type', + // and all types with same number of type parameters declared in a base type of 'type'. + foreach (var lookupGroup in lookupGroups) { + if (lookupGroup.AllHidden) + continue; // everything is already hidden + if (typeBaseTypes == null) + typeBaseTypes = type.GetNonInterfaceBaseTypes(); + + if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { + lookupGroup.MethodsAreHidden = true; + lookupGroup.NonMethodIsHidden = true; + if (lookupGroup.NestedTypes != null) + lookupGroup.NestedTypes.RemoveAll(t => InnerTypeParameterCount(t) == typeArgumentCount); + } + } + + // Add the new nested type. + if (newNestedTypes == null) + newNestedTypes = new List(); + newNestedTypes.Add(nestedType); + } + } + #endregion + + #region AddMembers + /// + /// Adds members to 'newMethods'/'newNonMethod'. + /// Removes any members in the existing lookup groups that were hidden by added members. + /// Substitutes 'virtual' members in the existing lookup groups for added 'override' members. + /// + /// Declaring type of the members + /// List of members to add. + /// Whether protected members are accessible + /// List of existing lookup groups + /// Whether to treat properties as methods + /// The base types of 'type' (initialized on demand) + /// The target list for methods (created on demand). + /// The target variable for non-method members. + void AddMembers(IType type, IEnumerable members, + bool allowProtectedAccess, + List lookupGroups, + bool treatAllParameterizedMembersAsMethods, + ref IEnumerable typeBaseTypes, ref List newMethods, ref IMember newNonMethod) + { + foreach (IMember member in members) { + if (!IsAccessible(member, allowProtectedAccess)) + continue; + + IParameterizedMember method; + if (treatAllParameterizedMembersAsMethods) + method = member as IParameterizedMember; + else + method = member as IMethod; + + bool replacedVirtualMemberWithOverride = false; + if (member.IsOverride) { + // Replacing virtual member with override: + + // Go backwards so that we find the corresponding virtual member + // in the most-derived type + for (int i = lookupGroups.Count - 1; i >= 0 && !replacedVirtualMemberWithOverride; i--) { + if (typeBaseTypes == null) + typeBaseTypes = type.GetNonInterfaceBaseTypes(); + + var lookupGroup = lookupGroups[i]; + if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { + if (method != null) { + // Find the matching method, and replace it with the override + for (int j = 0; j < lookupGroup.Methods.Count; j++) { + if (SignatureComparer.Ordinal.Equals(method, lookupGroup.Methods[j])) { + lookupGroup.Methods[j] = method; + replacedVirtualMemberWithOverride = true; + break; + } + } + } else { + // If the member type matches, replace it with the override + if (lookupGroup.NonMethod != null && lookupGroup.NonMethod.SymbolKind == member.SymbolKind) { + lookupGroup.NonMethod = member; + replacedVirtualMemberWithOverride = true; + break; + } + } + } + } + } + // If the member wasn't an override, or if we didn't find any matching virtual method, + // proceed to add the member. + if (!replacedVirtualMemberWithOverride) { + // Make the member hide other members: + foreach (var lookupGroup in lookupGroups) { + if (lookupGroup.AllHidden) + continue; // everything is already hidden + if (typeBaseTypes == null) + typeBaseTypes = type.GetNonInterfaceBaseTypes(); + + if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { + // Methods hide all non-methods; Non-methods hide everything + lookupGroup.NestedTypes = null; + lookupGroup.NonMethodIsHidden = true; + if (method == null) { // !(member is IMethod) + lookupGroup.MethodsAreHidden = true; + } + } + } + + // Add the new member + if (method != null) { + if (newMethods == null) + newMethods = new List(); + newMethods.Add(method); + } else { + newNonMethod = member; + } + } + } + } + #endregion + + #region RemoveInterfaceMembersHiddenByClassMembers + void RemoveInterfaceMembersHiddenByClassMembers(List lookupGroups) + { + foreach (var classLookupGroup in lookupGroups) { + if (IsInterfaceOrSystemObject(classLookupGroup.DeclaringType)) + continue; + // The current lookup groups contains class members that might hide interface members + bool hasNestedTypes = classLookupGroup.NestedTypes != null && classLookupGroup.NestedTypes.Count > 0; + if (hasNestedTypes || !classLookupGroup.NonMethodIsHidden) { + // Hide all members from interface types + foreach (var interfaceLookupGroup in lookupGroups) { + if (IsInterfaceOrSystemObject(interfaceLookupGroup.DeclaringType)) { + interfaceLookupGroup.NestedTypes = null; + interfaceLookupGroup.NonMethodIsHidden = true; + interfaceLookupGroup.MethodsAreHidden = true; + } + } + } else if (!classLookupGroup.MethodsAreHidden) { + foreach (var classMethod in classLookupGroup.Methods) { + // Hide all non-methods from interface types, and all methods with the same signature + // as a method in this class type. + foreach (var interfaceLookupGroup in lookupGroups) { + if (IsInterfaceOrSystemObject(interfaceLookupGroup.DeclaringType)) { + interfaceLookupGroup.NestedTypes = null; + interfaceLookupGroup.NonMethodIsHidden = true; + if (interfaceLookupGroup.Methods != null && !interfaceLookupGroup.MethodsAreHidden) { + // The mapping of virtual to overridden methods is already done, + // so we can simply remove the methods from the collection + interfaceLookupGroup.Methods.RemoveAll( + m => SignatureComparer.Ordinal.Equals(classMethod, m)); + } + } + } + } + } + } + } + + static bool IsInterfaceOrSystemObject(IType type) + { + // return true if type is an interface or System.Object + if (type.Kind == TypeKind.Interface) + return true; + ITypeDefinition d = type.GetDefinition(); + return d != null && d.KnownTypeCode == KnownTypeCode.Object; + } + #endregion + + #region CreateResult + ResolveResult CreateResult(ResolveResult targetResolveResult, List lookupGroups, string name, IList typeArguments) + { + // Remove all hidden groups + lookupGroups.RemoveAll(g => g.AllHidden); + + if (lookupGroups.Count == 0) { + // No members found + return new UnknownMemberResolveResult(targetResolveResult.Type, name, typeArguments); + } + + if (lookupGroups.Any(g => !g.MethodsAreHidden && g.Methods.Count > 0)) { + // If there are methods, make a MethodGroupResolveResult. + // Note that a conflict between a member and a method (possible with multiple interface inheritance) + // is only a warning, not an error, and the C# compiler will prefer the method group. + List methodLists = new List(); + foreach (var lookupGroup in lookupGroups) { + if (!lookupGroup.MethodsAreHidden && lookupGroup.Methods.Count > 0) { + var methodListWithDeclType = new MethodListWithDeclaringType(lookupGroup.DeclaringType); + foreach (var method in lookupGroup.Methods) { + methodListWithDeclType.Add((IMethod)method); + } + methodLists.Add(methodListWithDeclType); + } + } + + return new MethodGroupResolveResult(targetResolveResult, name, methodLists, typeArguments); + } + + // If there are ambiguities, report the most-derived result (last group) + LookupGroup resultGroup = lookupGroups[lookupGroups.Count - 1]; + if (resultGroup.NestedTypes != null && resultGroup.NestedTypes.Count > 0) { + if (resultGroup.NestedTypes.Count > 1 || !resultGroup.NonMethodIsHidden || lookupGroups.Count > 1) + return new AmbiguousTypeResolveResult(resultGroup.NestedTypes[0]); + else + return new TypeResolveResult(resultGroup.NestedTypes[0]); + } + + if (resultGroup.NonMethod.IsStatic && targetResolveResult is ThisResolveResult) { + targetResolveResult = new TypeResolveResult(targetResolveResult.Type); + } + + if (lookupGroups.Count > 1) { + return new AmbiguousMemberResolveResult(targetResolveResult, resultGroup.NonMethod); + } else { + if (isInEnumMemberInitializer) { + IField field = resultGroup.NonMethod as IField; + if (field != null && field.DeclaringTypeDefinition != null && field.DeclaringTypeDefinition.Kind == TypeKind.Enum) { + return new MemberResolveResult( + targetResolveResult, field, + field.DeclaringTypeDefinition.EnumUnderlyingType, + field.IsConst, field.ConstantValue); + } + } + return new MemberResolveResult(targetResolveResult, resultGroup.NonMethod); + } + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs new file mode 100644 index 000000000..d1b51248a --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs @@ -0,0 +1,292 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// A method list that belongs to a declaring type. + /// + public class MethodListWithDeclaringType : List + { + readonly IType declaringType; + + /// + /// The declaring type. + /// + /// + /// Not all methods in this list necessarily have this as their declaring type. + /// For example, this program: + /// + /// class Base { + /// public virtual void M() {} + /// } + /// class Derived : Base { + /// public override void M() {} + /// public void M(int i) {} + /// } + /// + /// results in two lists: + /// new MethodListWithDeclaringType(Base) { Derived.M() }, + /// new MethodListWithDeclaringType(Derived) { Derived.M(int) } + /// + public IType DeclaringType { + get { return declaringType; } + } + + public MethodListWithDeclaringType(IType declaringType) + { + this.declaringType = declaringType; + } + + public MethodListWithDeclaringType(IType declaringType, IEnumerable methods) + : base(methods) + { + this.declaringType = declaringType; + } + } + + /// + /// Represents a group of methods. + /// A method reference used to create a delegate is resolved to a MethodGroupResolveResult. + /// The MethodGroupResolveResult has no type. + /// To retrieve the delegate type or the chosen overload, look at the method group conversion. + /// + public class MethodGroupResolveResult : ResolveResult + { + readonly IList methodLists; + readonly IList typeArguments; + readonly ResolveResult targetResult; + readonly string methodName; + + public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IList methods, IList typeArguments) : base(SpecialType.UnknownType) + { + if (methods == null) + throw new ArgumentNullException("methods"); + this.targetResult = targetResult; + this.methodName = methodName; + this.methodLists = methods; + this.typeArguments = typeArguments ?? EmptyList.Instance; + } + + /// + /// Gets the resolve result for the target object. + /// + public ResolveResult TargetResult { + get { return targetResult; } + } + + /// + /// Gets the type of the reference to the target object. + /// + public IType TargetType { + get { return targetResult != null ? targetResult.Type : SpecialType.UnknownType; } + } + + /// + /// Gets the name of the methods in this group. + /// + public string MethodName { + get { return methodName; } + } + + /// + /// Gets the methods that were found. + /// This list does not include extension methods. + /// + public IEnumerable Methods { + get { return methodLists.SelectMany(m => m.Cast()); } + } + + /// + /// Gets the methods that were found, grouped by their declaring type. + /// This list does not include extension methods. + /// Base types come first in the list. + /// + public IEnumerable MethodsGroupedByDeclaringType { + get { return methodLists; } + } + + /// + /// Gets the type arguments that were explicitly provided. + /// + public IList TypeArguments { + get { return typeArguments; } + } + + /// + /// List of extension methods, used to avoid re-calculating it in ResolveInvocation() when it was already + /// calculated by ResolveMemberAccess(). + /// + internal List> extensionMethods; + + // the resolver is used to fetch extension methods on demand + internal CSharpResolver resolver; + + /// + /// Gets all candidate extension methods. + /// Note: this includes candidates that are not eligible due to an inapplicable + /// this argument. + /// The candidates will only be specialized if the type arguments were provided explicitly. + /// + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// + public IEnumerable> GetExtensionMethods() + { + if (resolver != null) { + Debug.Assert(extensionMethods == null); + try { + extensionMethods = resolver.GetExtensionMethods(methodName, typeArguments); + } finally { + resolver = null; + } + } + return extensionMethods ?? Enumerable.Empty>(); + } + + /// + /// Gets the eligible extension methods. + /// + /// + /// Specifies whether to produce a + /// when type arguments could be inferred from . + /// This setting is only used for inferred types and has no effect if the type parameters are + /// specified explicitly. + /// + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// + public IEnumerable> GetEligibleExtensionMethods(bool substituteInferredTypes) + { + var result = new List>(); + foreach (var methodGroup in GetExtensionMethods()) { + var outputGroup = new List(); + foreach (var method in methodGroup) { + IType[] inferredTypes; + if (CSharpResolver.IsEligibleExtensionMethod(this.TargetType, method, true, out inferredTypes)) { + if (substituteInferredTypes && inferredTypes != null) { + outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes))); + } else { + outputGroup.Add(method); + } + } + } + if (outputGroup.Count > 0) + result.Add(outputGroup); + } + return result; + } + + public override string ToString() + { + return string.Format("[{0} with {1} method(s)]", GetType().Name, this.Methods.Count()); + } + + public OverloadResolution PerformOverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, + bool allowExtensionMethods = true, + bool allowExpandingParams = true, + bool allowOptionalParameters = true, + bool checkForOverflow = false, CSharpConversions conversions = null) + { + Log.WriteLine("Performing overload resolution for " + this); + Log.WriteCollection(" Arguments: ", arguments); + + var typeArgumentArray = this.TypeArguments.ToArray(); + OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, typeArgumentArray, conversions); + or.AllowExpandingParams = allowExpandingParams; + or.AllowOptionalParameters = allowOptionalParameters; + or.CheckForOverflow = checkForOverflow; + + or.AddMethodLists(methodLists); + + if (allowExtensionMethods && !or.FoundApplicableCandidate) { + // No applicable match found, so let's try extension methods. + + var extensionMethods = this.GetExtensionMethods(); + + if (extensionMethods.Any()) { + Log.WriteLine("No candidate is applicable, trying {0} extension methods groups...", extensionMethods.Count()); + ResolveResult[] extArguments = new ResolveResult[arguments.Length + 1]; + extArguments[0] = new ResolveResult(this.TargetType); + arguments.CopyTo(extArguments, 1); + string[] extArgumentNames = null; + if (argumentNames != null) { + extArgumentNames = new string[argumentNames.Length + 1]; + argumentNames.CopyTo(extArgumentNames, 1); + } + var extOr = new OverloadResolution(compilation, extArguments, extArgumentNames, typeArgumentArray, conversions); + extOr.AllowExpandingParams = allowExpandingParams; + extOr.AllowOptionalParameters = allowOptionalParameters; + extOr.IsExtensionMethodInvocation = true; + extOr.CheckForOverflow = checkForOverflow; + + foreach (var g in extensionMethods) { + foreach (var method in g) { + Log.Indent(); + OverloadResolutionErrors errors = extOr.AddCandidate(method); + Log.Unindent(); + or.LogCandidateAddingResult(" Extension", method, errors); + } + if (extOr.FoundApplicableCandidate) + break; + } + // For the lack of a better comparison function (the one within OverloadResolution + // cannot be used as it depends on the argument set): + if (extOr.FoundApplicableCandidate || or.BestCandidate == null) { + // Consider an extension method result better than the normal result only + // if it's applicable; or if there is no normal result. + or = extOr; + } + } + } + Log.WriteLine("Overload resolution finished, best candidate is {0}.", or.GetBestCandidateWithSubstitutedTypeArguments()); + return or; + } + + public override IEnumerable GetChildResults() + { + if (targetResult != null) + return new[] { targetResult }; + else + return Enumerable.Empty(); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/NameLookupMode.cs b/ICSharpCode.Decompiler/CSharp/Resolver/NameLookupMode.cs new file mode 100644 index 000000000..3196dc583 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/NameLookupMode.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum NameLookupMode + { + /// + /// Normal name lookup in expressions + /// + Expression, + /// + /// Name lookup in expression, where the expression is the target of an invocation. + /// Such a lookup will only return methods and delegate-typed fields. + /// + InvocationTarget, + /// + /// Normal name lookup in type references. + /// + Type, + /// + /// Name lookup in the type reference inside a using declaration. + /// + TypeInUsingDeclaration, + /// + /// Name lookup for base type references. + /// + BaseTypeReference + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/NodeListResolveVisitorNavigator.cs b/ICSharpCode.Decompiler/CSharp/Resolver/NodeListResolveVisitorNavigator.cs new file mode 100644 index 000000000..11bde999b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/NodeListResolveVisitorNavigator.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// implementation that resolves a list of nodes. + /// We will skip all nodes which are not the target nodes or ancestors of the target nodes. + /// + public class NodeListResolveVisitorNavigator : IResolveVisitorNavigator + { + readonly Dictionary dict = new Dictionary(); + + /// + /// Creates a new NodeListResolveVisitorNavigator that resolves the specified nodes. + /// + public NodeListResolveVisitorNavigator(params AstNode[] nodes) + : this((IEnumerable)nodes) + { + } + + /// + /// Creates a new NodeListResolveVisitorNavigator that resolves the specified nodes. + /// + public NodeListResolveVisitorNavigator(IEnumerable nodes, bool scanOnly = false) + { + if (nodes == null) + throw new ArgumentNullException("nodes"); + foreach (var node in nodes) { + dict[node] = scanOnly ? ResolveVisitorNavigationMode.Scan : ResolveVisitorNavigationMode.Resolve; + for (var ancestor = node.Parent; ancestor != null && !dict.ContainsKey(ancestor); ancestor = ancestor.Parent) { + dict.Add(ancestor, ResolveVisitorNavigationMode.Scan); + } + } + } + + /// + public virtual ResolveVisitorNavigationMode Scan(AstNode node) + { + ResolveVisitorNavigationMode mode; + if (dict.TryGetValue(node, out mode)) { + return mode; + } else { + return ResolveVisitorNavigationMode.Skip; + } + } + + public virtual void Resolved(AstNode node, ResolveResult result) + { + } + + public virtual void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + { + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs new file mode 100644 index 000000000..c9e3e2690 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs @@ -0,0 +1,960 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// C# overload resolution (C# 4.0 spec: §7.5). + /// + public class OverloadResolution + { + sealed class Candidate + { + public readonly IParameterizedMember Member; + + /// + /// Returns the normal form candidate, if this is an expanded candidate. + /// + public readonly bool IsExpandedForm; + + /// + /// Gets the parameter types. In the first step, these are the types without any substition. + /// After type inference, substitutions will be performed. + /// + public readonly IType[] ParameterTypes; + + /// + /// argument index -> parameter index; -1 for arguments that could not be mapped + /// + public int[] ArgumentToParameterMap; + + public OverloadResolutionErrors Errors; + public int ErrorCount; + + public bool HasUnmappedOptionalParameters; + + public IType[] InferredTypes; + + /// + /// Gets the original member parameters (before any substitution!) + /// + public readonly IList Parameters; + + /// + /// Gets the original method type parameters (before any substitution!) + /// + public readonly IList TypeParameters; + + /// + /// Conversions applied to the arguments. + /// This field is set by the CheckApplicability step. + /// + public Conversion[] ArgumentConversions; + + public bool IsGenericMethod { + get { + IMethod method = Member as IMethod; + return method != null && method.TypeParameters.Count > 0; + } + } + + public int ArgumentsPassedToParamsArray { + get { + int count = 0; + if (IsExpandedForm) { + int paramsParameterIndex = this.Parameters.Count - 1; + foreach (int parameterIndex in ArgumentToParameterMap) { + if (parameterIndex == paramsParameterIndex) + count++; + } + } + return count; + } + } + + public Candidate(IParameterizedMember member, bool isExpanded) + { + this.Member = member; + this.IsExpandedForm = isExpanded; + IParameterizedMember memberDefinition = (IParameterizedMember)member.MemberDefinition; + // For specificialized methods, go back to the original parameters: + // (without any type parameter substitution, not even class type parameters) + // We'll re-substitute them as part of RunTypeInference(). + this.Parameters = memberDefinition.Parameters; + IMethod methodDefinition = memberDefinition as IMethod; + if (methodDefinition != null && methodDefinition.TypeParameters.Count > 0) { + this.TypeParameters = methodDefinition.TypeParameters; + } + this.ParameterTypes = new IType[this.Parameters.Count]; + } + + public void AddError(OverloadResolutionErrors newError) + { + this.Errors |= newError; + if (!IsApplicable(newError)) + this.ErrorCount++; + } + } + + readonly ICompilation compilation; + readonly ResolveResult[] arguments; + readonly string[] argumentNames; + readonly CSharpConversions conversions; + //List candidates = new List(); + Candidate bestCandidate; + Candidate bestCandidateAmbiguousWith; + IType[] explicitlyGivenTypeArguments; + bool bestCandidateWasValidated; + OverloadResolutionErrors bestCandidateValidationResult; + + #region Constructor + public OverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, CSharpConversions conversions = null) + { + if (compilation == null) + throw new ArgumentNullException("compilation"); + if (arguments == null) + throw new ArgumentNullException("arguments"); + if (argumentNames == null) + argumentNames = new string[arguments.Length]; + else if (argumentNames.Length != arguments.Length) + throw new ArgumentException("argumentsNames.Length must be equal to arguments.Length"); + this.compilation = compilation; + this.arguments = arguments; + this.argumentNames = argumentNames; + + // keep explicitlyGivenTypeArguments==null when no type arguments were specified + if (typeArguments != null && typeArguments.Length > 0) + this.explicitlyGivenTypeArguments = typeArguments; + + this.conversions = conversions ?? CSharpConversions.Get(compilation); + this.AllowExpandingParams = true; + this.AllowOptionalParameters = true; + } + #endregion + + #region Input Properties + /// + /// Gets/Sets whether the methods are extension methods that are being called using extension method syntax. + /// + /// + /// Setting this property to true restricts the possible conversions on the first argument to + /// implicit identity, reference, or boxing conversions. + /// + public bool IsExtensionMethodInvocation { get; set; } + + /// + /// Gets/Sets whether expanding 'params' into individual elements is allowed. + /// The default value is true. + /// + public bool AllowExpandingParams { get; set; } + + /// + /// Gets/Sets whether optional parameters may be left at their default value. + /// The default value is true. + /// If this property is set to false, optional parameters will be treated like regular parameters. + /// + public bool AllowOptionalParameters { get; set; } + + /// + /// Gets/Sets whether ConversionResolveResults created by this OverloadResolution + /// instance apply overflow checking. + /// The default value is false. + /// + public bool CheckForOverflow { get; set; } + + /// + /// Gets the arguments for which this OverloadResolution instance was created. + /// + public IList Arguments { + get { return arguments; } + } + #endregion + + #region AddCandidate + /// + /// Adds a candidate to overload resolution. + /// + /// The candidate member to add. + /// The errors that prevent the member from being applicable, if any. + /// Note: this method does not return errors that do not affect applicability. + public OverloadResolutionErrors AddCandidate(IParameterizedMember member) + { + return AddCandidate(member, OverloadResolutionErrors.None); + } + + /// + /// Adds a candidate to overload resolution. + /// + /// The candidate member to add. + /// Additional errors that apply to the candidate. + /// This is used to represent errors during member lookup (e.g. OverloadResolutionErrors.Inaccessible) + /// in overload resolution. + /// The errors that prevent the member from being applicable, if any. + /// Note: this method does not return errors that do not affect applicability. + public OverloadResolutionErrors AddCandidate(IParameterizedMember member, OverloadResolutionErrors additionalErrors) + { + if (member == null) + throw new ArgumentNullException("member"); + + Candidate c = new Candidate(member, false); + c.AddError(additionalErrors); + if (CalculateCandidate(c)) { + //candidates.Add(c); + } + + if (this.AllowExpandingParams && member.Parameters.Count > 0 + && member.Parameters[member.Parameters.Count - 1].IsParams) + { + Candidate expandedCandidate = new Candidate(member, true); + expandedCandidate.AddError(additionalErrors); + // consider expanded form only if it isn't obviously wrong + if (CalculateCandidate(expandedCandidate)) { + //candidates.Add(expandedCandidate); + + if (expandedCandidate.ErrorCount < c.ErrorCount) + return expandedCandidate.Errors; + } + } + return c.Errors; + } + + /// + /// Calculates applicability etc. for the candidate. + /// + /// True if the calculation was successful, false if the candidate should be removed without reporting an error + bool CalculateCandidate(Candidate candidate) + { + if (!ResolveParameterTypes(candidate, false)) + return false; + MapCorrespondingParameters(candidate); + RunTypeInference(candidate); + CheckApplicability(candidate); + ConsiderIfNewCandidateIsBest(candidate); + return true; + } + + bool ResolveParameterTypes(Candidate candidate, bool useSpecializedParameters) + { + for (int i = 0; i < candidate.Parameters.Count; i++) { + IType type; + if (useSpecializedParameters) { + // Use the parameter type of the specialized non-generic method or indexer + Debug.Assert(!candidate.IsGenericMethod); + type = candidate.Member.Parameters[i].Type; + } else { + // Use the type of the original formal parameter + type = candidate.Parameters[i].Type; + } + if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) { + ArrayType arrayType = type as ArrayType; + if (arrayType != null && arrayType.Dimensions == 1) + type = arrayType.ElementType; + else + return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate + } + candidate.ParameterTypes[i] = type; + } + return true; + } + #endregion + + #region AddMethodLists + /// + /// Adds all candidates from the method lists. + /// + /// This method implements the logic that causes applicable methods in derived types to hide + /// all methods in base types. + /// + /// The methods, grouped by declaring type. Base types must come first in the list. + public void AddMethodLists(IList methodLists) + { + if (methodLists == null) + throw new ArgumentNullException("methodLists"); + // Base types come first, so go through the list backwards (derived types first) + bool[] isHiddenByDerivedType; + if (methodLists.Count > 1) + isHiddenByDerivedType = new bool[methodLists.Count]; + else + isHiddenByDerivedType = null; + for (int i = methodLists.Count - 1; i >= 0; i--) { + if (isHiddenByDerivedType != null && isHiddenByDerivedType[i]) { + Log.WriteLine(" Skipping methods in {0} because they are hidden by an applicable method in a derived type", methodLists[i].DeclaringType); + continue; + } + + MethodListWithDeclaringType methodList = methodLists[i]; + bool foundApplicableCandidateInCurrentList = false; + + for (int j = 0; j < methodList.Count; j++) { + IParameterizedMember method = methodList[j]; + Log.Indent(); + OverloadResolutionErrors errors = AddCandidate(method); + Log.Unindent(); + LogCandidateAddingResult(" Candidate", method, errors); + + foundApplicableCandidateInCurrentList |= IsApplicable(errors); + } + + if (foundApplicableCandidateInCurrentList && i > 0) { + foreach (IType baseType in methodList.DeclaringType.GetAllBaseTypes()) { + for (int j = 0; j < i; j++) { + if (!isHiddenByDerivedType[j] && baseType.Equals(methodLists[j].DeclaringType)) + isHiddenByDerivedType[j] = true; + } + } + } + } + } + + [Conditional("DEBUG")] + internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors) + { + #if DEBUG + Log.WriteLine(string.Format("{0} {1} = {2}{3}", + text, method, + errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(), + this.BestCandidate == method ? " (best candidate so far)" : + this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : "" + )); + #endif + } + #endregion + + #region MapCorrespondingParameters + void MapCorrespondingParameters(Candidate candidate) + { + // C# 4.0 spec: §7.5.1.1 Corresponding parameters + candidate.ArgumentToParameterMap = new int[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) { + candidate.ArgumentToParameterMap[i] = -1; + if (argumentNames[i] == null) { + // positional argument + if (i < candidate.ParameterTypes.Length) { + candidate.ArgumentToParameterMap[i] = i; + } else if (candidate.IsExpandedForm) { + candidate.ArgumentToParameterMap[i] = candidate.ParameterTypes.Length - 1; + } else { + candidate.AddError(OverloadResolutionErrors.TooManyPositionalArguments); + } + } else { + // named argument + for (int j = 0; j < candidate.Parameters.Count; j++) { + if (argumentNames[i] == candidate.Parameters[j].Name) { + candidate.ArgumentToParameterMap[i] = j; + } + } + if (candidate.ArgumentToParameterMap[i] < 0) + candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument); + } + } + } + #endregion + + #region RunTypeInference + void RunTypeInference(Candidate candidate) + { + if (candidate.TypeParameters == null) { + if (explicitlyGivenTypeArguments != null) { + // method does not expect type arguments, but was given some + candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments); + } + // Grab new parameter types: + ResolveParameterTypes(candidate, true); + return; + } + ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType; + IList classTypeArguments; + if (parameterizedDeclaringType != null) { + classTypeArguments = parameterizedDeclaringType.TypeArguments; + } else { + classTypeArguments = null; + } + // The method is generic: + if (explicitlyGivenTypeArguments != null) { + if (explicitlyGivenTypeArguments.Length == candidate.TypeParameters.Count) { + candidate.InferredTypes = explicitlyGivenTypeArguments; + } else { + candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments); + // wrong number of type arguments given, so truncate the list or pad with UnknownType + candidate.InferredTypes = new IType[candidate.TypeParameters.Count]; + for (int i = 0; i < candidate.InferredTypes.Length; i++) { + if (i < explicitlyGivenTypeArguments.Length) + candidate.InferredTypes[i] = explicitlyGivenTypeArguments[i]; + else + candidate.InferredTypes[i] = SpecialType.UnknownType; + } + } + } else { + TypeInference ti = new TypeInference(compilation, conversions); + bool success; + candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, candidate.ParameterTypes, out success, classTypeArguments); + if (!success) + candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed); + } + // Now substitute in the formal parameters: + var substitution = new ConstraintValidatingSubstitution(classTypeArguments, candidate.InferredTypes, this); + for (int i = 0; i < candidate.ParameterTypes.Length; i++) { + candidate.ParameterTypes[i] = candidate.ParameterTypes[i].AcceptVisitor(substitution); + } + if (!substitution.ConstraintsValid) + candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint); + } + + sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution + { + readonly CSharpConversions conversions; + public bool ConstraintsValid = true; + + public ConstraintValidatingSubstitution(IList classTypeArguments, IList methodTypeArguments, OverloadResolution overloadResolution) + : base(classTypeArguments, methodTypeArguments) + { + this.conversions = overloadResolution.conversions; + } + + public override IType VisitParameterizedType(ParameterizedType type) + { + IType newType = base.VisitParameterizedType(type); + if (newType != type && ConstraintsValid) { + // something was changed, so we need to validate the constraints + ParameterizedType newParameterizedType = newType as ParameterizedType; + if (newParameterizedType != null) { + // C# 4.0 spec: §4.4.4 Satisfying constraints + var typeParameters = newParameterizedType.GetDefinition().TypeParameters; + var substitution = newParameterizedType.GetSubstitution(); + for (int i = 0; i < typeParameters.Count; i++) { + if (!ValidateConstraints(typeParameters[i], newParameterizedType.GetTypeArgument(i), substitution, conversions)) { + ConstraintsValid = false; + break; + } + } + } + } + return newType; + } + } + #endregion + + #region Validate Constraints + OverloadResolutionErrors ValidateMethodConstraints(Candidate candidate) + { + // If type inference already failed, we won't check the constraints: + if ((candidate.Errors & OverloadResolutionErrors.TypeInferenceFailed) != 0) + return OverloadResolutionErrors.None; + + if (candidate.TypeParameters == null || candidate.TypeParameters.Count == 0) + return OverloadResolutionErrors.None; // the method isn't generic + var substitution = GetSubstitution(candidate); + for (int i = 0; i < candidate.TypeParameters.Count; i++) { + if (!ValidateConstraints(candidate.TypeParameters[i], substitution.MethodTypeArguments[i], substitution)) + return OverloadResolutionErrors.MethodConstraintsNotSatisfied; + } + return OverloadResolutionErrors.None; + } + + /// + /// Validates whether the given type argument satisfies the constraints for the given type parameter. + /// + /// The type parameter. + /// The type argument. + /// The substitution that defines how type parameters are replaced with type arguments. + /// The substitution is used to check constraints that depend on other type parameters (or recursively on the same type parameter). + /// May be null if no substitution should be used. + /// True if the constraints are satisfied; false otherwise. + public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution = null) + { + if (typeParameter == null) + throw new ArgumentNullException("typeParameter"); + if (typeArgument == null) + throw new ArgumentNullException("typeArgument"); + return ValidateConstraints(typeParameter, typeArgument, substitution, CSharpConversions.Get(typeParameter.Owner.Compilation)); + } + + internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, CSharpConversions conversions) + { + switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments + case TypeKind.Void: + case TypeKind.Null: + case TypeKind.Pointer: + return false; + } + if (typeParameter.HasReferenceTypeConstraint) { + if (typeArgument.IsReferenceType != true) + return false; + } + if (typeParameter.HasValueTypeConstraint) { + if (!NullableType.IsNonNullableValueType(typeArgument)) + return false; + } + if (typeParameter.HasDefaultConstructorConstraint) { + ITypeDefinition def = typeArgument.GetDefinition(); + if (def != null && def.IsAbstract) + return false; + var ctors = typeArgument.GetConstructors( + m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public, + GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions + ); + if (!ctors.Any()) + return false; + } + foreach (IType constraintType in typeParameter.DirectBaseTypes) { + IType c = constraintType; + if (substitution != null) + c = c.AcceptVisitor(substitution); + if (!conversions.IsConstraintConvertible(typeArgument, c)) + return false; + } + return true; + } + #endregion + + #region CheckApplicability + /// + /// Returns whether a candidate with the given errors is still considered to be applicable. + /// + public static bool IsApplicable(OverloadResolutionErrors errors) + { + const OverloadResolutionErrors errorsThatDoNotMatterForApplicability = + OverloadResolutionErrors.AmbiguousMatch | OverloadResolutionErrors.MethodConstraintsNotSatisfied; + return (errors & ~errorsThatDoNotMatterForApplicability) == OverloadResolutionErrors.None; + } + + void CheckApplicability(Candidate candidate) + { + // C# 4.0 spec: §7.5.3.1 Applicable function member + + // Test whether parameters were mapped the correct number of arguments: + int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length]; + foreach (int parameterIndex in candidate.ArgumentToParameterMap) { + if (parameterIndex >= 0) + argumentCountPerParameter[parameterIndex]++; + } + for (int i = 0; i < argumentCountPerParameter.Length; i++) { + if (candidate.IsExpandedForm && i == argumentCountPerParameter.Length - 1) + continue; // any number of arguments is fine for the params-array + if (argumentCountPerParameter[i] == 0) { + if (this.AllowOptionalParameters && candidate.Parameters[i].IsOptional) + candidate.HasUnmappedOptionalParameters = true; + else + candidate.AddError(OverloadResolutionErrors.MissingArgumentForRequiredParameter); + } else if (argumentCountPerParameter[i] > 1) { + candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter); + } + } + + candidate.ArgumentConversions = new Conversion[arguments.Length]; + // Test whether argument passing mode matches the parameter passing mode + for (int i = 0; i < arguments.Length; i++) { + int parameterIndex = candidate.ArgumentToParameterMap[i]; + if (parameterIndex < 0) { + candidate.ArgumentConversions[i] = Conversion.None; + continue; + } + + ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; + if (brrr != null) { + if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef)) + candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); + } else { + if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef) + candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); + } + IType parameterType = candidate.ParameterTypes[parameterIndex]; + Conversion c = conversions.ImplicitConversion(arguments[i], parameterType); + candidate.ArgumentConversions[i] = c; + if (IsExtensionMethodInvocation && parameterIndex == 0) { + // First parameter to extension method must be an identity, reference or boxing conversion + if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion)) + candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); + } else { + if ((!c.IsValid && !c.IsUserDefined && !c.IsMethodGroupConversion) && parameterType.Kind != TypeKind.Unknown) + candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); + } + } + } + #endregion + + #region BetterFunctionMember + /// + /// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better. + /// + int BetterFunctionMember(Candidate c1, Candidate c2) + { + // prefer applicable members (part of heuristic that produces a best candidate even if none is applicable) + if (c1.ErrorCount == 0 && c2.ErrorCount > 0) + return 1; + if (c1.ErrorCount > 0 && c2.ErrorCount == 0) + return 2; + + // C# 4.0 spec: §7.5.3.2 Better function member + bool c1IsBetter = false; + bool c2IsBetter = false; + for (int i = 0; i < arguments.Length; i++) { + int p1 = c1.ArgumentToParameterMap[i]; + int p2 = c2.ArgumentToParameterMap[i]; + if (p1 >= 0 && p2 < 0) { + c1IsBetter = true; + } else if (p1 < 0 && p2 >= 0) { + c2IsBetter = true; + } else if (p1 >= 0 && p2 >= 0) { + switch (conversions.BetterConversion(arguments[i], c1.ParameterTypes[p1], c2.ParameterTypes[p2])) { + case 1: + c1IsBetter = true; + break; + case 2: + c2IsBetter = true; + break; + } + } + } + if (c1IsBetter && !c2IsBetter) + return 1; + if (!c1IsBetter && c2IsBetter) + return 2; + + // prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable) + if (c1.ErrorCount < c2.ErrorCount) return 1; + if (c1.ErrorCount > c2.ErrorCount) return 2; + + if (!c1IsBetter && !c2IsBetter) { + // we need the tie-breaking rules + + // non-generic methods are better + if (!c1.IsGenericMethod && c2.IsGenericMethod) + return 1; + else if (c1.IsGenericMethod && !c2.IsGenericMethod) + return 2; + + // non-expanded members are better + if (!c1.IsExpandedForm && c2.IsExpandedForm) + return 1; + else if (c1.IsExpandedForm && !c2.IsExpandedForm) + return 2; + + // prefer the member with less arguments mapped to the params-array + int r = c1.ArgumentsPassedToParamsArray.CompareTo(c2.ArgumentsPassedToParamsArray); + if (r < 0) return 1; + else if (r > 0) return 2; + + // prefer the member where no default values need to be substituted + if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters) + return 1; + else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters) + return 2; + + // compare the formal parameters + r = MoreSpecificFormalParameters(c1, c2); + if (r != 0) + return r; + + // prefer non-lifted operators + ILiftedOperator lift1 = c1.Member as ILiftedOperator; + ILiftedOperator lift2 = c2.Member as ILiftedOperator; + if (lift1 == null && lift2 != null) + return 1; + if (lift1 != null && lift2 == null) + return 2; + } + return 0; + } + + /// + /// Implement this interface to give overload resolution a hint that the member represents a lifted operator, + /// which is used in the tie-breaking rules. + /// + public interface ILiftedOperator : IParameterizedMember + { + IList NonLiftedParameters { get; } + } + + int MoreSpecificFormalParameters(Candidate c1, Candidate c2) + { + // prefer the member with more formal parmeters (in case both have different number of optional parameters) + int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count); + if (r > 0) return 1; + else if (r < 0) return 2; + + return MoreSpecificFormalParameters(c1.Parameters.Select(p => p.Type), c2.Parameters.Select(p => p.Type)); + } + + static int MoreSpecificFormalParameters(IEnumerable t1, IEnumerable t2) + { + bool c1IsBetter = false; + bool c2IsBetter = false; + foreach (var pair in t1.Zip(t2, (a,b) => new { Item1 = a, Item2 = b })) { + switch (MoreSpecificFormalParameter(pair.Item1, pair.Item2)) { + case 1: + c1IsBetter = true; + break; + case 2: + c2IsBetter = true; + break; + } + } + if (c1IsBetter && !c2IsBetter) + return 1; + if (!c1IsBetter && c2IsBetter) + return 2; + return 0; + } + + static int MoreSpecificFormalParameter(IType t1, IType t2) + { + if ((t1 is ITypeParameter) && !(t2 is ITypeParameter)) + return 2; + if ((t2 is ITypeParameter) && !(t1 is ITypeParameter)) + return 1; + + ParameterizedType p1 = t1 as ParameterizedType; + ParameterizedType p2 = t2 as ParameterizedType; + if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) { + int r = MoreSpecificFormalParameters(p1.TypeArguments, p2.TypeArguments); + if (r > 0) + return r; + } + TypeWithElementType tew1 = t1 as TypeWithElementType; + TypeWithElementType tew2 = t2 as TypeWithElementType; + if (tew1 != null && tew2 != null) { + return MoreSpecificFormalParameter(tew1.ElementType, tew2.ElementType); + } + return 0; + } + #endregion + + #region ConsiderIfNewCandidateIsBest + void ConsiderIfNewCandidateIsBest(Candidate candidate) + { + if (bestCandidate == null) { + bestCandidate = candidate; + bestCandidateWasValidated = false; + } else { + switch (BetterFunctionMember(candidate, bestCandidate)) { + case 0: + // Overwrite 'bestCandidateAmbiguousWith' so that API users can + // detect the set of all ambiguous methods if they look at + // bestCandidateAmbiguousWith after each step. + bestCandidateAmbiguousWith = candidate; + break; + case 1: + bestCandidate = candidate; + bestCandidateWasValidated = false; + bestCandidateAmbiguousWith = null; + break; + // case 2: best candidate stays best + } + } + } + #endregion + + #region Output Properties + public IParameterizedMember BestCandidate { + get { return bestCandidate != null ? bestCandidate.Member : null; } + } + + /// + /// Returns the errors that apply to the best candidate. + /// This includes additional errors that do not affect applicability (e.g. AmbiguousMatch, MethodConstraintsNotSatisfied) + /// + public OverloadResolutionErrors BestCandidateErrors { + get { + if (bestCandidate == null) + return OverloadResolutionErrors.None; + if (!bestCandidateWasValidated) { + bestCandidateValidationResult = ValidateMethodConstraints(bestCandidate); + bestCandidateWasValidated = true; + } + OverloadResolutionErrors err = bestCandidate.Errors | bestCandidateValidationResult; + if (bestCandidateAmbiguousWith != null) + err |= OverloadResolutionErrors.AmbiguousMatch; + return err; + } + } + + public bool FoundApplicableCandidate { + get { return bestCandidate != null && IsApplicable(bestCandidate.Errors); } + } + + public IParameterizedMember BestCandidateAmbiguousWith { + get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; } + } + + public bool BestCandidateIsExpandedForm { + get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; } + } + + public bool IsAmbiguous { + get { return bestCandidateAmbiguousWith != null; } + } + + public IList InferredTypeArguments { + get { + if (bestCandidate != null && bestCandidate.InferredTypes != null) + return bestCandidate.InferredTypes; + else + return EmptyList.Instance; + } + } + + /// + /// Gets the implicit conversions that are being applied to the arguments. + /// + public IList ArgumentConversions { + get { + if (bestCandidate != null && bestCandidate.ArgumentConversions != null) + return bestCandidate.ArgumentConversions; + else + return Enumerable.Repeat(Conversion.None, arguments.Length).ToList(); + } + } + + /// + /// Gets an array that maps argument indices to parameter indices. + /// For arguments that could not be mapped to any parameter, the value will be -1. + /// + /// parameterIndex = GetArgumentToParameterMap()[argumentIndex] + /// + public IList GetArgumentToParameterMap() + { + if (bestCandidate != null) + return bestCandidate.ArgumentToParameterMap; + else + return null; + } + + /// + /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters). + /// Arguments are wrapped in a if an implicit conversion is being applied + /// to them when calling the method. + /// + public IList GetArgumentsWithConversions() + { + if (bestCandidate == null) + return arguments; + else + return GetArgumentsWithConversions(null, null); + } + + /// + /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters). + /// Arguments are wrapped in a if an implicit conversion is being applied + /// to them when calling the method. + /// For arguments where an explicit argument name was provided, the argument will + /// be wrapped in a . + /// + public IList GetArgumentsWithConversionsAndNames() + { + if (bestCandidate == null) + return arguments; + else + return GetArgumentsWithConversions(null, GetBestCandidateWithSubstitutedTypeArguments()); + } + + IList GetArgumentsWithConversions(ResolveResult targetResolveResult, IParameterizedMember bestCandidateForNamedArguments) + { + var conversions = this.ArgumentConversions; + ResolveResult[] args = new ResolveResult[arguments.Length]; + for (int i = 0; i < args.Length; i++) { + var argument = arguments[i]; + if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null) + argument = targetResolveResult; + int parameterIndex = bestCandidate.ArgumentToParameterMap[i]; + if (parameterIndex >= 0 && conversions[i] != Conversion.IdentityConversion) { + // Wrap argument in ConversionResolveResult + IType parameterType = bestCandidate.ParameterTypes[parameterIndex]; + if (parameterType.Kind != TypeKind.Unknown) { + if (arguments[i].IsCompileTimeConstant && conversions[i].IsValid && !conversions[i].IsUserDefined) { + argument = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument); + } else { + argument = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow); + } + } + } + if (bestCandidateForNamedArguments != null && argumentNames[i] != null) { + // Wrap argument in NamedArgumentResolveResult + if (parameterIndex >= 0) { + argument = new NamedArgumentResolveResult(bestCandidateForNamedArguments.Parameters[parameterIndex], argument, bestCandidateForNamedArguments); + } else { + argument = new NamedArgumentResolveResult(argumentNames[i], argument); + } + } + args[i] = argument; + } + return args; + } + + public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments() + { + if (bestCandidate == null) + return null; + IMethod method = bestCandidate.Member as IMethod; + if (method != null && method.TypeParameters.Count > 0) { + return ((IMethod)method.MemberDefinition).Specialize(GetSubstitution(bestCandidate)); + } else { + return bestCandidate.Member; + } + } + + TypeParameterSubstitution GetSubstitution(Candidate candidate) + { + // Do not compose the substitutions, but merge them. + // This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce + return new TypeParameterSubstitution(candidate.Member.Substitution.ClassTypeArguments, candidate.InferredTypes); + } + + /// + /// Creates a ResolveResult representing the result of overload resolution. + /// + /// + /// The target expression of the call. May be null for static methods/constructors. + /// + /// + /// Statements for Objects/Collections initializer. + /// + /// + /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. + /// + public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList initializerStatements = null, IType returnTypeOverride = null) + { + IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments(); + if (member == null) + throw new InvalidOperationException(); + + return new CSharpInvocationResolveResult( + this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType ?? SpecialType.UnknownType) : targetResolveResult, + member, + GetArgumentsWithConversions(targetResolveResult, member), + this.BestCandidateErrors, + this.IsExtensionMethodInvocation, + this.BestCandidateIsExpandedForm, + isDelegateInvocation: false, + argumentToParameterMap: this.GetArgumentToParameterMap(), + initializerStatements: initializerStatements, + returnTypeOverride: returnTypeOverride); + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolutionErrors.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolutionErrors.cs new file mode 100644 index 000000000..9e4904bcf --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolutionErrors.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + [Flags] + public enum OverloadResolutionErrors + { + None = 0, + /// + /// Too many positional arguments (some could not be mapped to any parameter). + /// + TooManyPositionalArguments = 0x0001, + /// + /// A named argument could not be mapped to any parameter + /// + NoParameterFoundForNamedArgument = 0x0002, + /// + /// Type inference failed for a generic method. + /// + TypeInferenceFailed = 0x0004, + /// + /// Type arguments were explicitly specified, but did not match the number of type parameters. + /// + WrongNumberOfTypeArguments = 0x0008, + /// + /// After substituting type parameters with the inferred types; a constructed type within the formal parameters + /// does not satisfy its constraint. + /// + ConstructedTypeDoesNotSatisfyConstraint = 0x0010, + /// + /// No argument was mapped to a non-optional parameter + /// + MissingArgumentForRequiredParameter = 0x0020, + /// + /// Several arguments were mapped to a single (non-params-array) parameter + /// + MultipleArgumentsForSingleParameter = 0x0040, + /// + /// 'ref'/'out' passing mode doesn't match for at least 1 parameter + /// + ParameterPassingModeMismatch = 0x0080, + /// + /// Argument type cannot be converted to parameter type + /// + ArgumentTypeMismatch = 0x0100, + /// + /// There is no unique best overload. + /// This error does not apply to any single candidate, but only to the overall result of overload resolution. + /// + /// + /// This error does not prevent a candidate from being applicable. + /// + AmbiguousMatch = 0x0200, + /// + /// The member is not accessible. + /// + /// + /// This error is generated by member lookup; not by overload resolution. + /// + Inaccessible = 0x0400, + /// + /// A generic method + /// + /// + /// This error does not prevent a candidate from being applicable. + /// + MethodConstraintsNotSatisfied = 0x0800 + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs b/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs new file mode 100644 index 000000000..64acd15b8 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs @@ -0,0 +1,455 @@ +// +// ReducedExtensionMethod.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// An invocated extension method hides the extension parameter in its parameter list. + /// It's used to hide the internals of extension method invocation in certain situation to simulate the + /// syntactic way of writing extension methods on semantic level. + /// + public class ReducedExtensionMethod : IMethod + { + readonly IMethod baseMethod; + + public ReducedExtensionMethod(IMethod baseMethod) + { + this.baseMethod = baseMethod; + } + + public override bool Equals(object obj) + { + var other = obj as ReducedExtensionMethod; + if (other == null) + return false; + return baseMethod.Equals(other.baseMethod); + } + + public override int GetHashCode() + { + unchecked { + return baseMethod.GetHashCode() + 1; + } + } + + public override string ToString() + { + return string.Format("[ReducedExtensionMethod: ReducedFrom={0}]", ReducedFrom); + } + + #region IMember implementation + + [Serializable] + public sealed class ReducedExtensionMethodMemberReference : IMemberReference + { + readonly IMethod baseMethod; + + public ReducedExtensionMethodMemberReference (IMethod baseMethod) + { + this.baseMethod = baseMethod; + } + + public IMember Resolve(ITypeResolveContext context) + { + return new ReducedExtensionMethod ((IMethod)baseMethod.ToReference ().Resolve (context)); + } + + ISymbol ISymbolReference.Resolve(ITypeResolveContext context) + { + return Resolve(context); + } + + public ITypeReference DeclaringTypeReference { + get { + return baseMethod.ToReference ().DeclaringTypeReference; + } + } + } + + public IMemberReference ToMemberReference() + { + return new ReducedExtensionMethodMemberReference (baseMethod); + } + + public IMemberReference ToReference() + { + return new ReducedExtensionMethodMemberReference (baseMethod); + } + + ISymbolReference ISymbol.ToReference() + { + return ToReference(); + } + + public IMember MemberDefinition { + get { + return baseMethod.MemberDefinition; + } + } + + public IUnresolvedMember UnresolvedMember { + get { + return baseMethod.UnresolvedMember; + } + } + + public IType ReturnType { + get { + return baseMethod.ReturnType; + } + } + + public System.Collections.Generic.IList ImplementedInterfaceMembers { + get { + return baseMethod.ImplementedInterfaceMembers; + } + } + + public bool IsExplicitInterfaceImplementation { + get { + return baseMethod.IsExplicitInterfaceImplementation; + } + } + + public bool IsVirtual { + get { + return baseMethod.IsVirtual; + } + } + + public bool IsOverride { + get { + return baseMethod.IsOverride; + } + } + + public bool IsOverridable { + get { + return baseMethod.IsOverridable; + } + } + + public TypeParameterSubstitution Substitution { + get { + return baseMethod.Substitution; + } + } + + public IMethod Specialize(TypeParameterSubstitution substitution) + { + return new ReducedExtensionMethod((IMethod)baseMethod.Specialize(substitution)); + } + + IMember IMember.Specialize(TypeParameterSubstitution substitution) + { + return Specialize(substitution); + } + + public bool IsParameterized { + get { return baseMethod.IsParameterized; } + } + + #endregion + + #region IMethod implementation + + public System.Collections.Generic.IList Parts { + get { + return baseMethod.Parts; + } + } + + public System.Collections.Generic.IList ReturnTypeAttributes { + get { + return baseMethod.ReturnTypeAttributes; + } + } + + public System.Collections.Generic.IList TypeParameters { + get { + return baseMethod.TypeParameters; + } + } + + public bool IsExtensionMethod { + get { + return true; + } + } + + public bool IsConstructor { + get { + return baseMethod.IsConstructor; + } + } + + public bool IsDestructor { + get { + return baseMethod.IsDestructor; + } + } + + public bool IsOperator { + get { + return baseMethod.IsOperator; + } + } + + public bool IsPartial { + get { + return baseMethod.IsPartial; + } + } + + public bool IsAsync { + get { + return baseMethod.IsAsync; + } + } + + public bool HasBody { + get { + return baseMethod.HasBody; + } + } + + public bool IsAccessor { + get { + return baseMethod.IsAccessor; + } + } + + public IMember AccessorOwner { + get { + return baseMethod.AccessorOwner; + } + } + + public IMethod ReducedFrom { + get { + return baseMethod; + } + } + + public IList TypeArguments { + get { + return baseMethod.TypeArguments; + } + } + #endregion + + #region IParameterizedMember implementation + List parameters; + public System.Collections.Generic.IList Parameters { + get { + if (parameters == null) + parameters = new List (baseMethod.Parameters.Skip (1)); + return parameters; + } + } + + #endregion + + #region IEntity implementation + + public SymbolKind SymbolKind { + get { + return baseMethod.SymbolKind; + } + } + + [Obsolete("Use the SymbolKind property instead.")] + public EntityType EntityType { + get { + return baseMethod.EntityType; + } + } + + public DomRegion Region { + get { + return baseMethod.Region; + } + } + + public DomRegion BodyRegion { + get { + return baseMethod.BodyRegion; + } + } + + public ITypeDefinition DeclaringTypeDefinition { + get { + return baseMethod.DeclaringTypeDefinition; + } + } + + public IType DeclaringType { + get { + return baseMethod.DeclaringType; + } + } + + public IAssembly ParentAssembly { + get { + return baseMethod.ParentAssembly; + } + } + + public System.Collections.Generic.IList Attributes { + get { + return baseMethod.Attributes; + } + } + + public ICSharpCode.NRefactory.Documentation.DocumentationComment Documentation { + get { + return baseMethod.Documentation; + } + } + + public bool IsStatic { + get { + return false; + } + } + + public bool IsAbstract { + get { + return baseMethod.IsAbstract; + } + } + + public bool IsSealed { + get { + return baseMethod.IsSealed; + } + } + + public bool IsShadowing { + get { + return baseMethod.IsShadowing; + } + } + + public bool IsSynthetic { + get { + return baseMethod.IsSynthetic; + } + } + + #endregion + + #region IHasAccessibility implementation + + public Accessibility Accessibility { + get { + return baseMethod.Accessibility; + } + } + + public bool IsPrivate { + get { + return baseMethod.IsPrivate; + } + } + + public bool IsPublic { + get { + return baseMethod.IsPublic; + } + } + + public bool IsProtected { + get { + return baseMethod.IsProtected; + } + } + + public bool IsInternal { + get { + return baseMethod.IsInternal; + } + } + + public bool IsProtectedOrInternal { + get { + return baseMethod.IsProtectedOrInternal; + } + } + + public bool IsProtectedAndInternal { + get { + return baseMethod.IsProtectedAndInternal; + } + } + + #endregion + + #region INamedElement implementation + + public string FullName { + get { + return baseMethod.FullName; + } + } + + public string Name { + get { + return baseMethod.Name; + } + } + + public string ReflectionName { + get { + return baseMethod.ReflectionName; + } + } + + public string Namespace { + get { + return baseMethod.Namespace; + } + } + + #endregion + + #region ICompilationProvider implementation + + public ICompilation Compilation { + get { + return baseMethod.Compilation; + } + } + + #endregion + } +} + diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs b/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs new file mode 100644 index 000000000..dbeadb205 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2010-2014 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; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Arguments for the callback of . + /// + public class RenameCallbackArguments + { + public AstNode NodeToReplace { get; private set; } + public AstNode NewNode { get; private set; } + + public RenameCallbackArguments(AstNode nodeToReplace, AstNode newNode) + { + if (nodeToReplace == null) + throw new ArgumentNullException("nodeToReplace"); + if (newNode == null) + throw new ArgumentNullException("newNode"); + this.NodeToReplace = nodeToReplace; + this.NewNode = newNode; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs new file mode 100644 index 000000000..acbdbbf32 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -0,0 +1,978 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + public enum TypeInferenceAlgorithm + { + /// + /// C# 4.0 type inference. + /// + CSharp4, + /// + /// Improved algorithm (not part of any specification) using FindTypeInBounds for fixing. + /// + Improved, + /// + /// Improved algorithm (not part of any specification) using FindTypeInBounds for fixing; + /// uses to report all results (in case of ambiguities). + /// + ImprovedReturnAllResults + } + + /// + /// Implements C# 4.0 Type Inference (§7.5.2). + /// + public sealed class TypeInference + { + readonly ICompilation compilation; + readonly CSharpConversions conversions; + TypeInferenceAlgorithm algorithm = TypeInferenceAlgorithm.CSharp4; + + // determines the maximum generic nesting level; necessary to avoid infinite recursion in 'Improved' mode. + const int maxNestingLevel = 5; + int nestingLevel; + + #region Constructor + public TypeInference(ICompilation compilation) + { + if (compilation == null) + throw new ArgumentNullException("compilation"); + this.compilation = compilation; + this.conversions = CSharpConversions.Get(compilation); + } + + internal TypeInference(ICompilation compilation, CSharpConversions conversions) + { + Debug.Assert(compilation != null); + Debug.Assert(conversions != null); + this.compilation = compilation; + this.conversions = conversions; + } + #endregion + + #region Properties + /// + /// Gets/Sets the type inference algorithm used. + /// + public TypeInferenceAlgorithm Algorithm { + get { return algorithm; } + set { algorithm = value; } + } + + TypeInference CreateNestedInstance() + { + TypeInference c = new TypeInference(compilation, conversions); + c.algorithm = algorithm; + c.nestingLevel = nestingLevel + 1; + return c; + } + #endregion + + TP[] typeParameters; + IType[] parameterTypes; + ResolveResult[] arguments; + bool[,] dependencyMatrix; + IList classTypeArguments; + + #region InferTypeArguments (main function) + /// + /// Performs type inference. + /// + /// The method type parameters that should be inferred. + /// The arguments passed to the method. + /// The parameter types of the method. + /// Out: whether type inference was successful + /// + /// Class type arguments. These are substituted for class type parameters in the formal parameter types + /// when inferring a method group or lambda. + /// + /// The inferred type arguments. + public IType[] InferTypeArguments(IList typeParameters, IList arguments, IList parameterTypes, out bool success, IList classTypeArguments = null) + { + if (typeParameters == null) + throw new ArgumentNullException("typeParameters"); + if (arguments == null) + throw new ArgumentNullException("arguments"); + if (parameterTypes == null) + throw new ArgumentNullException("parameterTypes"); + try { + this.typeParameters = new TP[typeParameters.Count]; + for (int i = 0; i < this.typeParameters.Length; i++) { + if (i != typeParameters[i].Index) + throw new ArgumentException("Type parameter has wrong index"); + if (typeParameters[i].OwnerType != SymbolKind.Method) + throw new ArgumentException("Type parameter must be owned by a method"); + this.typeParameters[i] = new TP(typeParameters[i]); + } + this.parameterTypes = new IType[Math.Min(arguments.Count, parameterTypes.Count)]; + this.arguments = new ResolveResult[this.parameterTypes.Length]; + for (int i = 0; i < this.parameterTypes.Length; i++) { + if (arguments[i] == null || parameterTypes[i] == null) + throw new ArgumentNullException(); + this.arguments[i] = arguments[i]; + this.parameterTypes[i] = parameterTypes[i]; + } + this.classTypeArguments = classTypeArguments; + Log.WriteLine("Type Inference"); + Log.WriteLine(" Signature: M<" + string.Join(", ", this.typeParameters) + ">" + + "(" + string.Join(", ", this.parameterTypes) + ")"); + Log.WriteCollection(" Arguments: ", arguments); + Log.Indent(); + + PhaseOne(); + success = PhaseTwo(); + + Log.Unindent(); + Log.WriteLine(" Type inference finished " + (success ? "successfully" : "with errors") + ": " + + "M<" + string.Join(", ", this.typeParameters.Select(tp => tp.FixedTo ?? SpecialType.UnknownType)) + ">"); + return this.typeParameters.Select(tp => tp.FixedTo ?? SpecialType.UnknownType).ToArray(); + } finally { + Reset(); + } + } + + void Reset() + { + // clean up so that memory used by the operation can be garbage collected as soon as possible + this.typeParameters = null; + this.parameterTypes = null; + this.arguments = null; + this.dependencyMatrix = null; + this.classTypeArguments = null; + } + + /// + /// Infers type arguments for the occurring in the + /// so that the resulting type (after substition) satisfies the given bounds. + /// + public IType[] InferTypeArgumentsFromBounds(IList typeParameters, IType targetType, IList lowerBounds, IList upperBounds, out bool success) + { + if (typeParameters == null) + throw new ArgumentNullException("typeParameters"); + if (targetType == null) + throw new ArgumentNullException("targetType"); + if (lowerBounds == null) + throw new ArgumentNullException("lowerBounds"); + if (upperBounds == null) + throw new ArgumentNullException("upperBounds"); + this.typeParameters = new TP[typeParameters.Count]; + for (int i = 0; i < this.typeParameters.Length; i++) { + if (i != typeParameters[i].Index) + throw new ArgumentException("Type parameter has wrong index"); + this.typeParameters[i] = new TP(typeParameters[i]); + } + foreach (IType b in lowerBounds) { + MakeLowerBoundInference(b, targetType); + } + foreach (IType b in upperBounds) { + MakeUpperBoundInference(b, targetType); + } + IType[] result = new IType[this.typeParameters.Length]; + success = true; + for (int i = 0; i < result.Length; i++) { + success &= Fix(this.typeParameters[i]); + result[i] = this.typeParameters[i].FixedTo ?? SpecialType.UnknownType; + } + Reset(); + return result; + } + #endregion + + sealed class TP + { + public readonly HashSet LowerBounds = new HashSet(); + public readonly HashSet UpperBounds = new HashSet(); + public IType ExactBound; + public bool MultipleDifferentExactBounds; + public readonly ITypeParameter TypeParameter; + public IType FixedTo; + + public bool IsFixed { + get { return FixedTo != null; } + } + + public bool HasBounds { + get { return LowerBounds.Count > 0 || UpperBounds.Count > 0 || ExactBound != null; } + } + + public TP(ITypeParameter typeParameter) + { + if (typeParameter == null) + throw new ArgumentNullException("typeParameter"); + this.TypeParameter = typeParameter; + } + + public void AddExactBound(IType type) + { + // Exact bounds need to stored separately, not just as Lower+Upper bounds, + // due to TypeInferenceTests.GenericArgumentImplicitlyConvertibleToAndFromAnotherTypeList (see #281) + if (ExactBound == null) + ExactBound = type; + else if (!ExactBound.Equals(type)) + MultipleDifferentExactBounds = true; + } + + public override string ToString() + { + return TypeParameter.Name; + } + } + + sealed class OccursInVisitor : TypeVisitor + { + readonly TP[] tp; + public readonly bool[] Occurs; + + public OccursInVisitor(TypeInference typeInference) + { + this.tp = typeInference.typeParameters; + this.Occurs = new bool[tp.Length]; + } + + public override IType VisitTypeParameter(ITypeParameter type) + { + int index = type.Index; + if (index < tp.Length && tp[index].TypeParameter == type) + Occurs[index] = true; + return base.VisitTypeParameter(type); + } + } + + #region Inference Phases + void PhaseOne() + { + // C# 4.0 spec: §7.5.2.1 The first phase + Log.WriteLine("Phase One"); + for (int i = 0; i < arguments.Length; i++) { + ResolveResult Ei = arguments[i]; + IType Ti = parameterTypes[i]; + + LambdaResolveResult lrr = Ei as LambdaResolveResult; + if (lrr != null) { + MakeExplicitParameterTypeInference(lrr, Ti); + } + if (lrr != null || Ei is MethodGroupResolveResult) { + // this is not in the spec??? + if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) { + MakeOutputTypeInference(Ei, Ti); + } + } + + if (IsValidType(Ei.Type)) { + if (Ti is ByReferenceType) { + MakeExactInference(Ei.Type, Ti); + } else { + MakeLowerBoundInference(Ei.Type, Ti); + } + } + } + } + + static bool IsValidType(IType type) + { + return type.Kind != TypeKind.Unknown && type.Kind != TypeKind.Null; + } + + bool PhaseTwo() + { + // C# 4.0 spec: §7.5.2.2 The second phase + Log.WriteLine("Phase Two"); + // All unfixed type variables Xi which do not depend on any Xj are fixed. + List typeParametersToFix = new List(); + foreach (TP Xi in typeParameters) { + if (Xi.IsFixed == false) { + if (!typeParameters.Any((TP Xj) => !Xj.IsFixed && DependsOn(Xi, Xj))) { + typeParametersToFix.Add(Xi); + } + } + } + // If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold: + if (typeParametersToFix.Count == 0) { + Log.WriteLine("Type parameters cannot be fixed due to dependency cycles"); + Log.WriteLine("Trying to break the cycle by fixing any TPs that have non-empty bounds..."); + foreach (TP Xi in typeParameters) { + // Xi has a non­empty set of bounds + if (!Xi.IsFixed && Xi.HasBounds) { + // There is at least one type variable Xj that depends on Xi + if (typeParameters.Any((TP Xj) => DependsOn(Xj, Xi))) { + typeParametersToFix.Add(Xi); + } + } + } + } + // now fix 'em + bool errorDuringFix = false; + foreach (TP tp in typeParametersToFix) { + if (!Fix(tp)) + errorDuringFix = true; + } + if (errorDuringFix) + return false; + bool unfixedTypeVariablesExist = typeParameters.Any((TP X) => X.IsFixed == false); + if (typeParametersToFix.Count == 0 && unfixedTypeVariablesExist) { + // If no such type variables exist and there are still unfixed type variables, type inference fails. + Log.WriteLine("Type inference fails: there are still unfixed TPs remaining"); + return false; + } else if (!unfixedTypeVariablesExist) { + // Otherwise, if no further unfixed type variables exist, type inference succeeds. + return true; + } else { + // Otherwise, for all arguments ei with corresponding parameter type Ti + for (int i = 0; i < arguments.Length; i++) { + ResolveResult Ei = arguments[i]; + IType Ti = parameterTypes[i]; + // where the output types (§7.4.2.4) contain unfixed type variables Xj + // but the input types (§7.4.2.3) do not + if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) { + // an output type inference (§7.4.2.6) is made for ei with type Ti. + Log.WriteLine("MakeOutputTypeInference for argument #" + i); + MakeOutputTypeInference(Ei, Ti); + } + } + // Then the second phase is repeated. + return PhaseTwo(); + } + } + #endregion + + #region Input Types / Output Types (§7.5.2.3 + §7.5.2.4) + static readonly IType[] emptyTypeArray = new IType[0]; + + IType[] InputTypes(ResolveResult e, IType t) + { + // C# 4.0 spec: §7.5.2.3 Input types + LambdaResolveResult lrr = e as LambdaResolveResult; + if (lrr != null && lrr.IsImplicitlyTyped || e is MethodGroupResolveResult) { + IMethod m = GetDelegateOrExpressionTreeSignature(t); + if (m != null) { + IType[] inputTypes = new IType[m.Parameters.Count]; + for (int i = 0; i < inputTypes.Length; i++) { + inputTypes[i] = m.Parameters[i].Type; + } + return inputTypes; + } + } + return emptyTypeArray; + } + + IType[] OutputTypes(ResolveResult e, IType t) + { + // C# 4.0 spec: §7.5.2.4 Output types + LambdaResolveResult lrr = e as LambdaResolveResult; + if (lrr != null || e is MethodGroupResolveResult) { + IMethod m = GetDelegateOrExpressionTreeSignature(t); + if (m != null) { + return new[] { m.ReturnType }; + } + } + return emptyTypeArray; + } + + static IMethod GetDelegateOrExpressionTreeSignature(IType t) + { + ParameterizedType pt = t as ParameterizedType; + if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" + && pt.Namespace == "System.Linq.Expressions") + { + t = pt.GetTypeArgument(0); + } + return t.GetDelegateInvokeMethod(); + } + + bool InputTypesContainsUnfixed(ResolveResult argument, IType parameterType) + { + return AnyTypeContainsUnfixedParameter(InputTypes(argument, parameterType)); + } + + bool OutputTypeContainsUnfixed(ResolveResult argument, IType parameterType) + { + return AnyTypeContainsUnfixedParameter(OutputTypes(argument, parameterType)); + } + + bool AnyTypeContainsUnfixedParameter(IEnumerable types) + { + OccursInVisitor o = new OccursInVisitor(this); + foreach (var type in types) { + type.AcceptVisitor(o); + } + for (int i = 0; i < typeParameters.Length; i++) { + if (!typeParameters[i].IsFixed && o.Occurs[i]) + return true; + } + return false; + } + #endregion + + #region DependsOn (§7.5.2.5) + // C# 4.0 spec: §7.5.2.5 Dependance + + void CalculateDependencyMatrix() + { + int n = typeParameters.Length; + dependencyMatrix = new bool[n, n]; + for (int k = 0; k < arguments.Length; k++) { + OccursInVisitor input = new OccursInVisitor(this); + OccursInVisitor output = new OccursInVisitor(this); + foreach (var type in InputTypes(arguments[k], parameterTypes[k])) { + type.AcceptVisitor(input); + } + foreach (var type in OutputTypes(arguments[k], parameterTypes[k])) { + type.AcceptVisitor(output); + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dependencyMatrix[i, j] |= input.Occurs[j] && output.Occurs[i]; + } + } + } + // calculate transitive closure using Warshall's algorithm: + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (dependencyMatrix[i, j]) { + for (int k = 0; k < n; k++) { + if (dependencyMatrix[j, k]) + dependencyMatrix[i, k] = true; + } + } + } + } + } + + bool DependsOn(TP x, TP y) + { + if (dependencyMatrix == null) + CalculateDependencyMatrix(); + // x depends on y + return dependencyMatrix[x.TypeParameter.Index, y.TypeParameter.Index]; + } + #endregion + + #region MakeOutputTypeInference (§7.5.2.6) + void MakeOutputTypeInference(ResolveResult e, IType t) + { + Log.WriteLine(" MakeOutputTypeInference from " + e + " to " + t); + // If E is an anonymous function with inferred return type U (§7.5.2.12) and T is a delegate type or expression + // tree type with return type Tb, then a lower-bound inference (§7.5.2.9) is made from U to Tb. + LambdaResolveResult lrr = e as LambdaResolveResult; + if (lrr != null) { + IMethod m = GetDelegateOrExpressionTreeSignature(t); + if (m != null) { + IType inferredReturnType; + if (lrr.IsImplicitlyTyped) { + if (m.Parameters.Count != lrr.Parameters.Count) + return; // cannot infer due to mismatched parameter lists + TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); + IType[] inferredParameterTypes = new IType[m.Parameters.Count]; + for (int i = 0; i < inferredParameterTypes.Length; i++) { + IType parameterType = m.Parameters[i].Type; + inferredParameterTypes[i] = parameterType.AcceptVisitor(substitution); + } + inferredReturnType = lrr.GetInferredReturnType(inferredParameterTypes); + } else { + inferredReturnType = lrr.GetInferredReturnType(null); + } + MakeLowerBoundInference(inferredReturnType, m.ReturnType); + return; + } + } + // Otherwise, if E is a method group and T is a delegate type or expression tree type + // with parameter types T1…Tk and return type Tb, and overload resolution + // of E with the types T1…Tk yields a single method with return type U, then a lower­-bound + // inference is made from U to Tb. + MethodGroupResolveResult mgrr = e as MethodGroupResolveResult; + if (mgrr != null) { + IMethod m = GetDelegateOrExpressionTreeSignature(t); + if (m != null) { + ResolveResult[] args = new ResolveResult[m.Parameters.Count]; + TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); + for (int i = 0; i < args.Length; i++) { + IParameter param = m.Parameters[i]; + IType parameterType = param.Type.AcceptVisitor(substitution); + if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { + parameterType = ((ByReferenceType)parameterType).ElementType; + args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); + } else { + args[i] = new ResolveResult(parameterType); + } + } + var or = mgrr.PerformOverloadResolution(compilation, + args, + allowExpandingParams: false, allowOptionalParameters: false); + if (or.FoundApplicableCandidate && or.BestCandidateAmbiguousWith == null) { + IType returnType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType; + MakeLowerBoundInference(returnType, m.ReturnType); + } + } + return; + } + // Otherwise, if E is an expression with type U, then a lower-bound inference is made from U to T. + if (IsValidType(e.Type)) { + MakeLowerBoundInference(e.Type, t); + } + } + + TypeParameterSubstitution GetSubstitutionForFixedTPs() + { + IType[] fixedTypes = new IType[typeParameters.Length]; + for (int i = 0; i < fixedTypes.Length; i++) { + fixedTypes[i] = typeParameters[i].FixedTo ?? SpecialType.UnknownType; + } + return new TypeParameterSubstitution(classTypeArguments, fixedTypes); + } + #endregion + + #region MakeExplicitParameterTypeInference (§7.5.2.7) + void MakeExplicitParameterTypeInference(LambdaResolveResult e, IType t) + { + // C# 4.0 spec: §7.5.2.7 Explicit parameter type inferences + if (e.IsImplicitlyTyped || !e.HasParameterList) + return; + Log.WriteLine(" MakeExplicitParameterTypeInference from " + e + " to " + t); + IMethod m = GetDelegateOrExpressionTreeSignature(t); + if (m == null) + return; + for (int i = 0; i < e.Parameters.Count && i < m.Parameters.Count; i++) { + MakeExactInference(e.Parameters[i].Type, m.Parameters[i].Type); + } + } + #endregion + + #region MakeExactInference (§7.5.2.8) + /// + /// Make exact inference from U to V. + /// C# 4.0 spec: §7.5.2.8 Exact inferences + /// + void MakeExactInference(IType U, IType V) + { + Log.WriteLine("MakeExactInference from " + U + " to " + V); + + // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. + TP tp = GetTPForType(V); + if (tp != null && tp.IsFixed == false) { + Log.WriteLine(" Add exact bound '" + U + "' to " + tp); + tp.AddExactBound(U); + return; + } + // Handle by reference types: + ByReferenceType brU = U as ByReferenceType; + ByReferenceType brV = V as ByReferenceType; + if (brU != null && brV != null) { + MakeExactInference(brU.ElementType, brV.ElementType); + return; + } + // Handle array types: + ArrayType arrU = U as ArrayType; + ArrayType arrV = V as ArrayType; + if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) { + MakeExactInference(arrU.ElementType, arrV.ElementType); + return; + } + // Handle parameterized type: + ParameterizedType pU = U as ParameterizedType; + ParameterizedType pV = V as ParameterizedType; + if (pU != null && pV != null + && object.Equals(pU.GetDefinition(), pV.GetDefinition()) + && pU.TypeParameterCount == pV.TypeParameterCount) + { + Log.Indent(); + for (int i = 0; i < pU.TypeParameterCount; i++) { + MakeExactInference(pU.GetTypeArgument(i), pV.GetTypeArgument(i)); + } + Log.Unindent(); + } + } + + TP GetTPForType(IType v) + { + ITypeParameter p = v as ITypeParameter; + if (p != null) { + int index = p.Index; + if (index < typeParameters.Length && typeParameters[index].TypeParameter == p) + return typeParameters[index]; + } + return null; + } + #endregion + + #region MakeLowerBoundInference (§7.5.2.9) + /// + /// Make lower bound inference from U to V. + /// C# 4.0 spec: §7.5.2.9 Lower-bound inferences + /// + void MakeLowerBoundInference(IType U, IType V) + { + Log.WriteLine(" MakeLowerBoundInference from " + U + " to " + V); + + // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. + TP tp = GetTPForType(V); + if (tp != null && tp.IsFixed == false) { + Log.WriteLine(" Add lower bound '" + U + "' to " + tp); + tp.LowerBounds.Add(U); + return; + } + // Handle nullable covariance: + if (NullableType.IsNullable(U) && NullableType.IsNullable(V)) { + MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V)); + return; + } + + // Handle array types: + ArrayType arrU = U as ArrayType; + ArrayType arrV = V as ArrayType; + ParameterizedType pV = V as ParameterizedType; + if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) { + MakeLowerBoundInference(arrU.ElementType, arrV.ElementType); + return; + } else if (arrU != null && IsGenericInterfaceImplementedByArray(pV) && arrU.Dimensions == 1) { + MakeLowerBoundInference(arrU.ElementType, pV.GetTypeArgument(0)); + return; + } + // Handle parameterized types: + if (pV != null) { + ParameterizedType uniqueBaseType = null; + foreach (IType baseU in U.GetAllBaseTypes()) { + ParameterizedType pU = baseU as ParameterizedType; + if (pU != null && object.Equals(pU.GetDefinition(), pV.GetDefinition()) && pU.TypeParameterCount == pV.TypeParameterCount) { + if (uniqueBaseType == null) + uniqueBaseType = pU; + else + return; // cannot make an inference because it's not unique + } + } + Log.Indent(); + if (uniqueBaseType != null) { + for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) { + IType Ui = uniqueBaseType.GetTypeArgument(i); + IType Vi = pV.GetTypeArgument(i); + if (Ui.IsReferenceType == true) { + // look for variance + ITypeParameter Xi = pV.GetDefinition().TypeParameters[i]; + switch (Xi.Variance) { + case VarianceModifier.Covariant: + MakeLowerBoundInference(Ui, Vi); + break; + case VarianceModifier.Contravariant: + MakeUpperBoundInference(Ui, Vi); + break; + default: // invariant + MakeExactInference(Ui, Vi); + break; + } + } else { + // not known to be a reference type + MakeExactInference(Ui, Vi); + } + } + } + Log.Unindent(); + } + } + + static bool IsGenericInterfaceImplementedByArray(ParameterizedType rt) + { + if (rt == null || rt.TypeParameterCount != 1) + return false; + switch (rt.GetDefinition().KnownTypeCode) { + case KnownTypeCode.IEnumerableOfT: + case KnownTypeCode.ICollectionOfT: + case KnownTypeCode.IListOfT: + case KnownTypeCode.IReadOnlyCollectionOfT: + case KnownTypeCode.IReadOnlyListOfT: + return true; + default: + return false; + } + } + #endregion + + #region MakeUpperBoundInference (§7.5.2.10) + /// + /// Make upper bound inference from U to V. + /// C# 4.0 spec: §7.5.2.10 Upper-bound inferences + /// + void MakeUpperBoundInference(IType U, IType V) + { + Log.WriteLine(" MakeUpperBoundInference from " + U + " to " + V); + + // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. + TP tp = GetTPForType(V); + if (tp != null && tp.IsFixed == false) { + Log.WriteLine(" Add upper bound '" + U + "' to " + tp); + tp.UpperBounds.Add(U); + return; + } + + // Handle array types: + ArrayType arrU = U as ArrayType; + ArrayType arrV = V as ArrayType; + ParameterizedType pU = U as ParameterizedType; + if (arrV != null && arrU != null && arrU.Dimensions == arrV.Dimensions) { + MakeUpperBoundInference(arrU.ElementType, arrV.ElementType); + return; + } else if (arrV != null && IsGenericInterfaceImplementedByArray(pU) && arrV.Dimensions == 1) { + MakeUpperBoundInference(pU.GetTypeArgument(0), arrV.ElementType); + return; + } + // Handle parameterized types: + if (pU != null) { + ParameterizedType uniqueBaseType = null; + foreach (IType baseV in V.GetAllBaseTypes()) { + ParameterizedType pV = baseV as ParameterizedType; + if (pV != null && object.Equals(pU.GetDefinition(), pV.GetDefinition()) && pU.TypeParameterCount == pV.TypeParameterCount) { + if (uniqueBaseType == null) + uniqueBaseType = pV; + else + return; // cannot make an inference because it's not unique + } + } + Log.Indent(); + if (uniqueBaseType != null) { + for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) { + IType Ui = pU.GetTypeArgument(i); + IType Vi = uniqueBaseType.GetTypeArgument(i); + if (Ui.IsReferenceType == true) { + // look for variance + ITypeParameter Xi = pU.GetDefinition().TypeParameters[i]; + switch (Xi.Variance) { + case VarianceModifier.Covariant: + MakeUpperBoundInference(Ui, Vi); + break; + case VarianceModifier.Contravariant: + MakeLowerBoundInference(Ui, Vi); + break; + default: // invariant + MakeExactInference(Ui, Vi); + break; + } + } else { + // not known to be a reference type + MakeExactInference(Ui, Vi); + } + } + } + Log.Unindent(); + } + } + #endregion + + #region Fixing (§7.5.2.11) + bool Fix(TP tp) + { + Log.WriteLine(" Trying to fix " + tp); + Debug.Assert(!tp.IsFixed); + if (tp.ExactBound != null) { + // the exact bound will always be the result + tp.FixedTo = tp.ExactBound; + // check validity + if (tp.MultipleDifferentExactBounds) + return false; + return tp.LowerBounds.All(b => conversions.ImplicitConversion(b, tp.FixedTo).IsValid) + && tp.UpperBounds.All(b => conversions.ImplicitConversion(tp.FixedTo, b).IsValid); + } + Log.Indent(); + var types = CreateNestedInstance().FindTypesInBounds(tp.LowerBounds.ToArray(), tp.UpperBounds.ToArray()); + Log.Unindent(); + if (algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults) { + tp.FixedTo = IntersectionType.Create(types); + Log.WriteLine(" T was fixed " + (types.Count >= 1 ? "successfully" : "(with errors)") + " to " + tp.FixedTo); + return types.Count >= 1; + } else { + tp.FixedTo = GetFirstTypePreferNonInterfaces(types); + Log.WriteLine(" T was fixed " + (types.Count == 1 ? "successfully" : "(with errors)") + " to " + tp.FixedTo); + return types.Count == 1; + } + } + #endregion + + #region Finding the best common type of a set of expresssions + /// + /// Gets the best common type (C# 4.0 spec: §7.5.2.14) of a set of expressions. + /// + public IType GetBestCommonType(IList expressions, out bool success) + { + if (expressions == null) + throw new ArgumentNullException("expressions"); + if (expressions.Count == 1) { + success = (expressions[0].Type.Kind != TypeKind.Unknown); + return expressions[0].Type; + } + Log.WriteCollection("GetBestCommonType() for ", expressions); + try { + ITypeParameter tp = DummyTypeParameter.GetMethodTypeParameter(0); + this.typeParameters = new TP[1] { new TP(tp) }; + foreach (ResolveResult r in expressions) { + MakeOutputTypeInference(r, tp); + } + success = Fix(typeParameters[0]); + return typeParameters[0].FixedTo ?? SpecialType.UnknownType; + } finally { + Reset(); + } + } + #endregion + + #region FindTypeInBounds + /// + /// Finds a type that satisfies the given lower and upper bounds. + /// + public IType FindTypeInBounds(IList lowerBounds, IList upperBounds) + { + if (lowerBounds == null) + throw new ArgumentNullException("lowerBounds"); + if (upperBounds == null) + throw new ArgumentNullException("upperBounds"); + + IList result = FindTypesInBounds(lowerBounds, upperBounds); + + if (algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults) { + return IntersectionType.Create(result); + } else { + // return any of the candidates (prefer non-interfaces) + return GetFirstTypePreferNonInterfaces(result); + } + } + + static IType GetFirstTypePreferNonInterfaces(IList result) + { + return result.FirstOrDefault(c => c.Kind != TypeKind.Interface) + ?? result.FirstOrDefault() ?? SpecialType.UnknownType; + } + + IList FindTypesInBounds(IList lowerBounds, IList upperBounds) + { + // If there's only a single type; return that single type. + // If both inputs are empty, return the empty list. + if (lowerBounds.Count == 0 && upperBounds.Count <= 1) + return upperBounds; + if (upperBounds.Count == 0 && lowerBounds.Count <= 1) + return lowerBounds; + if (nestingLevel > maxNestingLevel) + return EmptyList.Instance; + + // Finds a type X so that "LB <: X <: UB" + Log.WriteCollection("FindTypesInBound, LowerBounds=", lowerBounds); + Log.WriteCollection("FindTypesInBound, UpperBounds=", upperBounds); + + // First try the Fixing algorithm from the C# spec (§7.5.2.11) + List candidateTypes = lowerBounds.Union(upperBounds) + .Where(c => lowerBounds.All(b => conversions.ImplicitConversion(b, c).IsValid)) + .Where(c => upperBounds.All(b => conversions.ImplicitConversion(c, b).IsValid)) + .ToList(); // evaluate the query only once + + Log.WriteCollection("FindTypesInBound, Candidates=", candidateTypes); + + // According to the C# specification, we need to pick the most specific + // of the candidate types. (the type which has conversions to all others) + // However, csc actually seems to choose the least specific. + candidateTypes = candidateTypes.Where( + c => candidateTypes.All(o => conversions.ImplicitConversion(o, c).IsValid) + ).ToList(); + + // If the specified algorithm produces a single candidate, we return + // that candidate. + // We also return the whole candidate list if we're not using the improved + // algorithm. + if (candidateTypes.Count == 1 || !(algorithm == TypeInferenceAlgorithm.Improved || algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults)) + { + return candidateTypes; + } + candidateTypes.Clear(); + + // Now try the improved algorithm + Log.Indent(); + List candidateTypeDefinitions; + if (lowerBounds.Count > 0) { + // Find candidates by using the lower bounds: + var hashSet = new HashSet(lowerBounds[0].GetAllBaseTypeDefinitions()); + for (int i = 1; i < lowerBounds.Count; i++) { + hashSet.IntersectWith(lowerBounds[i].GetAllBaseTypeDefinitions()); + } + candidateTypeDefinitions = hashSet.ToList(); + } else { + // Find candidates by looking at all classes in the project: + candidateTypeDefinitions = compilation.GetAllTypeDefinitions().ToList(); + } + + // Now filter out candidates that violate the upper bounds: + foreach (IType ub in upperBounds) { + ITypeDefinition ubDef = ub.GetDefinition(); + if (ubDef != null) { + candidateTypeDefinitions.RemoveAll(c => !c.IsDerivedFrom(ubDef)); + } + } + + foreach (ITypeDefinition candidateDef in candidateTypeDefinitions) { + // determine the type parameters for the candidate: + IType candidate; + if (candidateDef.TypeParameterCount == 0) { + candidate = candidateDef; + } else { + Log.WriteLine("Inferring arguments for candidate type definition: " + candidateDef); + bool success; + IType[] result = InferTypeArgumentsFromBounds( + candidateDef.TypeParameters, + new ParameterizedType(candidateDef, candidateDef.TypeParameters), + lowerBounds, upperBounds, + out success); + if (success) { + candidate = new ParameterizedType(candidateDef, result); + } else { + Log.WriteLine("Inference failed; ignoring candidate"); + continue; + } + } + Log.WriteLine("Candidate type: " + candidate); + + if (upperBounds.Count == 0) { + // if there were only lower bounds, we aim for the most specific candidate: + + // if this candidate isn't made redundant by an existing, more specific candidate: + if (!candidateTypes.Any(c => c.GetDefinition().IsDerivedFrom(candidateDef))) { + // remove all existing candidates made redundant by this candidate: + candidateTypes.RemoveAll(c => candidateDef.IsDerivedFrom(c.GetDefinition())); + // add new candidate + candidateTypes.Add(candidate); + } + } else { + // if there were upper bounds, we aim for the least specific candidate: + + // if this candidate isn't made redundant by an existing, less specific candidate: + if (!candidateTypes.Any(c => candidateDef.IsDerivedFrom(c.GetDefinition()))) { + // remove all existing candidates made redundant by this candidate: + candidateTypes.RemoveAll(c => c.GetDefinition().IsDerivedFrom(candidateDef)); + // add new candidate + candidateTypes.Add(candidate); + } + } + } + Log.Unindent(); + return candidateTypes; + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index c712ae7a0..a124b1164 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -23,7 +23,6 @@ using System.Linq; using System.Threading; using ICSharpCode.Decompiler.IL; using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.Analysis; using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index f8a1a2d49..dc0644989 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -23,6 +23,7 @@ using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; +using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; @@ -310,7 +311,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); BlockStatement block = (BlockStatement)varDecl.Parent; - DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(block, context.CancellationToken); + DefiniteAssignmentAnalysis daa = CreateDAA(block); daa.SetAnalyzedRange(targetStatement, block, startInclusive: false); daa.Analyze(varDecl.Variables.Single().Name); return daa.UnassignedVariableUses.Count == 0; @@ -332,7 +333,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms List blocks = targetStatement.Ancestors.TakeWhile(block => block != varDecl.Parent).OfType().ToList(); blocks.Add((BlockStatement)varDecl.Parent); // also handle the varDecl.Parent block itself blocks.Reverse(); // go from parent blocks to child blocks - DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(blocks[0], context.CancellationToken); + var daa = CreateDAA(blocks[0]); declarationPoint = null; return false; /*foreach (BlockStatement block in blocks) { @@ -342,7 +343,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } return true;*/ } - + + private DefiniteAssignmentAnalysis CreateDAA(BlockStatement block) + { + var typeResolveContext = new CSharpTypeResolveContext(context.TypeSystem.MainAssembly); + return new DefiniteAssignmentAnalysis(block, (node, ct) => node.GetResolveResult(), typeResolveContext, context.CancellationToken); + } + /// /// Gets whether there is an assignment to 'variableName' anywhere within the given node. /// diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs new file mode 100644 index 000000000..3b3e9e5ef --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Looks up an alias (identifier in front of :: operator). + /// + /// + /// The member lookup performed by the :: operator is handled + /// by . + /// + [Serializable] + public sealed class AliasNamespaceReference : TypeOrNamespaceReference, ISupportsInterning + { + readonly string identifier; + + public AliasNamespaceReference(string identifier) + { + if (identifier == null) + throw new ArgumentNullException("identifier"); + this.identifier = identifier; + } + + public string Identifier { + get { return identifier; } + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveAlias(identifier); + } + + public override IType ResolveType(CSharpResolver resolver) + { + // alias cannot refer to types + return SpecialType.UnknownType; + } + + public override string ToString() + { + return identifier + "::"; + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return identifier.GetHashCode(); + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + AliasNamespaceReference anr = other as AliasNamespaceReference; + return anr != null && this.identifier == anr.identifier; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/AttributeTypeReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/AttributeTypeReference.cs new file mode 100644 index 000000000..36f4ee0f0 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/AttributeTypeReference.cs @@ -0,0 +1,91 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Type reference used within an attribute. + /// Looks up both 'withoutSuffix' and 'withSuffix' and returns the type that exists. + /// + [Serializable] + public sealed class AttributeTypeReference : ITypeReference, ISupportsInterning + { + readonly ITypeReference withoutSuffix, withSuffix; + + public AttributeTypeReference(ITypeReference withoutSuffix, ITypeReference withSuffix) + { + if (withoutSuffix == null) + throw new ArgumentNullException("withoutSuffix"); + if (withSuffix == null) + throw new ArgumentNullException("withSuffix"); + this.withoutSuffix = withoutSuffix; + this.withSuffix = withSuffix; + } + + public IType Resolve(ITypeResolveContext context) + { + IType t1 = withoutSuffix.Resolve(context); + IType t2 = withSuffix.Resolve(context); + return PreferAttributeTypeWithSuffix(t1, t2, context.Compilation) ? t2 : t1; + } + + internal static bool PreferAttributeTypeWithSuffix(IType t1, IType t2, ICompilation compilation) + { + if (t2.Kind == TypeKind.Unknown) return false; + if (t1.Kind == TypeKind.Unknown) return true; + + var attrTypeDef = compilation.FindType(KnownTypeCode.Attribute).GetDefinition(); + if (attrTypeDef != null) { + bool t1IsAttribute = (t1.GetDefinition() != null && t1.GetDefinition().IsDerivedFrom(attrTypeDef)); + bool t2IsAttribute = (t2.GetDefinition() != null && t2.GetDefinition().IsDerivedFrom(attrTypeDef)); + if (t2IsAttribute && !t1IsAttribute) + return true; + // If both types exist and are attributes, C# considers that to be an ambiguity, but we are less strict. + } + return false; + } + + public override string ToString() + { + return withoutSuffix.ToString() + "[Attribute]"; + } + + int ISupportsInterning.GetHashCodeForInterning() + { + unchecked { + return withoutSuffix.GetHashCode() + 715613 * withSuffix.GetHashCode(); + } + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + AttributeTypeReference atr = other as AttributeTypeReference; + return atr != null && this.withoutSuffix == atr.withoutSuffix && this.withSuffix == atr.withSuffix; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpAttribute.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpAttribute.cs new file mode 100644 index 000000000..5af7d8e3b --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpAttribute.cs @@ -0,0 +1,169 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + [Serializable] + public sealed class CSharpAttribute : IUnresolvedAttribute + { + ITypeReference attributeType; + DomRegion region; + IList positionalArguments; + IList> namedCtorArguments; + IList> namedArguments; + + public CSharpAttribute(ITypeReference attributeType, DomRegion region, + IList positionalArguments, + IList> namedCtorArguments, + IList> namedArguments) + { + if (attributeType == null) + throw new ArgumentNullException("attributeType"); + this.attributeType = attributeType; + this.region = region; + this.positionalArguments = positionalArguments ?? EmptyList.Instance; + this.namedCtorArguments = namedCtorArguments ?? EmptyList>.Instance; + this.namedArguments = namedArguments ?? EmptyList>.Instance; + } + + public DomRegion Region { + get { return region; } + } + + public ITypeReference AttributeType { + get { return attributeType; } + } + + public IAttribute CreateResolvedAttribute(ITypeResolveContext context) + { + return new CSharpResolvedAttribute((CSharpTypeResolveContext)context, this); + } + + sealed class CSharpResolvedAttribute : IAttribute + { + readonly CSharpTypeResolveContext context; + readonly CSharpAttribute unresolved; + readonly IType attributeType; + + IList> namedArguments; + + public CSharpResolvedAttribute(CSharpTypeResolveContext context, CSharpAttribute unresolved) + { + this.context = context; + this.unresolved = unresolved; + // Pretty much any access to the attribute checks the type first, so + // we don't need to use lazy-loading for that. + this.attributeType = unresolved.AttributeType.Resolve(context); + } + + DomRegion IAttribute.Region { + get { return unresolved.Region; } + } + + IType IAttribute.AttributeType { + get { return attributeType; } + } + + ResolveResult ctorInvocation; + + InvocationResolveResult GetCtorInvocation() + { + ResolveResult rr = LazyInit.VolatileRead(ref this.ctorInvocation); + if (rr != null) { + return rr as InvocationResolveResult; + } else { + CSharpResolver resolver = new CSharpResolver(context); + int totalArgumentCount = unresolved.positionalArguments.Count + unresolved.namedCtorArguments.Count; + ResolveResult[] arguments = new ResolveResult[totalArgumentCount]; + string[] argumentNames = new string[totalArgumentCount]; + int i = 0; + while (i < unresolved.positionalArguments.Count) { + IConstantValue cv = unresolved.positionalArguments[i]; + arguments[i] = cv.Resolve(context); + i++; + } + foreach (var pair in unresolved.namedCtorArguments) { + argumentNames[i] = pair.Key; + arguments[i] = pair.Value.Resolve(context); + i++; + } + rr = resolver.ResolveObjectCreation(attributeType, arguments, argumentNames); + return LazyInit.GetOrSet(ref this.ctorInvocation, rr) as InvocationResolveResult; + } + } + + IMethod IAttribute.Constructor { + get { + var invocation = GetCtorInvocation(); + if (invocation != null) + return invocation.Member as IMethod; + else + return null; + } + } + + IList positionalArguments; + + IList IAttribute.PositionalArguments { + get { + var result = LazyInit.VolatileRead(ref this.positionalArguments); + if (result != null) { + return result; + } else { + var invocation = GetCtorInvocation(); + if (invocation != null) + result = invocation.GetArgumentsForCall(); + else + result = EmptyList.Instance; + return LazyInit.GetOrSet(ref this.positionalArguments, result); + } + } + } + + IList> IAttribute.NamedArguments { + get { + var namedArgs = LazyInit.VolatileRead(ref this.namedArguments); + if (namedArgs != null) { + return namedArgs; + } else { + namedArgs = new List>(); + foreach (var pair in unresolved.namedArguments) { + IMember member = attributeType.GetMembers(m => (m.SymbolKind == SymbolKind.Field || m.SymbolKind == SymbolKind.Property) && m.Name == pair.Key).FirstOrDefault(); + if (member != null) { + ResolveResult val = pair.Value.Resolve(context); + namedArgs.Add(new KeyValuePair(member, val)); + } + } + return LazyInit.GetOrSet(ref this.namedArguments, namedArgs); + } + } + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpTypeResolveContext.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpTypeResolveContext.cs new file mode 100644 index 000000000..40933dbfd --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpTypeResolveContext.cs @@ -0,0 +1,96 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + public sealed class CSharpTypeResolveContext : ITypeResolveContext + { + readonly IAssembly assembly; + readonly ResolvedUsingScope currentUsingScope; + readonly ITypeDefinition currentTypeDefinition; + readonly IMember currentMember; + readonly string[] methodTypeParameterNames; + + public CSharpTypeResolveContext(IAssembly assembly, ResolvedUsingScope usingScope = null, ITypeDefinition typeDefinition = null, IMember member = null) + { + if (assembly == null) + throw new ArgumentNullException("assembly"); + this.assembly = assembly; + this.currentUsingScope = usingScope; + this.currentTypeDefinition = typeDefinition; + this.currentMember = member; + } + + private CSharpTypeResolveContext(IAssembly assembly, ResolvedUsingScope usingScope, ITypeDefinition typeDefinition, IMember member, string[] methodTypeParameterNames) + { + this.assembly = assembly; + this.currentUsingScope = usingScope; + this.currentTypeDefinition = typeDefinition; + this.currentMember = member; + this.methodTypeParameterNames = methodTypeParameterNames; + } + + public ResolvedUsingScope CurrentUsingScope { + get { return currentUsingScope; } + } + + public ICompilation Compilation { + get { return assembly.Compilation; } + } + + public IAssembly CurrentAssembly { + get { return assembly; } + } + + public ITypeDefinition CurrentTypeDefinition { + get { return currentTypeDefinition; } + } + + public IMember CurrentMember { + get { return currentMember; } + } + + public CSharpTypeResolveContext WithCurrentTypeDefinition(ITypeDefinition typeDefinition) + { + return new CSharpTypeResolveContext(assembly, currentUsingScope, typeDefinition, currentMember, methodTypeParameterNames); + } + + ITypeResolveContext ITypeResolveContext.WithCurrentTypeDefinition(ITypeDefinition typeDefinition) + { + return WithCurrentTypeDefinition(typeDefinition); + } + + public CSharpTypeResolveContext WithCurrentMember(IMember member) + { + return new CSharpTypeResolveContext(assembly, currentUsingScope, currentTypeDefinition, member, methodTypeParameterNames); + } + + ITypeResolveContext ITypeResolveContext.WithCurrentMember(IMember member) + { + return WithCurrentMember(member); + } + + public CSharpTypeResolveContext WithUsingScope(ResolvedUsingScope usingScope) + { + return new CSharpTypeResolveContext(assembly, usingScope, currentTypeDefinition, currentMember, methodTypeParameterNames); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/ConstantValues.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/ConstantValues.cs new file mode 100644 index 000000000..24760b0db --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/ConstantValues.cs @@ -0,0 +1,520 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem.ConstantValues +{ + // Contains representations for constant C# expressions. + // We use these instead of storing the full AST to reduce the memory usage. + + [Serializable] + public abstract class ConstantExpression : IConstantValue + { + public abstract ResolveResult Resolve(CSharpResolver resolver); + + public ResolveResult Resolve(ITypeResolveContext context) + { + var csContext = (CSharpTypeResolveContext)context; + if (context.CurrentAssembly != context.Compilation.MainAssembly) { + // The constant needs to be resolved in a different compilation. + IProjectContent pc = context.CurrentAssembly as IProjectContent; + if (pc != null) { + ICompilation nestedCompilation = context.Compilation.SolutionSnapshot.GetCompilation(pc); + if (nestedCompilation != null) { + var nestedContext = MapToNestedCompilation(csContext, nestedCompilation); + ResolveResult rr = Resolve(new CSharpResolver(nestedContext)); + return MapToNewContext(rr, context); + } + } + } + // Resolve in current context. + return Resolve(new CSharpResolver(csContext)); + } + + CSharpTypeResolveContext MapToNestedCompilation(CSharpTypeResolveContext context, ICompilation nestedCompilation) + { + var nestedContext = new CSharpTypeResolveContext(nestedCompilation.MainAssembly); + if (context.CurrentUsingScope != null) { + nestedContext = nestedContext.WithUsingScope(context.CurrentUsingScope.UnresolvedUsingScope.Resolve(nestedCompilation)); + } + if (context.CurrentTypeDefinition != null) { + nestedContext = nestedContext.WithCurrentTypeDefinition(nestedCompilation.Import(context.CurrentTypeDefinition)); + } + return nestedContext; + } + + static ResolveResult MapToNewContext(ResolveResult rr, ITypeResolveContext newContext) + { + if (rr is TypeOfResolveResult) { + return new TypeOfResolveResult( + rr.Type.ToTypeReference().Resolve(newContext), + ((TypeOfResolveResult)rr).ReferencedType.ToTypeReference().Resolve(newContext)); + } else if (rr is ArrayCreateResolveResult) { + ArrayCreateResolveResult acrr = (ArrayCreateResolveResult)rr; + return new ArrayCreateResolveResult( + acrr.Type.ToTypeReference().Resolve(newContext), + MapToNewContext(acrr.SizeArguments, newContext), + MapToNewContext(acrr.InitializerElements, newContext)); + } else if (rr.IsCompileTimeConstant) { + return new ConstantResolveResult( + rr.Type.ToTypeReference().Resolve(newContext), + rr.ConstantValue + ); + } else { + return new ErrorResolveResult(rr.Type.ToTypeReference().Resolve(newContext)); + } + } + + static ResolveResult[] MapToNewContext(IList input, ITypeResolveContext newContext) + { + if (input == null) + return null; + ResolveResult[] output = new ResolveResult[input.Count]; + for (int i = 0; i < output.Length; i++) { + output[i] = MapToNewContext(input[i], newContext); + } + return output; + } + } + + /// + /// Used for constants that could not be converted to IConstantValue. + /// + [Serializable] + public sealed class ErrorConstantValue : IConstantValue + { + readonly ITypeReference type; + + public ErrorConstantValue(ITypeReference type) + { + if (type == null) + throw new ArgumentNullException("type"); + this.type = type; + } + + public ResolveResult Resolve(ITypeResolveContext context) + { + return new ErrorResolveResult(type.Resolve(context)); + } + } + + /// + /// Increments an integer by a fixed amount without changing the type. + /// + [Serializable] + public sealed class IncrementConstantValue : IConstantValue, ISupportsInterning + { + readonly IConstantValue baseValue; + readonly int incrementAmount; + + public IncrementConstantValue(IConstantValue baseValue, int incrementAmount = 1) + { + if (baseValue == null) + throw new ArgumentNullException("baseValue"); + IncrementConstantValue icv = baseValue as IncrementConstantValue; + if (icv != null) { + this.baseValue = icv.baseValue; + this.incrementAmount = icv.incrementAmount + incrementAmount; + } else { + this.baseValue = baseValue; + this.incrementAmount = incrementAmount; + } + } + + public ResolveResult Resolve(ITypeResolveContext context) + { + ResolveResult rr = baseValue.Resolve(context); + if (rr.IsCompileTimeConstant && rr.ConstantValue != null) { + object val = rr.ConstantValue; + TypeCode typeCode = Type.GetTypeCode(val.GetType()); + if (typeCode >= TypeCode.SByte && typeCode <= TypeCode.UInt64) { + long intVal = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, val, false); + object newVal = CSharpPrimitiveCast.Cast(typeCode, unchecked(intVal + incrementAmount), false); + return new ConstantResolveResult(rr.Type, newVal); + } + } + return new ErrorResolveResult(rr.Type); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + unchecked { + return baseValue.GetHashCode() * 33 ^ incrementAmount; + } + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + IncrementConstantValue o = other as IncrementConstantValue; + return o != null && baseValue == o.baseValue && incrementAmount == o.incrementAmount; + } + } + + /// + /// C#'s equivalent to the SimpleConstantValue. + /// + [Serializable] + public sealed class PrimitiveConstantExpression : ConstantExpression, ISupportsInterning + { + readonly ITypeReference type; + readonly object value; + + public ITypeReference Type { + get { return type; } + } + + public object Value { + get { return value; } + } + + public PrimitiveConstantExpression(ITypeReference type, object value) + { + if (type == null) + throw new ArgumentNullException("type"); + this.type = type; + this.value = value; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return new ConstantResolveResult(type.Resolve(resolver.CurrentTypeResolveContext), value); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return type.GetHashCode() ^ (value != null ? value.GetHashCode() : 0); + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + PrimitiveConstantExpression scv = other as PrimitiveConstantExpression; + return scv != null && type == scv.type && value == scv.value; + } + } + + [Serializable] + public sealed class TypeOfConstantExpression : ConstantExpression + { + readonly ITypeReference type; + + public ITypeReference Type { + get { return type; } + } + + public TypeOfConstantExpression(ITypeReference type) + { + this.type = type; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveTypeOf(type.Resolve(resolver.CurrentTypeResolveContext)); + } + } + + [Serializable] + public sealed class ConstantCast : ConstantExpression, ISupportsInterning + { + readonly ITypeReference targetType; + readonly ConstantExpression expression; + readonly bool allowNullableConstants; + + public ConstantCast(ITypeReference targetType, ConstantExpression expression, bool allowNullableConstants) + { + if (targetType == null) + throw new ArgumentNullException("targetType"); + if (expression == null) + throw new ArgumentNullException("expression"); + this.targetType = targetType; + this.expression = expression; + this.allowNullableConstants = allowNullableConstants; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + var type = targetType.Resolve(resolver.CurrentTypeResolveContext); + var resolveResult = expression.Resolve(resolver); + if (allowNullableConstants && NullableType.IsNullable(type)) { + resolveResult = resolver.ResolveCast(NullableType.GetUnderlyingType(type), resolveResult); + if (resolveResult.IsCompileTimeConstant) + return new ConstantResolveResult(type, resolveResult.ConstantValue); + } + return resolver.ResolveCast(type, resolveResult); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + unchecked { + return targetType.GetHashCode() + expression.GetHashCode() * 1018829; + } + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + ConstantCast cast = other as ConstantCast; + return cast != null + && this.targetType == cast.targetType && this.expression == cast.expression && this.allowNullableConstants == cast.allowNullableConstants; + } + } + + [Serializable] + public sealed class ConstantIdentifierReference : ConstantExpression + { + readonly string identifier; + readonly IList typeArguments; + + public ConstantIdentifierReference(string identifier, IList typeArguments = null) + { + if (identifier == null) + throw new ArgumentNullException("identifier"); + this.identifier = identifier; + this.typeArguments = typeArguments ?? EmptyList.Instance; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveSimpleName(identifier, typeArguments.Resolve(resolver.CurrentTypeResolveContext)); + } + } + + [Serializable] + public sealed class ConstantMemberReference : ConstantExpression + { + readonly ITypeReference targetType; + readonly ConstantExpression targetExpression; + readonly string memberName; + readonly IList typeArguments; + + public ConstantMemberReference(ITypeReference targetType, string memberName, IList typeArguments = null) + { + if (targetType == null) + throw new ArgumentNullException("targetType"); + if (memberName == null) + throw new ArgumentNullException("memberName"); + this.targetType = targetType; + this.memberName = memberName; + this.typeArguments = typeArguments ?? EmptyList.Instance; + } + + public ConstantMemberReference(ConstantExpression targetExpression, string memberName, IList typeArguments = null) + { + if (targetExpression == null) + throw new ArgumentNullException("targetExpression"); + if (memberName == null) + throw new ArgumentNullException("memberName"); + this.targetExpression = targetExpression; + this.memberName = memberName; + this.typeArguments = typeArguments ?? EmptyList.Instance; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + ResolveResult rr; + if (targetType != null) + rr = new TypeResolveResult(targetType.Resolve(resolver.CurrentTypeResolveContext)); + else + rr = targetExpression.Resolve(resolver); + return resolver.ResolveMemberAccess(rr, memberName, typeArguments.Resolve(resolver.CurrentTypeResolveContext)); + } + } + + [Serializable] + public sealed class ConstantCheckedExpression : ConstantExpression + { + readonly bool checkForOverflow; + readonly ConstantExpression expression; + + public ConstantCheckedExpression(bool checkForOverflow, ConstantExpression expression) + { + if (expression == null) + throw new ArgumentNullException("expression"); + this.checkForOverflow = checkForOverflow; + this.expression = expression; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return expression.Resolve(resolver.WithCheckForOverflow(checkForOverflow)); + } + } + + [Serializable] + public sealed class ConstantDefaultValue : ConstantExpression, ISupportsInterning + { + readonly ITypeReference type; + + public ConstantDefaultValue(ITypeReference type) + { + if (type == null) + throw new ArgumentNullException("type"); + this.type = type; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveDefaultValue(type.Resolve(resolver.CurrentTypeResolveContext)); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return type.GetHashCode(); + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + ConstantDefaultValue o = other as ConstantDefaultValue; + return o != null && this.type == o.type; + } + } + + [Serializable] + public sealed class ConstantUnaryOperator : ConstantExpression + { + readonly UnaryOperatorType operatorType; + readonly ConstantExpression expression; + + public ConstantUnaryOperator(UnaryOperatorType operatorType, ConstantExpression expression) + { + if (expression == null) + throw new ArgumentNullException("expression"); + this.operatorType = operatorType; + this.expression = expression; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveUnaryOperator(operatorType, expression.Resolve(resolver)); + } + } + + [Serializable] + public sealed class ConstantBinaryOperator : ConstantExpression + { + readonly ConstantExpression left; + readonly BinaryOperatorType operatorType; + readonly ConstantExpression right; + + public ConstantBinaryOperator(ConstantExpression left, BinaryOperatorType operatorType, ConstantExpression right) + { + if (left == null) + throw new ArgumentNullException("left"); + if (right == null) + throw new ArgumentNullException("right"); + this.left = left; + this.operatorType = operatorType; + this.right = right; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + ResolveResult lhs = left.Resolve(resolver); + ResolveResult rhs = right.Resolve(resolver); + return resolver.ResolveBinaryOperator(operatorType, lhs, rhs); + } + } + + [Serializable] + public sealed class ConstantConditionalOperator : ConstantExpression + { + readonly ConstantExpression condition, trueExpr, falseExpr; + + public ConstantConditionalOperator(ConstantExpression condition, ConstantExpression trueExpr, ConstantExpression falseExpr) + { + if (condition == null) + throw new ArgumentNullException("condition"); + if (trueExpr == null) + throw new ArgumentNullException("trueExpr"); + if (falseExpr == null) + throw new ArgumentNullException("falseExpr"); + this.condition = condition; + this.trueExpr = trueExpr; + this.falseExpr = falseExpr; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveConditional( + condition.Resolve(resolver), + trueExpr.Resolve(resolver), + falseExpr.Resolve(resolver) + ); + } + } + + /// + /// Represents an array creation (as used within an attribute argument) + /// + [Serializable] + public sealed class ConstantArrayCreation : ConstantExpression + { + // type may be null when the element is being inferred + readonly ITypeReference elementType; + readonly IList arrayElements; + + public ConstantArrayCreation(ITypeReference type, IList arrayElements) + { + if (arrayElements == null) + throw new ArgumentNullException("arrayElements"); + this.elementType = type; + this.arrayElements = arrayElements; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + ResolveResult[] elements = new ResolveResult[arrayElements.Count]; + for (int i = 0; i < elements.Length; i++) { + elements[i] = arrayElements[i].Resolve(resolver); + } + int[] sizeArguments = { elements.Length }; + if (elementType != null) { + return resolver.ResolveArrayCreation(elementType.Resolve(resolver.CurrentTypeResolveContext), sizeArguments, elements); + } else { + return resolver.ResolveArrayCreation(null, sizeArguments, elements); + } + } + } + + /// + /// Used for sizeof() expressions in constants. + /// + [Serializable] + public sealed class SizeOfConstantValue : ConstantExpression + { + readonly ITypeReference type; + + public SizeOfConstantValue(ITypeReference type) + { + if (type == null) + throw new ArgumentNullException("type"); + this.type = type; + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + return resolver.ResolveSizeOf(type.Resolve(resolver.CurrentTypeResolveContext)); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs new file mode 100644 index 000000000..4315e8d29 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs @@ -0,0 +1,121 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Reference to a qualified type or namespace name. + /// + [Serializable] + public sealed class MemberTypeOrNamespaceReference : TypeOrNamespaceReference, ISupportsInterning + { + readonly TypeOrNamespaceReference target; + readonly string identifier; + readonly IList typeArguments; + readonly NameLookupMode lookupMode; + + public MemberTypeOrNamespaceReference(TypeOrNamespaceReference target, string identifier, IList typeArguments, NameLookupMode lookupMode = NameLookupMode.Type) + { + if (target == null) + throw new ArgumentNullException("target"); + if (identifier == null) + throw new ArgumentNullException("identifier"); + this.target = target; + this.identifier = identifier; + this.typeArguments = typeArguments ?? EmptyList.Instance; + this.lookupMode = lookupMode; + } + + public string Identifier { + get { return identifier; } + } + + public TypeOrNamespaceReference Target { + get { return target; } + } + + public IList TypeArguments { + get { return typeArguments; } + } + + public NameLookupMode LookupMode { + get { return lookupMode; } + } + + /// + /// Adds a suffix to the identifier. + /// Does not modify the existing type reference, but returns a new one. + /// + public MemberTypeOrNamespaceReference AddSuffix(string suffix) + { + return new MemberTypeOrNamespaceReference(target, identifier + suffix, typeArguments, lookupMode); + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + ResolveResult targetRR = target.Resolve(resolver); + if (targetRR.IsError) + return targetRR; + IList typeArgs = typeArguments.Resolve(resolver.CurrentTypeResolveContext); + return resolver.ResolveMemberAccess(targetRR, identifier, typeArgs, lookupMode); + } + + public override IType ResolveType(CSharpResolver resolver) + { + TypeResolveResult trr = Resolve(resolver) as TypeResolveResult; + return trr != null ? trr.Type : new UnknownType(null, identifier, typeArguments.Count); + } + + public override string ToString() + { + if (typeArguments.Count == 0) + return target.ToString() + "." + identifier; + else + return target.ToString() + "." + identifier + "<" + string.Join(",", typeArguments) + ">"; + } + + int ISupportsInterning.GetHashCodeForInterning() + { + int hashCode = 0; + unchecked { + hashCode += 1000000007 * target.GetHashCode(); + hashCode += 1000000033 * identifier.GetHashCode(); + hashCode += 1000000087 * typeArguments.GetHashCode(); + hashCode += 1000000021 * (int)lookupMode; + } + return hashCode; + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + MemberTypeOrNamespaceReference o = other as MemberTypeOrNamespaceReference; + return o != null && this.target == o.target + && this.identifier == o.identifier && this.typeArguments == o.typeArguments + && this.lookupMode == o.lookupMode; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs new file mode 100644 index 000000000..fc9e6cd86 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs @@ -0,0 +1,121 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + [Serializable] + public sealed class MethodTypeParameterWithInheritedConstraints : DefaultUnresolvedTypeParameter + { + public MethodTypeParameterWithInheritedConstraints(int index, string name) + : base(SymbolKind.Method, index, name) + { + } + + static ITypeParameter ResolveBaseTypeParameter(IMethod parentMethod, int index) + { + IMethod baseMethod = null; + if (parentMethod.IsOverride) { + foreach (IMethod m in InheritanceHelper.GetBaseMembers(parentMethod, false).OfType()) { + if (!m.IsOverride) { + baseMethod = m; + break; + } + } + } else if (parentMethod.IsExplicitInterfaceImplementation && parentMethod.ImplementedInterfaceMembers.Count == 1) { + baseMethod = parentMethod.ImplementedInterfaceMembers[0] as IMethod; + } + if (baseMethod != null && index < baseMethod.TypeParameters.Count) + return baseMethod.TypeParameters[index]; + else + return null; + } + + public override ITypeParameter CreateResolvedTypeParameter(ITypeResolveContext context) + { + if (context.CurrentMember is IMethod) { + return new ResolvedMethodTypeParameterWithInheritedConstraints(this, context); + } else { + return base.CreateResolvedTypeParameter(context); + } + } + + sealed class ResolvedMethodTypeParameterWithInheritedConstraints : AbstractTypeParameter + { + volatile ITypeParameter baseTypeParameter; + + public ResolvedMethodTypeParameterWithInheritedConstraints(MethodTypeParameterWithInheritedConstraints unresolved, ITypeResolveContext context) + : base(context.CurrentMember, unresolved.Index, unresolved.Name, unresolved.Variance, + unresolved.Attributes.CreateResolvedAttributes(context), unresolved.Region) + { + } + + ITypeParameter GetBaseTypeParameter() + { + ITypeParameter baseTP = this.baseTypeParameter; + if (baseTP == null) { + // ResolveBaseTypeParameter() is idempotent, so this is thread-safe. + this.baseTypeParameter = baseTP = ResolveBaseTypeParameter((IMethod)this.Owner, this.Index); + } + return baseTP; + } + + public override bool HasValueTypeConstraint { + get { + ITypeParameter baseTP = GetBaseTypeParameter(); + return baseTP != null ? baseTP.HasValueTypeConstraint : false; + } + } + + public override bool HasReferenceTypeConstraint { + get { + ITypeParameter baseTP = GetBaseTypeParameter(); + return baseTP != null ? baseTP.HasReferenceTypeConstraint : false; + } + } + + public override bool HasDefaultConstructorConstraint { + get { + ITypeParameter baseTP = GetBaseTypeParameter(); + return baseTP != null ? baseTP.HasDefaultConstructorConstraint : false; + } + } + + public override IEnumerable DirectBaseTypes { + get { + ITypeParameter baseTP = GetBaseTypeParameter(); + if (baseTP != null) { + // Substitute occurrences of the base method's type parameters in the constraints + // with the type parameters from the + IMethod owner = (IMethod)this.Owner; + var substitution = new TypeParameterSubstitution(null, new ProjectedList(owner.TypeParameters, t => t)); + return baseTP.DirectBaseTypes.Select(t => t.AcceptVisitor(substitution)); + } else { + return EmptyList.Instance; + } + } + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs new file mode 100644 index 000000000..210325117 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs @@ -0,0 +1,210 @@ +// Copyright (c) 2010-2013 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.Collections.ObjectModel; +using System.Diagnostics; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Resolved version of using scope. + /// + public class ResolvedUsingScope + { + readonly CSharpTypeResolveContext parentContext; + readonly UsingScope usingScope; + + internal readonly ConcurrentDictionary ResolveCache = new ConcurrentDictionary(); + internal List> AllExtensionMethods; + + public ResolvedUsingScope(CSharpTypeResolveContext context, UsingScope usingScope) + { + if (context == null) + throw new ArgumentNullException("context"); + if (usingScope == null) + throw new ArgumentNullException("usingScope"); + this.parentContext = context; + this.usingScope = usingScope; + if (usingScope.Parent != null) { + if (context.CurrentUsingScope == null) + throw new InvalidOperationException(); + } else { + if (context.CurrentUsingScope != null) + throw new InvalidOperationException(); + } + } + + public UsingScope UnresolvedUsingScope { + get { return usingScope; } + } + + INamespace @namespace; + + public INamespace Namespace { + get { + INamespace result = LazyInit.VolatileRead(ref this.@namespace); + if (result != null) { + return result; + } else { + if (parentContext.CurrentUsingScope != null) { + result = parentContext.CurrentUsingScope.Namespace.GetChildNamespace(usingScope.ShortNamespaceName); + if (result == null) + result = new DummyNamespace(parentContext.CurrentUsingScope.Namespace, usingScope.ShortNamespaceName); + } else { + result = parentContext.Compilation.RootNamespace; + } + Debug.Assert(result != null); + return LazyInit.GetOrSet(ref this.@namespace, result); + } + } + } + + public ResolvedUsingScope Parent { + get { return parentContext.CurrentUsingScope; } + } + + IList usings; + + public IList Usings { + get { + var result = LazyInit.VolatileRead(ref this.usings); + if (result != null) { + return result; + } else { + result = new List(); + CSharpResolver resolver = new CSharpResolver(parentContext.WithUsingScope(this)); + foreach (var u in usingScope.Usings) { + INamespace ns = u.ResolveNamespace(resolver); + if (ns != null && !result.Contains(ns)) + result.Add(ns); + } + return LazyInit.GetOrSet(ref this.usings, new ReadOnlyCollection(result)); + } + } + } + + IList> usingAliases; + + public IList> UsingAliases { + get { + var result = LazyInit.VolatileRead(ref this.usingAliases); + if (result != null) { + return result; + } else { + CSharpResolver resolver = new CSharpResolver(parentContext.WithUsingScope(this)); + result = new KeyValuePair[usingScope.UsingAliases.Count]; + for (int i = 0; i < result.Count; i++) { + var rr = usingScope.UsingAliases[i].Value.Resolve(resolver); + if (rr is TypeResolveResult) { + rr = new AliasTypeResolveResult (usingScope.UsingAliases[i].Key, (TypeResolveResult)rr); + } else if (rr is NamespaceResolveResult) { + rr = new AliasNamespaceResolveResult (usingScope.UsingAliases[i].Key, (NamespaceResolveResult)rr); + } + result[i] = new KeyValuePair( + usingScope.UsingAliases[i].Key, + rr + ); + } + return LazyInit.GetOrSet(ref this.usingAliases, result); + } + } + } + + public IList ExternAliases { + get { return usingScope.ExternAliases; } + } + + /// + /// Gets whether this using scope has an alias (either using or extern) + /// with the specified name. + /// + public bool HasAlias(string identifier) + { + return usingScope.HasAlias(identifier); + } + + sealed class DummyNamespace : INamespace + { + readonly INamespace parentNamespace; + readonly string name; + + public DummyNamespace(INamespace parentNamespace, string name) + { + this.parentNamespace = parentNamespace; + this.name = name; + } + + public string ExternAlias { get; set; } + + string INamespace.FullName { + get { return NamespaceDeclaration.BuildQualifiedName(parentNamespace.FullName, name); } + } + + public string Name { + get { return name; } + } + + SymbolKind ISymbol.SymbolKind { + get { return SymbolKind.Namespace; } + } + + INamespace INamespace.ParentNamespace { + get { return parentNamespace; } + } + + IEnumerable INamespace.ChildNamespaces { + get { return EmptyList.Instance; } + } + + IEnumerable INamespace.Types { + get { return EmptyList.Instance; } + } + + IEnumerable INamespace.ContributingAssemblies { + get { return EmptyList.Instance; } + } + + ICompilation ICompilationProvider.Compilation { + get { return parentNamespace.Compilation; } + } + + INamespace INamespace.GetChildNamespace(string name) + { + return null; + } + + ITypeDefinition INamespace.GetTypeDefinition(string name, int typeParameterCount) + { + return null; + } + + public ISymbolReference ToReference() + { + return new MergedNamespaceReference(ExternAlias, ((INamespace)this).FullName); + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs new file mode 100644 index 000000000..33cb1639c --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs @@ -0,0 +1,109 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Represents a simple C# name. (a single non-qualified identifier with an optional list of type arguments) + /// + [Serializable] + public sealed class SimpleTypeOrNamespaceReference : TypeOrNamespaceReference, ISupportsInterning + { + readonly string identifier; + readonly IList typeArguments; + readonly NameLookupMode lookupMode; + + public SimpleTypeOrNamespaceReference(string identifier, IList typeArguments, NameLookupMode lookupMode = NameLookupMode.Type) + { + if (identifier == null) + throw new ArgumentNullException("identifier"); + this.identifier = identifier; + this.typeArguments = typeArguments ?? EmptyList.Instance; + this.lookupMode = lookupMode; + } + + public string Identifier { + get { return identifier; } + } + + public IList TypeArguments { + get { return typeArguments; } + } + + public NameLookupMode LookupMode { + get { return lookupMode; } + } + + /// + /// Adds a suffix to the identifier. + /// Does not modify the existing type reference, but returns a new one. + /// + public SimpleTypeOrNamespaceReference AddSuffix(string suffix) + { + return new SimpleTypeOrNamespaceReference(identifier + suffix, typeArguments, lookupMode); + } + + public override ResolveResult Resolve(CSharpResolver resolver) + { + var typeArgs = typeArguments.Resolve(resolver.CurrentTypeResolveContext); + return resolver.LookupSimpleNameOrTypeName(identifier, typeArgs, lookupMode); + } + + public override IType ResolveType(CSharpResolver resolver) + { + TypeResolveResult trr = Resolve(resolver) as TypeResolveResult; + return trr != null ? trr.Type : new UnknownType(null, identifier, typeArguments.Count); + } + + public override string ToString() + { + if (typeArguments.Count == 0) + return identifier; + else + return identifier + "<" + string.Join(",", typeArguments) + ">"; + } + + int ISupportsInterning.GetHashCodeForInterning() + { + int hashCode = 0; + unchecked { + hashCode += 1000000021 * identifier.GetHashCode(); + hashCode += 1000000033 * typeArguments.GetHashCode(); + hashCode += 1000000087 * (int)lookupMode; + } + return hashCode; + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + SimpleTypeOrNamespaceReference o = other as SimpleTypeOrNamespaceReference; + return o != null && this.identifier == o.identifier + && this.typeArguments == o.typeArguments && this.lookupMode == o.lookupMode; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/TypeOrNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/TypeOrNamespaceReference.cs new file mode 100644 index 000000000..c4d0d5143 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/TypeOrNamespaceReference.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Represents a reference which could point to a type or namespace. + /// + [Serializable] + public abstract class TypeOrNamespaceReference : ITypeReference + { + /// + /// Resolves the reference and returns the ResolveResult. + /// + public abstract ResolveResult Resolve(CSharpResolver resolver); + + /// + /// Returns the type that is referenced; or an UnknownType if the type isn't found. + /// + public abstract IType ResolveType(CSharpResolver resolver); + + /// + /// Returns the namespace that is referenced; or null if no such namespace is found. + /// + public INamespace ResolveNamespace(CSharpResolver resolver) + { + NamespaceResolveResult nrr = Resolve(resolver) as NamespaceResolveResult; + return nrr != null ? nrr.Namespace : null; + } + + IType ITypeReference.Resolve(ITypeResolveContext context) + { + // Strictly speaking, we might have to resolve the type in a nested compilation, similar + // to what we're doing with ConstantExpression. + // However, in almost all cases this will work correctly - if the resulting type is only available in the + // nested compilation and not in this, we wouldn't be able to map it anyways. + var ctx = context as CSharpTypeResolveContext; + if (ctx == null) { + ctx = new CSharpTypeResolveContext(context.CurrentAssembly ?? context.Compilation.MainAssembly, null, context.CurrentTypeDefinition, context.CurrentMember); + } + return ResolveType(new CSharpResolver(ctx)); + + // A potential issue might be this scenario: + + // Assembly 1: + // class A { public class Nested {} } + + // Assembly 2: (references asm 1) + // class B : A {} + + // Assembly 3: (references asm 1 and 2) + // class C { public B.Nested Field; } + + // Assembly 4: (references asm 1 and 3, but not 2): + // uses C.Field; + + // Here we would not be able to resolve 'B.Nested' in the compilation of assembly 4, as type B is missing there. + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs new file mode 100644 index 000000000..6050a1c69 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs @@ -0,0 +1,174 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.TypeSystem +{ + /// + /// Represents a scope that contains "using" statements. + /// This is either the file itself, or a namespace declaration. + /// + [Serializable] + public class UsingScope : AbstractFreezable + { + readonly UsingScope parent; + DomRegion region; + string shortName = ""; + IList usings; + IList> usingAliases; + IList externAliases; + + protected override void FreezeInternal() + { + usings = FreezableHelper.FreezeList(usings); + usingAliases = FreezableHelper.FreezeList(usingAliases); + externAliases = FreezableHelper.FreezeList(externAliases); + + // In current model (no child scopes), it makes sense to freeze the parent as well + // to ensure the whole lookup chain is immutable. + if (parent != null) + parent.Freeze(); + + base.FreezeInternal(); + } + + /// + /// Creates a new root using scope. + /// + public UsingScope() + { + } + + /// + /// Creates a new nested using scope. + /// + /// The parent using scope. + /// The short namespace name. + public UsingScope(UsingScope parent, string shortName) + { + if (parent == null) + throw new ArgumentNullException("parent"); + if (shortName == null) + throw new ArgumentNullException("shortName"); + this.parent = parent; + this.shortName = shortName; + } + + public UsingScope Parent { + get { return parent; } + } + + public DomRegion Region { + get { return region; } + set { + FreezableHelper.ThrowIfFrozen(this); + region = value; + } + } + + public string ShortNamespaceName { + get { + return shortName; + } + } + + public string NamespaceName { + get { + if (parent != null) + return NamespaceDeclaration.BuildQualifiedName(parent.NamespaceName, shortName); + else + return shortName; + } +// set { +// if (value == null) +// throw new ArgumentNullException("NamespaceName"); +// FreezableHelper.ThrowIfFrozen(this); +// namespaceName = value; +// } + } + + public IList Usings { + get { + if (usings == null) + usings = new List(); + return usings; + } + } + + public IList> UsingAliases { + get { + if (usingAliases == null) + usingAliases = new List>(); + return usingAliases; + } + } + + public IList ExternAliases { + get { + if (externAliases == null) + externAliases = new List(); + return externAliases; + } + } + +// public IList ChildScopes { +// get { +// if (childScopes == null) +// childScopes = new List(); +// return childScopes; +// } +// } + + /// + /// Gets whether this using scope has an alias (either using or extern) + /// with the specified name. + /// + public bool HasAlias(string identifier) + { + if (usingAliases != null) { + foreach (var pair in usingAliases) { + if (pair.Key == identifier) + return true; + } + } + return externAliases != null && externAliases.Contains(identifier); + } + + /// + /// Resolves the namespace represented by this using scope. + /// + public ResolvedUsingScope Resolve(ICompilation compilation) + { + CacheManager cache = compilation.CacheManager; + ResolvedUsingScope resolved = cache.GetShared(this) as ResolvedUsingScope; + if (resolved == null) { + var csContext = new CSharpTypeResolveContext(compilation.MainAssembly, parent != null ? parent.Resolve(compilation) : null); + resolved = (ResolvedUsingScope)cache.GetOrAddShared(this, new ResolvedUsingScope(csContext, this)); + } + return resolved; + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 81c7f7e00..bc801d645 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -46,6 +46,7 @@ false TRACE;DEBUG;STEP ICSharpCode.Decompiler.ruleset + true false @@ -66,8 +67,178 @@ + + + + + + + + + + + + + + + + + + + ArrayInitializerExpression.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -202,10 +373,6 @@ {2B8F4F83-C2B3-4E84-A27B-8DEE1BE0E006} ICSharpCode.NRefactory.Cecil - - {53dca265-3c3c-42f9-b647-f72ba678122b} - ICSharpCode.NRefactory.CSharp - {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index a2d02bb44..772543e83 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -113,10 +113,6 @@ {d68133bd-1e63-496e-9ede-4fbdbf77b486} Mono.Cecil - - {53dca265-3c3c-42f9-b647-f72ba678122b} - ICSharpCode.NRefactory.CSharp - {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory diff --git a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs new file mode 100644 index 000000000..4abc463bc --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs @@ -0,0 +1,1811 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using ICSharpCode.NRefactory.Documentation; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; +using Mono.Cecil; + + +namespace ICSharpCode.NRefactory.TypeSystem +{ + using BlobReader = ICSharpCode.NRefactory.TypeSystem.Implementation.BlobReader; + + /// + /// Allows loading an IProjectContent from an already compiled assembly. + /// + /// Instance methods are not thread-safe; you need to create multiple instances of CecilLoader + /// if you want to load multiple project contents in parallel. + public sealed class CecilLoader : AssemblyLoader + { + /// + /// Version number of the cecil loader. + /// Should be incremented when fixing bugs in the cecil loader so that project contents cached on disk + /// (which might be incorrect due to the bug) are re-created. + /// + const int cecilLoaderVersion = 1; + + #region Options + // Most options are defined in the AssemblyLoader base class + + /// + /// Specifies whether to use lazy loading. The default is false. + /// If this property is set to true, the CecilLoader will not copy all the relevant information + /// out of the Cecil object model, but will maintain references to the Cecil objects. + /// This speeds up the loading process and avoids loading unnecessary information, but it causes + /// the Cecil objects to stay in memory (which can significantly increase memory usage). + /// It also prevents serialization of the Cecil-loaded type system. + /// + /// + /// Because the type system can be used on multiple threads, but Cecil is not + /// thread-safe for concurrent read access, the CecilLoader will lock on the instance + /// for every delay-loading operation. + /// If you access the Cecil objects directly in your application, you may need to take the same lock. + /// + public bool LazyLoad { get; set; } + + /// + /// This delegate gets executed whenever an entity was loaded. + /// + /// + /// This callback may be to build a dictionary that maps between + /// entities and cecil objects. + /// Warning: if delay-loading is used and the type system is accessed by multiple threads, + /// the callback may be invoked concurrently on multiple threads. + /// + [CLSCompliant(false)] + public Action OnEntityLoaded { get; set; } + + /// + /// Gets a value indicating whether this instance stores references to the cecil objects. + /// + /// + /// true if this instance has references to the cecil objects; otherwise, false. + /// + public bool HasCecilReferences { get { return typeSystemTranslationTable != null; } } + + bool shortenInterfaceImplNames = true; + + /// + /// Specifies whether method names of explicit interface-implementations should be shortened. + /// + /// This is important when working with parser-initialized type-systems in order to be consistent. + public bool ShortenInterfaceImplNames { + get { + return shortenInterfaceImplNames; + } + set { + shortenInterfaceImplNames = value; + } + } + #endregion + + ModuleDefinition currentModule; + CecilUnresolvedAssembly currentAssembly; + + /// + /// Initializes a new instance of the class. + /// + public CecilLoader() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// If true references to the cecil objects are hold. In this case the cecil loader can do a type system -> cecil mapping. + /// + [Obsolete("The built-in entity<->cecil mapping is obsolete. Use the OnEntityLoaded callback instead!")] + public CecilLoader(bool createCecilReferences) : this() + { + if (createCecilReferences) + typeSystemTranslationTable = new Dictionary (); + } + + /// + /// Creates a nested CecilLoader for lazy-loading. + /// + private CecilLoader(CecilLoader loader) + { + // use a shared typeSystemTranslationTable + this.typeSystemTranslationTable = loader.typeSystemTranslationTable; + this.IncludeInternalMembers = loader.IncludeInternalMembers; + this.LazyLoad = loader.LazyLoad; + this.OnEntityLoaded = loader.OnEntityLoaded; + this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames; + this.currentModule = loader.currentModule; + this.currentAssembly = loader.currentAssembly; + // don't use interning - the interning provider is most likely not thread-safe + this.interningProvider = InterningProvider.Dummy; + // don't use cancellation for delay-loaded members + } + + #region Load From AssemblyDefinition + /// + /// Loads the assembly definition into a project content. + /// + /// Unresolved type system representing the assembly + [CLSCompliant(false)] + public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition) + { + if (assemblyDefinition == null) + throw new ArgumentNullException("assemblyDefinition"); + return LoadModule(assemblyDefinition.MainModule); + } + + /// + /// Loads the module definition into a project content. + /// + /// Unresolved type system representing the assembly + [CLSCompliant(false)] + public IUnresolvedAssembly LoadModule(ModuleDefinition moduleDefinition) + { + if (moduleDefinition == null) + throw new ArgumentNullException("moduleDefinition"); + + this.currentModule = moduleDefinition; + + // Read assembly and module attributes + IList assemblyAttributes = new List(); + IList moduleAttributes = new List(); + AssemblyDefinition assemblyDefinition = moduleDefinition.Assembly; + if (assemblyDefinition != null) { + AddAttributes(assemblyDefinition, assemblyAttributes); + } + AddAttributes(moduleDefinition, moduleAttributes); + + assemblyAttributes = interningProvider.InternList(assemblyAttributes); + moduleAttributes = interningProvider.InternList(moduleAttributes); + + this.currentAssembly = new CecilUnresolvedAssembly(assemblyDefinition != null ? assemblyDefinition.Name.FullName : moduleDefinition.Name, this.DocumentationProvider); + currentAssembly.Location = moduleDefinition.FullyQualifiedName; + currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes); + currentAssembly.ModuleAttributes.AddRange(assemblyAttributes); + + // Register type forwarders: + foreach (ExportedType type in moduleDefinition.ExportedTypes) { + if (type.IsForwarder) { + int typeParameterCount; + string ns = type.Namespace; + string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); + ns = interningProvider.Intern(ns); + name = interningProvider.Intern(name); + var typeRef = new GetClassTypeReference(GetAssemblyReference(type.Scope), ns, name, typeParameterCount); + typeRef = interningProvider.Intern(typeRef); + var key = new TopLevelTypeName(ns, name, typeParameterCount); + currentAssembly.AddTypeForwarder(key, typeRef); + } + } + + // Create and register all types: + CecilLoader cecilLoaderCloneForLazyLoading = LazyLoad ? new CecilLoader(this) : null; + List cecilTypeDefs = new List(); + List typeDefs = new List(); + foreach (TypeDefinition td in moduleDefinition.Types) { + this.CancellationToken.ThrowIfCancellationRequested(); + if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) { + string name = td.Name; + if (name.Length == 0) + continue; + + if (this.LazyLoad) { + var t = new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td); + currentAssembly.AddTypeDefinition(t); + RegisterCecilObject(t, td); + } else { + var t = CreateTopLevelTypeDefinition(td); + cecilTypeDefs.Add(td); + typeDefs.Add(t); + currentAssembly.AddTypeDefinition(t); + // The registration will happen after the members are initialized + } + } + } + // Initialize the type's members: + for (int i = 0; i < typeDefs.Count; i++) { + InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]); + } + + AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition); + // Freezing the assembly here is important: + // otherwise it will be frozen when a compilation is first created + // from it. But freezing has the effect of changing some collection instances + // (to ReadOnlyCollection). This hidden mutation was causing a crash + // when the FastSerializer was saving the assembly at the same time as + // the first compilation was created from it. + // By freezing the assembly now, we ensure it is usable on multiple + // threads without issues. + currentAssembly.Freeze(); + + var result = this.currentAssembly; + this.currentAssembly = null; + this.currentModule = null; + return result; + } + + /// + /// Sets the current module. + /// This causes ReadTypeReference() to use for references + /// in that module. + /// + [CLSCompliant(false)] + public void SetCurrentModule(ModuleDefinition module) + { + this.currentModule = module; + } + + /// + /// Loads a type from Cecil. + /// + /// The Cecil TypeDefinition. + /// ITypeDefinition representing the Cecil type. + [CLSCompliant(false)] + public IUnresolvedTypeDefinition LoadType(TypeDefinition typeDefinition) + { + if (typeDefinition == null) + throw new ArgumentNullException("typeDefinition"); + var td = CreateTopLevelTypeDefinition(typeDefinition); + InitTypeDefinition(typeDefinition, td); + return td; + } + #endregion + + #region IUnresolvedAssembly implementation + [Serializable, FastSerializerVersion(cecilLoaderVersion)] + sealed class CecilUnresolvedAssembly : DefaultUnresolvedAssembly, IDocumentationProvider + { + readonly IDocumentationProvider documentationProvider; + + public CecilUnresolvedAssembly(string fullAssemblyName, IDocumentationProvider documentationProvider) + : base(fullAssemblyName) + { + this.documentationProvider = documentationProvider; + } + + DocumentationComment IDocumentationProvider.GetDocumentation(IEntity entity) + { + if (documentationProvider != null) + return documentationProvider.GetDocumentation(entity); + else + return null; + } + } + #endregion + + #region Load Assembly From Disk + public override IUnresolvedAssembly LoadAssemblyFile(string fileName) + { + if (fileName == null) + throw new ArgumentNullException("fileName"); + var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }; + ModuleDefinition module = ModuleDefinition.ReadModule(fileName, param); + return LoadModule(module); + } + + // used to prevent Cecil from loading referenced assemblies + sealed class DummyAssemblyResolver : IAssemblyResolver + { + public AssemblyDefinition Resolve(AssemblyNameReference name) + { + return null; + } + + public AssemblyDefinition Resolve(string fullName) + { + return null; + } + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + return null; + } + + public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) + { + return null; + } + } + #endregion + + #region Read Type Reference + /// + /// Reads a type reference. + /// + /// The Cecil type reference that should be converted into + /// a type system type reference. + /// Attributes associated with the Cecil type reference. + /// This is used to support the 'dynamic' type. + [CLSCompliant(false)] + public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null) + { + int typeIndex = 0; + return CreateType(type, typeAttributes, ref typeIndex); + } + + ITypeReference CreateType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex) + { + while (type is OptionalModifierType || type is RequiredModifierType) { + type = ((TypeSpecification)type).ElementType; + } + if (type == null) { + return SpecialType.UnknownType; + } + + if (type is Mono.Cecil.ByReferenceType) { + typeIndex++; + return interningProvider.Intern( + new ByReferenceTypeReference( + CreateType( + (type as Mono.Cecil.ByReferenceType).ElementType, + typeAttributes, ref typeIndex))); + } else if (type is Mono.Cecil.PointerType) { + typeIndex++; + return interningProvider.Intern( + new PointerTypeReference( + CreateType( + (type as Mono.Cecil.PointerType).ElementType, + typeAttributes, ref typeIndex))); + } else if (type is Mono.Cecil.ArrayType) { + typeIndex++; + return interningProvider.Intern( + new ArrayTypeReference( + CreateType( + (type as Mono.Cecil.ArrayType).ElementType, + typeAttributes, ref typeIndex), + (type as Mono.Cecil.ArrayType).Rank)); + } else if (type is GenericInstanceType) { + GenericInstanceType gType = (GenericInstanceType)type; + ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref typeIndex); + ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count]; + for (int i = 0; i < para.Length; ++i) { + typeIndex++; + para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref typeIndex); + } + return interningProvider.Intern(new ParameterizedTypeReference(baseType, para)); + } else if (type is GenericParameter) { + GenericParameter typeGP = (GenericParameter)type; + return TypeParameterReference.Create(typeGP.Owner is MethodReference ? SymbolKind.Method : SymbolKind.TypeDefinition, typeGP.Position); + } else if (type.IsNested) { + ITypeReference typeRef = CreateType(type.DeclaringType, typeAttributes, ref typeIndex); + int partTypeParameterCount; + string namepart = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out partTypeParameterCount); + namepart = interningProvider.Intern(namepart); + return interningProvider.Intern(new NestedTypeReference(typeRef, namepart, partTypeParameterCount)); + } else { + string ns = interningProvider.Intern(type.Namespace ?? string.Empty); + string name = type.Name; + if (name == null) + throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString()); + + if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) { + return SpecialType.Dynamic; + } else { + int typeParameterCount; + name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount); + name = interningProvider.Intern(name); + if (currentAssembly != null) { + IUnresolvedTypeDefinition c = currentAssembly.GetTypeDefinition(ns, name, typeParameterCount); + if (c != null) + return c; + } + return interningProvider.Intern(new GetClassTypeReference(GetAssemblyReference(type.Scope), ns, name, typeParameterCount)); + } + } + } + + IAssemblyReference GetAssemblyReference(IMetadataScope scope) + { + if (scope == null || scope == currentModule) + return DefaultAssemblyReference.CurrentAssembly; + else + return interningProvider.Intern(new DefaultAssemblyReference(scope.Name)); + } + + static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) + { + if (attributeProvider == null || !attributeProvider.HasCustomAttributes) + return false; + foreach (CustomAttribute a in attributeProvider.CustomAttributes) { + TypeReference type = a.AttributeType; + if (type.Name == "DynamicAttribute" && type.Namespace == "System.Runtime.CompilerServices") { + if (a.ConstructorArguments.Count == 1) { + CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; + if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) + return (bool)values[typeIndex].Value; + } + return true; + } + } + return false; + } + #endregion + + #region Read Attributes + #region Assembly Attributes + static readonly ITypeReference assemblyVersionAttributeTypeRef = typeof(System.Reflection.AssemblyVersionAttribute).ToTypeReference(); + + void AddAttributes(AssemblyDefinition assembly, IList outputList) + { + if (assembly.HasCustomAttributes) { + AddCustomAttributes(assembly.CustomAttributes, outputList); + } + if (assembly.HasSecurityDeclarations) { + AddSecurityAttributes(assembly.SecurityDeclarations, outputList); + } + + // AssemblyVersionAttribute + if (assembly.Name.Version != null) { + var assemblyVersion = new DefaultUnresolvedAttribute(assemblyVersionAttributeTypeRef, new[] { KnownTypeReference.String }); + assemblyVersion.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, assembly.Name.Version.ToString())); + outputList.Add(interningProvider.Intern(assemblyVersion)); + } + } + + IConstantValue CreateSimpleConstantValue(ITypeReference type, object value) + { + return interningProvider.Intern(new SimpleConstantValue(type, interningProvider.InternValue(value))); + } + #endregion + + #region Module Attributes + void AddAttributes(ModuleDefinition module, IList outputList) + { + if (module.HasCustomAttributes) { + AddCustomAttributes(module.CustomAttributes, outputList); + } + } + #endregion + + #region Parameter Attributes + static readonly IUnresolvedAttribute inAttribute = new DefaultUnresolvedAttribute(typeof(InAttribute).ToTypeReference()); + static readonly IUnresolvedAttribute outAttribute = new DefaultUnresolvedAttribute(typeof(OutAttribute).ToTypeReference()); + + void AddAttributes(ParameterDefinition parameter, DefaultUnresolvedParameter targetParameter) + { + if (!targetParameter.IsOut) { + if (parameter.IsIn) + targetParameter.Attributes.Add(inAttribute); + if (parameter.IsOut) + targetParameter.Attributes.Add(outAttribute); + } + if (parameter.HasCustomAttributes) { + AddCustomAttributes(parameter.CustomAttributes, targetParameter.Attributes); + } + if (parameter.HasMarshalInfo) { + targetParameter.Attributes.Add(ConvertMarshalInfo(parameter.MarshalInfo)); + } + } + #endregion + + #region Method Attributes + static readonly ITypeReference dllImportAttributeTypeRef = typeof(DllImportAttribute).ToTypeReference(); + static readonly SimpleConstantValue trueValue = new SimpleConstantValue(KnownTypeReference.Boolean, true); + static readonly SimpleConstantValue falseValue = new SimpleConstantValue(KnownTypeReference.Boolean, false); + static readonly ITypeReference callingConventionTypeRef = typeof(CallingConvention).ToTypeReference(); + static readonly IUnresolvedAttribute preserveSigAttribute = new DefaultUnresolvedAttribute(typeof(PreserveSigAttribute).ToTypeReference()); + static readonly ITypeReference methodImplAttributeTypeRef = typeof(MethodImplAttribute).ToTypeReference(); + static readonly ITypeReference methodImplOptionsTypeRef = typeof(MethodImplOptions).ToTypeReference(); + + static bool HasAnyAttributes(MethodDefinition methodDefinition) + { + if (methodDefinition.HasPInvokeInfo) + return true; + if ((methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask) != 0) + return true; + if (methodDefinition.MethodReturnType.HasFieldMarshal) + return true; + return methodDefinition.HasCustomAttributes || methodDefinition.MethodReturnType.HasCustomAttributes; + } + + void AddAttributes(MethodDefinition methodDefinition, IList attributes, IList returnTypeAttributes) + { + MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; + + #region DllImportAttribute + if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) { + PInvokeInfo info = methodDefinition.PInvokeInfo; + var dllImport = new DefaultUnresolvedAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String }); + dllImport.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, info.Module.Name)); + + if (info.IsBestFitDisabled) + dllImport.AddNamedFieldArgument("BestFitMapping", falseValue); + if (info.IsBestFitEnabled) + dllImport.AddNamedFieldArgument("BestFitMapping", trueValue); + + CallingConvention callingConvention; + switch (info.Attributes & PInvokeAttributes.CallConvMask) { + case (PInvokeAttributes)0: + Debug.WriteLine ("P/Invoke calling convention not set on:" + methodDefinition.FullName); + callingConvention = 0; + break; + case PInvokeAttributes.CallConvCdecl: + callingConvention = CallingConvention.Cdecl; + break; + case PInvokeAttributes.CallConvFastcall: + callingConvention = CallingConvention.FastCall; + break; + case PInvokeAttributes.CallConvStdCall: + callingConvention = CallingConvention.StdCall; + break; + case PInvokeAttributes.CallConvThiscall: + callingConvention = CallingConvention.ThisCall; + break; + case PInvokeAttributes.CallConvWinapi: + callingConvention = CallingConvention.Winapi; + break; + default: + throw new NotSupportedException("unknown calling convention"); + } + if (callingConvention != CallingConvention.Winapi) + dllImport.AddNamedFieldArgument("CallingConvention", CreateSimpleConstantValue(callingConventionTypeRef, (int)callingConvention)); + + CharSet charSet = CharSet.None; + switch (info.Attributes & PInvokeAttributes.CharSetMask) { + case PInvokeAttributes.CharSetAnsi: + charSet = CharSet.Ansi; + break; + case PInvokeAttributes.CharSetAuto: + charSet = CharSet.Auto; + break; + case PInvokeAttributes.CharSetUnicode: + charSet = CharSet.Unicode; + break; + } + if (charSet != CharSet.None) + dllImport.AddNamedFieldArgument("CharSet", CreateSimpleConstantValue(charSetTypeRef, (int)charSet)); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name) + dllImport.AddNamedFieldArgument("EntryPoint", CreateSimpleConstantValue(KnownTypeReference.String, info.EntryPoint)); + + if (info.IsNoMangle) + dllImport.AddNamedFieldArgument("ExactSpelling", trueValue); + + if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) + implAttributes &= ~MethodImplAttributes.PreserveSig; + else + dllImport.AddNamedFieldArgument("PreserveSig", falseValue); + + if (info.SupportsLastError) + dllImport.AddNamedFieldArgument("SetLastError", trueValue); + + if (info.IsThrowOnUnmappableCharDisabled) + dllImport.AddNamedFieldArgument("ThrowOnUnmappableChar", falseValue); + if (info.IsThrowOnUnmappableCharEnabled) + dllImport.AddNamedFieldArgument("ThrowOnUnmappableChar", trueValue); + + attributes.Add(interningProvider.Intern(dllImport)); + } + #endregion + + #region PreserveSigAttribute + if (implAttributes == MethodImplAttributes.PreserveSig) { + attributes.Add(preserveSigAttribute); + implAttributes = 0; + } + #endregion + + #region MethodImplAttribute + if (implAttributes != 0) { + var methodImpl = new DefaultUnresolvedAttribute(methodImplAttributeTypeRef, new[] { methodImplOptionsTypeRef }); + methodImpl.PositionalArguments.Add(CreateSimpleConstantValue(methodImplOptionsTypeRef, (int)implAttributes)); + attributes.Add(interningProvider.Intern(methodImpl)); + } + #endregion + + if (methodDefinition.HasCustomAttributes) { + AddCustomAttributes(methodDefinition.CustomAttributes, attributes); + } + if (methodDefinition.HasSecurityDeclarations) { + AddSecurityAttributes(methodDefinition.SecurityDeclarations, attributes); + } + if (methodDefinition.MethodReturnType.HasMarshalInfo) { + returnTypeAttributes.Add(ConvertMarshalInfo(methodDefinition.MethodReturnType.MarshalInfo)); + } + if (methodDefinition.MethodReturnType.HasCustomAttributes) { + AddCustomAttributes(methodDefinition.MethodReturnType.CustomAttributes, returnTypeAttributes); + } + } + #endregion + + #region Type Attributes + static readonly DefaultUnresolvedAttribute serializableAttribute = new DefaultUnresolvedAttribute(typeof(SerializableAttribute).ToTypeReference()); + static readonly DefaultUnresolvedAttribute comImportAttribute = new DefaultUnresolvedAttribute(typeof(ComImportAttribute).ToTypeReference()); + static readonly ITypeReference structLayoutAttributeTypeRef = typeof(StructLayoutAttribute).ToTypeReference(); + static readonly ITypeReference layoutKindTypeRef = typeof(LayoutKind).ToTypeReference(); + static readonly ITypeReference charSetTypeRef = typeof(CharSet).ToTypeReference(); + + void AddAttributes(TypeDefinition typeDefinition, IUnresolvedTypeDefinition targetEntity) + { + // SerializableAttribute + if (typeDefinition.IsSerializable) + targetEntity.Attributes.Add(serializableAttribute); + + // ComImportAttribute + if (typeDefinition.IsImport) + targetEntity.Attributes.Add(comImportAttribute); + + #region StructLayoutAttribute + LayoutKind layoutKind = LayoutKind.Auto; + switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) { + case TypeAttributes.SequentialLayout: + layoutKind = LayoutKind.Sequential; + break; + case TypeAttributes.ExplicitLayout: + layoutKind = LayoutKind.Explicit; + break; + } + CharSet charSet = CharSet.None; + switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) { + case TypeAttributes.AnsiClass: + charSet = CharSet.Ansi; + break; + case TypeAttributes.AutoClass: + charSet = CharSet.Auto; + break; + case TypeAttributes.UnicodeClass: + charSet = CharSet.Unicode; + break; + } + LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto; + if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { + DefaultUnresolvedAttribute structLayout = new DefaultUnresolvedAttribute(structLayoutAttributeTypeRef, new[] { layoutKindTypeRef }); + structLayout.PositionalArguments.Add(CreateSimpleConstantValue(layoutKindTypeRef, (int)layoutKind)); + if (charSet != CharSet.Ansi) { + structLayout.AddNamedFieldArgument("CharSet", CreateSimpleConstantValue(charSetTypeRef, (int)charSet)); + } + if (typeDefinition.PackingSize > 0) { + structLayout.AddNamedFieldArgument("Pack", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)typeDefinition.PackingSize)); + } + if (typeDefinition.ClassSize > 0) { + structLayout.AddNamedFieldArgument("Size", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)typeDefinition.ClassSize)); + } + targetEntity.Attributes.Add(interningProvider.Intern(structLayout)); + } + #endregion + + if (typeDefinition.HasCustomAttributes) { + AddCustomAttributes(typeDefinition.CustomAttributes, targetEntity.Attributes); + } + if (typeDefinition.HasSecurityDeclarations) { + AddSecurityAttributes(typeDefinition.SecurityDeclarations, targetEntity.Attributes); + } + } + #endregion + + #region Field Attributes + static readonly ITypeReference fieldOffsetAttributeTypeRef = typeof(FieldOffsetAttribute).ToTypeReference(); + static readonly IUnresolvedAttribute nonSerializedAttribute = new DefaultUnresolvedAttribute(typeof(NonSerializedAttribute).ToTypeReference()); + + void AddAttributes(FieldDefinition fieldDefinition, IUnresolvedEntity targetEntity) + { + // FieldOffsetAttribute + if (fieldDefinition.HasLayoutInfo) { + DefaultUnresolvedAttribute fieldOffset = new DefaultUnresolvedAttribute(fieldOffsetAttributeTypeRef, new[] { KnownTypeReference.Int32 }); + fieldOffset.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.Int32, fieldDefinition.Offset)); + targetEntity.Attributes.Add(interningProvider.Intern(fieldOffset)); + } + + // NonSerializedAttribute + if (fieldDefinition.IsNotSerialized) { + targetEntity.Attributes.Add(nonSerializedAttribute); + } + + if (fieldDefinition.HasMarshalInfo) { + targetEntity.Attributes.Add(ConvertMarshalInfo(fieldDefinition.MarshalInfo)); + } + + if (fieldDefinition.HasCustomAttributes) { + AddCustomAttributes(fieldDefinition.CustomAttributes, targetEntity.Attributes); + } + } + #endregion + + #region Event Attributes + void AddAttributes(EventDefinition eventDefinition, IUnresolvedEntity targetEntity) + { + if (eventDefinition.HasCustomAttributes) { + AddCustomAttributes(eventDefinition.CustomAttributes, targetEntity.Attributes); + } + } + #endregion + + #region Property Attributes + void AddAttributes(PropertyDefinition propertyDefinition, IUnresolvedEntity targetEntity) + { + if (propertyDefinition.HasCustomAttributes) { + AddCustomAttributes(propertyDefinition.CustomAttributes, targetEntity.Attributes); + } + } + #endregion + + #region Type Parameter Attributes + void AddAttributes(GenericParameter genericParameter, IUnresolvedTypeParameter targetTP) + { + if (genericParameter.HasCustomAttributes) { + AddCustomAttributes(genericParameter.CustomAttributes, targetTP.Attributes); + } + } + #endregion + + #region MarshalAsAttribute (ConvertMarshalInfo) + static readonly ITypeReference marshalAsAttributeTypeRef = typeof(MarshalAsAttribute).ToTypeReference(); + static readonly ITypeReference unmanagedTypeTypeRef = typeof(UnmanagedType).ToTypeReference(); + + IUnresolvedAttribute ConvertMarshalInfo(MarshalInfo marshalInfo) + { + DefaultUnresolvedAttribute attr = new DefaultUnresolvedAttribute(marshalAsAttributeTypeRef, new[] { unmanagedTypeTypeRef }); + attr.PositionalArguments.Add(CreateSimpleConstantValue(unmanagedTypeTypeRef, (int)marshalInfo.NativeType)); + + FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; + if (fami != null) { + attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)fami.Size)); + if (fami.ElementType != NativeType.None) + attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, (int)fami.ElementType)); + } + SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; + if (sami != null && sami.ElementType != VariantType.None) { + attr.AddNamedFieldArgument("SafeArraySubType", CreateSimpleConstantValue(typeof(VarEnum).ToTypeReference(), (int)sami.ElementType)); + } + ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo; + if (ami != null) { + if (ami.ElementType != NativeType.Max) + attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, (int)ami.ElementType)); + if (ami.Size >= 0) + attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)ami.Size)); + if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0) + attr.AddNamedFieldArgument("SizeParamIndex", CreateSimpleConstantValue(KnownTypeReference.Int16, (short)ami.SizeParameterIndex)); + } + CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; + if (cmi != null) { + if (cmi.ManagedType != null) + attr.AddNamedFieldArgument("MarshalType", CreateSimpleConstantValue(KnownTypeReference.String, cmi.ManagedType.FullName)); + if (!string.IsNullOrEmpty(cmi.Cookie)) + attr.AddNamedFieldArgument("MarshalCookie", CreateSimpleConstantValue(KnownTypeReference.String, cmi.Cookie)); + } + FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo; + if (fssmi != null) { + attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)fssmi.Size)); + } + + return InterningProvider.Intern(attr); + } + #endregion + + #region Custom Attributes (ReadAttribute) + void AddCustomAttributes(Mono.Collections.Generic.Collection attributes, IList targetCollection) + { + foreach (var cecilAttribute in attributes) { + TypeReference type = cecilAttribute.AttributeType; + if (type.Namespace == "System.Runtime.CompilerServices") { + if (type.Name == "DynamicAttribute" || type.Name == "ExtensionAttribute" || type.Name == "DecimalConstantAttribute") + continue; + } else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") { + continue; + } + targetCollection.Add(ReadAttribute(cecilAttribute)); + } + } + + [CLSCompliant(false)] + public IUnresolvedAttribute ReadAttribute(CustomAttribute attribute) + { + if (attribute == null) + throw new ArgumentNullException("attribute"); + MethodReference ctor = attribute.Constructor; + ITypeReference attributeType = ReadTypeReference(attribute.AttributeType); + IList ctorParameterTypes = EmptyList.Instance; + if (ctor.HasParameters) { + ctorParameterTypes = new ITypeReference[ctor.Parameters.Count]; + for (int i = 0; i < ctorParameterTypes.Count; i++) { + ctorParameterTypes[i] = ReadTypeReference(ctor.Parameters[i].ParameterType); + } + ctorParameterTypes = interningProvider.InternList(ctorParameterTypes); + } + return interningProvider.Intern(new UnresolvedAttributeBlob(attributeType, ctorParameterTypes, attribute.GetBlob())); + } + #endregion + + #region Security Attributes + /// + /// Reads a security declaration. + /// + [CLSCompliant(false)] + public IList ReadSecurityDeclaration(SecurityDeclaration secDecl) + { + if (secDecl == null) + throw new ArgumentNullException("secDecl"); + var result = new List(); + AddSecurityAttributes(secDecl, result); + return result; + } + + void AddSecurityAttributes(Mono.Collections.Generic.Collection securityDeclarations, IList targetCollection) + { + foreach (var secDecl in securityDeclarations) { + AddSecurityAttributes(secDecl, targetCollection); + } + } + + void AddSecurityAttributes(SecurityDeclaration secDecl, IList targetCollection) + { + byte[] blob; + try { + blob = secDecl.GetBlob(); + } catch (NotSupportedException) { + return; // https://github.com/icsharpcode/SharpDevelop/issues/284 + } + var blobSecDecl = new UnresolvedSecurityDeclarationBlob((int)secDecl.Action, blob); + targetCollection.AddRange(blobSecDecl.UnresolvedAttributes); + } + #endregion + #endregion + + #region Read Type Definition + DefaultUnresolvedTypeDefinition CreateTopLevelTypeDefinition(TypeDefinition typeDefinition) + { + string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name); + var td = new DefaultUnresolvedTypeDefinition(typeDefinition.Namespace, name); + if (typeDefinition.HasGenericParameters) + InitTypeParameters(typeDefinition, td.TypeParameters); + return td; + } + + static void InitTypeParameters(TypeDefinition typeDefinition, IList typeParameters) + { + // Type parameters are initialized within the constructor so that the class can be put into the type storage + // before the rest of the initialization runs - this allows it to be available for early binding as soon as possible. + for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) { + if (typeDefinition.GenericParameters[i].Position != i) + throw new InvalidOperationException("g.Position != i"); + typeParameters.Add(new DefaultUnresolvedTypeParameter( + SymbolKind.TypeDefinition, i, typeDefinition.GenericParameters[i].Name)); + } + } + + void InitTypeParameterConstraints(TypeDefinition typeDefinition, IList typeParameters) + { + for (int i = 0; i < typeParameters.Count; i++) { + var tp = (DefaultUnresolvedTypeParameter)typeParameters[i]; + AddConstraints(tp, typeDefinition.GenericParameters[i]); + AddAttributes(typeDefinition.GenericParameters[i], tp); + tp.ApplyInterningProvider(interningProvider); + } + } + + void InitTypeDefinition(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) + { + td.Kind = GetTypeKind(typeDefinition); + InitTypeModifiers(typeDefinition, td); + InitTypeParameterConstraints(typeDefinition, td.TypeParameters); + + // nested types can be initialized only after generic parameters were created + InitNestedTypes(typeDefinition, td, td.NestedTypes); + AddAttributes(typeDefinition, td); + td.HasExtensionMethods = HasExtensionAttribute(typeDefinition); + + InitBaseTypes(typeDefinition, td.BaseTypes); + + td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum); + InitMembers(typeDefinition, td, td.Members); + td.ApplyInterningProvider(interningProvider); + td.Freeze(); + RegisterCecilObject(td, typeDefinition); + } + + void InitBaseTypes(TypeDefinition typeDefinition, IList baseTypes) + { + // set base classes + if (typeDefinition.IsEnum) { + foreach (FieldDefinition enumField in typeDefinition.Fields) { + if (!enumField.IsStatic) { + baseTypes.Add(ReadTypeReference(enumField.FieldType)); + break; + } + } + } else { + if (typeDefinition.BaseType != null) { + baseTypes.Add(ReadTypeReference(typeDefinition.BaseType)); + } + if (typeDefinition.HasInterfaces) { + foreach (TypeReference iface in typeDefinition.Interfaces) { + baseTypes.Add(ReadTypeReference(iface)); + } + } + } + } + + void InitNestedTypes(TypeDefinition typeDefinition, IUnresolvedTypeDefinition declaringTypeDefinition, IList nestedTypes) + { + if (!typeDefinition.HasNestedTypes) + return; + foreach (TypeDefinition nestedTypeDef in typeDefinition.NestedTypes) { + TypeAttributes visibility = nestedTypeDef.Attributes & TypeAttributes.VisibilityMask; + if (this.IncludeInternalMembers + || visibility == TypeAttributes.NestedPublic + || visibility == TypeAttributes.NestedFamily + || visibility == TypeAttributes.NestedFamORAssem) + { + string name = nestedTypeDef.Name; + int pos = name.LastIndexOf('/'); + if (pos > 0) + name = name.Substring(pos + 1); + name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); + var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name); + InitTypeParameters(nestedTypeDef, nestedType.TypeParameters); + nestedTypes.Add(nestedType); + InitTypeDefinition(nestedTypeDef, nestedType); + } + } + } + + static TypeKind GetTypeKind(TypeDefinition typeDefinition) + { + // set classtype + if (typeDefinition.IsInterface) { + return TypeKind.Interface; + } else if (typeDefinition.IsEnum) { + return TypeKind.Enum; + } else if (typeDefinition.IsValueType) { + return TypeKind.Struct; + } else if (IsDelegate(typeDefinition)) { + return TypeKind.Delegate; + } else if (IsModule(typeDefinition)) { + return TypeKind.Module; + } else { + return TypeKind.Class; + } + } + + static void InitTypeModifiers(TypeDefinition typeDefinition, AbstractUnresolvedEntity td) + { + td.IsSealed = typeDefinition.IsSealed; + td.IsAbstract = typeDefinition.IsAbstract; + switch (typeDefinition.Attributes & TypeAttributes.VisibilityMask) { + case TypeAttributes.NotPublic: + case TypeAttributes.NestedAssembly: + td.Accessibility = Accessibility.Internal; + break; + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + td.Accessibility = Accessibility.Public; + break; + case TypeAttributes.NestedPrivate: + td.Accessibility = Accessibility.Private; + break; + case TypeAttributes.NestedFamily: + td.Accessibility = Accessibility.Protected; + break; + case TypeAttributes.NestedFamANDAssem: + td.Accessibility = Accessibility.ProtectedAndInternal; + break; + case TypeAttributes.NestedFamORAssem: + td.Accessibility = Accessibility.ProtectedOrInternal; + break; + } + } + + static bool IsDelegate(TypeDefinition type) + { + if (type.BaseType != null && type.BaseType.Namespace == "System") { + if (type.BaseType.Name == "MulticastDelegate") + return true; + if (type.BaseType.Name == "Delegate" && type.Name != "MulticastDelegate") + return true; + } + return false; + } + + static bool IsModule(TypeDefinition type) + { + if (!type.HasCustomAttributes) + return false; + foreach (var att in type.CustomAttributes) { + if (att.AttributeType.FullName == "Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute" + || att.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGlobalScopeAttribute") + { + return true; + } + } + return false; + } + + void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList members) + { + if (typeDefinition.HasMethods) { + foreach (MethodDefinition method in typeDefinition.Methods) { + if (IsVisible(method.Attributes) && !IsAccessor(method.SemanticsAttributes)) { + SymbolKind type = SymbolKind.Method; + if (method.IsSpecialName) { + if (method.IsConstructor) + type = SymbolKind.Constructor; + else if (method.Name.StartsWith("op_", StringComparison.Ordinal)) + type = SymbolKind.Operator; + } + members.Add(ReadMethod(method, td, type)); + } + } + } + if (typeDefinition.HasFields) { + foreach (FieldDefinition field in typeDefinition.Fields) { + if (IsVisible(field.Attributes) && !field.IsSpecialName) { + members.Add(ReadField(field, td)); + } + } + } + if (typeDefinition.HasProperties) { + string defaultMemberName = null; + var defaultMemberAttribute = typeDefinition.CustomAttributes.FirstOrDefault( + a => a.AttributeType.FullName == typeof(System.Reflection.DefaultMemberAttribute).FullName); + if (defaultMemberAttribute != null && defaultMemberAttribute.ConstructorArguments.Count == 1) { + defaultMemberName = defaultMemberAttribute.ConstructorArguments[0].Value as string; + } + foreach (PropertyDefinition property in typeDefinition.Properties) { + bool getterVisible = property.GetMethod != null && IsVisible(property.GetMethod.Attributes); + bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes); + if (getterVisible || setterVisible) { + SymbolKind type = SymbolKind.Property; + if (property.HasParameters) { + // Try to detect indexer: + if (property.Name == defaultMemberName) { + type = SymbolKind.Indexer; // normal indexer + } else if (property.Name.EndsWith(".Item", StringComparison.Ordinal) && (property.GetMethod ?? property.SetMethod).HasOverrides) { + // explicit interface implementation of indexer + type = SymbolKind.Indexer; + // We can't really tell parameterized properties and indexers apart in this case without + // resolving the interface, so we rely on the "Item" naming convention instead. + } + } + members.Add(ReadProperty(property, td, type)); + } + } + } + if (typeDefinition.HasEvents) { + foreach (EventDefinition ev in typeDefinition.Events) { + if (ev.AddMethod != null && IsVisible(ev.AddMethod.Attributes)) { + members.Add(ReadEvent(ev, td)); + } + } + } + } + + static bool IsAccessor(MethodSemanticsAttributes semantics) + { + return !(semantics == MethodSemanticsAttributes.None || semantics == MethodSemanticsAttributes.Other); + } + #endregion + + #region Lazy-Loaded Type Definition + /// + /// Given an assembly that was created by the CecilLoader with lazy-loading enabled, + /// this method will eagerly load all classes, and free any references to the source Cecil objects. + /// + /// The intended usage pattern for this method is: + /// 1. use lazy-loading to improve the latency when new assemblies have to be loaded + /// 2. later, when the CPU is idle, call FinishLazyLoading() to free up the memory consumed by the Cecil objects + /// + public static void FinishLazyLoading(IUnresolvedAssembly assembly) + { + if (assembly == null) + throw new ArgumentNullException("assembly"); + foreach (var type in assembly.TopLevelTypeDefinitions) { + var lctd = type as LazyCecilTypeDefinition; + if (lctd != null) + lctd.InitAndReleaseReferences(); + } + } + + sealed class LazyCecilTypeDefinition : AbstractUnresolvedEntity, IUnresolvedTypeDefinition + { + // loader + cecilTypeDef, used for lazy-loading; and set to null after lazy loading is complete + CecilLoader loader; + TypeDefinition cecilTypeDef; + + readonly string namespaceName; + readonly TypeKind kind; + readonly IList typeParameters; + + // lazy-loaded fields + IList baseTypes; + IList nestedTypes; + IList members; + + public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition) + { + this.loader = loader; + this.cecilTypeDef = typeDefinition; + this.SymbolKind = SymbolKind.TypeDefinition; + this.namespaceName = typeDefinition.Namespace; + this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name); + var tps = new List(); + InitTypeParameters(typeDefinition, tps); + this.typeParameters = FreezableHelper.FreezeList(tps); + + this.kind = GetTypeKind(typeDefinition); + InitTypeModifiers(typeDefinition, this); + loader.InitTypeParameterConstraints(typeDefinition, typeParameters); + + loader.AddAttributes(typeDefinition, this); + flags[FlagHasExtensionMethods] = HasExtensionAttribute(typeDefinition); + + this.ApplyInterningProvider(loader.interningProvider); + this.Freeze(); + } + + public override string Namespace { + get { return namespaceName; } + set { throw new NotSupportedException(); } + } + + public override string ReflectionName { + get { return this.FullTypeName.ReflectionName; } + } + + public FullTypeName FullTypeName { + get { + return new TopLevelTypeName(namespaceName, this.Name, typeParameters.Count); + } + } + + public TypeKind Kind { + get { return kind; } + } + + public IList TypeParameters { + get { return typeParameters; } + } + + public IList BaseTypes { + get { + var result = LazyInit.VolatileRead(ref this.baseTypes); + if (result != null) { + return result; + } else { + return LazyInit.GetOrSet(ref this.baseTypes, TryInitBaseTypes()); + } + } + } + + IList TryInitBaseTypes() + { + var loader = LazyInit.VolatileRead(ref this.loader); + var cecilTypeDef = LazyInit.VolatileRead(ref this.cecilTypeDef); + if (loader == null || cecilTypeDef == null) { + // Cannot initialize because the references to loader/cecilTypeDef + // have already been cleared. + // This can only happen if the class was loaded by another thread concurrently to the TryInitBaseTypes() call, + // so the GetOrSet() call in the property will retrieve the value set by the other thread. + return null; + } + lock (loader.currentModule) { + var result = new List(); + loader.InitBaseTypes(cecilTypeDef, result); + return FreezableHelper.FreezeList(result); + } + } + + public IList NestedTypes { + get { + var result = LazyInit.VolatileRead(ref this.nestedTypes); + if (result != null) { + return result; + } else { + return LazyInit.GetOrSet(ref this.nestedTypes, TryInitNestedTypes()); + } + } + } + + IList TryInitNestedTypes() + { + var loader = LazyInit.VolatileRead(ref this.loader); + var cecilTypeDef = LazyInit.VolatileRead(ref this.cecilTypeDef); + if (loader == null || cecilTypeDef == null) { + // Cannot initialize because the references to loader/cecilTypeDef + // have already been cleared. + // This can only happen if the class was loaded by another thread concurrently to the TryInitNestedTypes() call, + // so the GetOrSet() call in the property will retrieve the value set by the other thread. + return null; + } + lock (loader.currentModule) { + var result = new List(); + loader.InitNestedTypes(cecilTypeDef, this, result); + return FreezableHelper.FreezeList(result); + } + } + + public IList Members { + get { + var result = LazyInit.VolatileRead(ref this.members); + if (result != null) { + return result; + } else { + return LazyInit.GetOrSet(ref this.members, TryInitMembers()); + } + } + } + + IList TryInitMembers() + { + var loader = LazyInit.VolatileRead(ref this.loader); + var cecilTypeDef = LazyInit.VolatileRead(ref this.cecilTypeDef); + if (loader == null || cecilTypeDef == null) { + // Cannot initialize because the references to loader/cecilTypeDef + // have already been cleared. + // This can only happen if the class was loaded by another thread concurrently to the TryInitMembers() call, + // so the GetOrSet() call in the property will retrieve the value set by the other thread. + return null; + } + lock (loader.currentModule) { + if (this.members != null) + return this.members; + var result = new List(); + loader.InitMembers(cecilTypeDef, this, result); + return FreezableHelper.FreezeList(result); + } + } + + public void InitAndReleaseReferences() + { + if (LazyInit.VolatileRead(ref this.baseTypes) == null) + LazyInit.GetOrSet(ref this.baseTypes, TryInitBaseTypes()); + if (LazyInit.VolatileRead(ref this.nestedTypes) == null) + LazyInit.GetOrSet(ref this.nestedTypes, TryInitNestedTypes()); + if (LazyInit.VolatileRead(ref this.members) == null) + LazyInit.GetOrSet(ref this.members, TryInitMembers()); + Thread.MemoryBarrier(); // commit lazily-initialized fields to memory before nulling out the references + // Allow the GC to collect the cecil type definition + loader = null; + cecilTypeDef = null; + } + + public IEnumerable Methods { + get { return Members.OfType(); } + } + + public IEnumerable Properties { + get { return Members.OfType(); } + } + + public IEnumerable Fields { + get { return Members.OfType(); } + } + + public IEnumerable Events { + get { return Members.OfType(); } + } + + public bool AddDefaultConstructorIfRequired { + get { return kind == TypeKind.Struct || kind == TypeKind.Enum; } + } + + public bool? HasExtensionMethods { + get { return flags[FlagHasExtensionMethods]; } + // we always return true or false, never null. + // FlagHasNoExtensionMethods is unused in LazyCecilTypeDefinition + } + + public bool IsPartial { + get { return false; } + } + + public override object Clone() + { + throw new NotSupportedException(); + } + + public IType Resolve(ITypeResolveContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + if (context.CurrentAssembly == null) + throw new ArgumentException("An ITypeDefinition cannot be resolved in a context without a current assembly."); + return context.CurrentAssembly.GetTypeDefinition(this.FullTypeName) + ?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count); + } + + public ITypeResolveContext CreateResolveContext(ITypeResolveContext parentContext) + { + return parentContext; + } + } + #endregion + + #region Read Method + [CLSCompliant(false)] + public IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, SymbolKind methodType = SymbolKind.Method) + { + return ReadMethod(method, parentType, methodType, null); + } + + IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, SymbolKind methodType, IUnresolvedMember accessorOwner) + { + if (method == null) + return null; + DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(parentType, method.Name); + m.SymbolKind = methodType; + m.AccessorOwner = accessorOwner; + m.HasBody = method.HasBody; + if (method.HasGenericParameters) { + for (int i = 0; i < method.GenericParameters.Count; i++) { + if (method.GenericParameters[i].Position != i) + throw new InvalidOperationException("g.Position != i"); + m.TypeParameters.Add(new DefaultUnresolvedTypeParameter( + SymbolKind.Method, i, method.GenericParameters[i].Name)); + } + for (int i = 0; i < method.GenericParameters.Count; i++) { + var tp = (DefaultUnresolvedTypeParameter)m.TypeParameters[i]; + AddConstraints(tp, method.GenericParameters[i]); + AddAttributes(method.GenericParameters[i], tp); + tp.ApplyInterningProvider(interningProvider); + } + } + + m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method.MethodReturnType); + + if (HasAnyAttributes(method)) + AddAttributes(method, m.Attributes, m.ReturnTypeAttributes); + TranslateModifiers(method, m); + + if (method.HasParameters) { + foreach (ParameterDefinition p in method.Parameters) { + m.Parameters.Add(ReadParameter(p)); + } + } + if (method.CallingConvention == MethodCallingConvention.VarArg) { + m.Parameters.Add(new DefaultUnresolvedParameter(SpecialType.ArgList, string.Empty)); + } + + // mark as extension method if the attribute is set + if (method.IsStatic && HasExtensionAttribute(method)) { + m.IsExtensionMethod = true; + } + + int lastDot = method.Name.LastIndexOf('.'); + if (lastDot >= 0 && method.HasOverrides) { + // To be consistent with the parser-initialized type system, shorten the method name: + if (ShortenInterfaceImplNames) + m.Name = method.Name.Substring(lastDot + 1); + m.IsExplicitInterfaceImplementation = true; + foreach (var or in method.Overrides) { + m.ExplicitInterfaceImplementations.Add(new DefaultMemberReference( + accessorOwner != null ? SymbolKind.Accessor : SymbolKind.Method, + ReadTypeReference(or.DeclaringType), + or.Name, or.GenericParameters.Count, m.Parameters.Select(p => p.Type).ToList())); + } + } + + FinishReadMember(m, method); + return m; + } + + static bool HasExtensionAttribute(ICustomAttributeProvider provider) + { + if (provider.HasCustomAttributes) { + foreach (var attr in provider.CustomAttributes) { + if (attr.AttributeType.Name == "ExtensionAttribute" && attr.AttributeType.Namespace == "System.Runtime.CompilerServices") + return true; + } + } + return false; + } + + bool IsVisible(MethodAttributes att) + { + att &= MethodAttributes.MemberAccessMask; + return IncludeInternalMembers + || att == MethodAttributes.Public + || att == MethodAttributes.Family + || att == MethodAttributes.FamORAssem; + } + + static Accessibility GetAccessibility(MethodAttributes attr) + { + switch (attr & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Public: + return Accessibility.Public; + case MethodAttributes.FamANDAssem: + return Accessibility.ProtectedAndInternal; + case MethodAttributes.Assembly: + return Accessibility.Internal; + case MethodAttributes.Family: + return Accessibility.Protected; + case MethodAttributes.FamORAssem: + return Accessibility.ProtectedOrInternal; + default: + return Accessibility.Private; + } + } + + void TranslateModifiers(MethodDefinition method, AbstractUnresolvedMember m) + { + if (m.DeclaringTypeDefinition.Kind == TypeKind.Interface) { + // interface members don't have modifiers, but we want to handle them as "public abstract" + m.Accessibility = Accessibility.Public; + m.IsAbstract = true; + } else { + m.Accessibility = GetAccessibility(method.Attributes); + if (method.IsAbstract) { + m.IsAbstract = true; + m.IsOverride = !method.IsNewSlot; + } else if (method.IsFinal) { + if (!method.IsNewSlot) { + m.IsSealed = true; + m.IsOverride = true; + } + } else if (method.IsVirtual) { + if (method.IsNewSlot) + m.IsVirtual = true; + else + m.IsOverride = true; + } + m.IsStatic = method.IsStatic; + } + } + #endregion + + #region Read Parameter + [CLSCompliant(false)] + public IUnresolvedParameter ReadParameter(ParameterDefinition parameter) + { + if (parameter == null) + throw new ArgumentNullException("parameter"); + var type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter); + var p = new DefaultUnresolvedParameter(type, interningProvider.Intern(parameter.Name)); + + if (parameter.ParameterType is Mono.Cecil.ByReferenceType) { + if (!parameter.IsIn && parameter.IsOut) + p.IsOut = true; + else + p.IsRef = true; + } + AddAttributes(parameter, p); + + if (parameter.IsOptional) { + p.DefaultValue = CreateSimpleConstantValue(type, parameter.Constant); + } + + if (parameter.ParameterType is Mono.Cecil.ArrayType) { + foreach (CustomAttribute att in parameter.CustomAttributes) { + if (att.AttributeType.FullName == typeof(ParamArrayAttribute).FullName) { + p.IsParams = true; + break; + } + } + } + + return interningProvider.Intern(p); + } + #endregion + + #region Read Field + bool IsVisible(FieldAttributes att) + { + att &= FieldAttributes.FieldAccessMask; + return IncludeInternalMembers + || att == FieldAttributes.Public + || att == FieldAttributes.Family + || att == FieldAttributes.FamORAssem; + } + + decimal? TryDecodeDecimalConstantAttribute(CustomAttribute attribute) + { + if (attribute.ConstructorArguments.Count != 5) + return null; + + BlobReader reader = new BlobReader(attribute.GetBlob(), null); + if (reader.ReadUInt16() != 0x0001) { + Debug.WriteLine("Unknown blob prolog"); + return null; + } + + // DecimalConstantAttribute has the arguments (byte scale, byte sign, uint hi, uint mid, uint low) or (byte scale, byte sign, int hi, int mid, int low) + // Both of these invoke the Decimal constructor (int lo, int mid, int hi, bool isNegative, byte scale) with explicit argument conversions if required. + var ctorArgs = new object[attribute.ConstructorArguments.Count]; + for (int i = 0; i < ctorArgs.Length; i++) { + switch (attribute.ConstructorArguments[i].Type.FullName) { + case "System.Byte": + ctorArgs[i] = reader.ReadByte(); + break; + case "System.Int32": + ctorArgs[i] = reader.ReadInt32(); + break; + case "System.UInt32": + ctorArgs[i] = unchecked((int)reader.ReadUInt32()); + break; + default: + return null; + } + } + + if (!ctorArgs.Select(a => a.GetType()).SequenceEqual(new[] { typeof(byte), typeof(byte), typeof(int), typeof(int), typeof(int) })) + return null; + + return new decimal((int)ctorArgs[4], (int)ctorArgs[3], (int)ctorArgs[2], (byte)ctorArgs[1] != 0, (byte)ctorArgs[0]); + } + + [CLSCompliant(false)] + public IUnresolvedField ReadField(FieldDefinition field, IUnresolvedTypeDefinition parentType) + { + if (field == null) + throw new ArgumentNullException("field"); + if (parentType == null) + throw new ArgumentNullException("parentType"); + + DefaultUnresolvedField f = new DefaultUnresolvedField(parentType, field.Name); + f.Accessibility = GetAccessibility(field.Attributes); + f.IsReadOnly = field.IsInitOnly; + f.IsStatic = field.IsStatic; + f.ReturnType = ReadTypeReference(field.FieldType, typeAttributes: field); + if (field.HasConstant) { + f.ConstantValue = CreateSimpleConstantValue(f.ReturnType, field.Constant); + } + else { + var decConstant = field.CustomAttributes.FirstOrDefault(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.DecimalConstantAttribute"); + if (decConstant != null) { + var constValue = TryDecodeDecimalConstantAttribute(decConstant); + if (constValue != null) + f.ConstantValue = CreateSimpleConstantValue(f.ReturnType, constValue); + } + } + AddAttributes(field, f); + + RequiredModifierType modreq = field.FieldType as RequiredModifierType; + if (modreq != null && modreq.ModifierType.FullName == typeof(IsVolatile).FullName) { + f.IsVolatile = true; + } + + FinishReadMember(f, field); + return f; + } + + static Accessibility GetAccessibility(FieldAttributes attr) + { + switch (attr & FieldAttributes.FieldAccessMask) { + case FieldAttributes.Public: + return Accessibility.Public; + case FieldAttributes.FamANDAssem: + return Accessibility.ProtectedAndInternal; + case FieldAttributes.Assembly: + return Accessibility.Internal; + case FieldAttributes.Family: + return Accessibility.Protected; + case FieldAttributes.FamORAssem: + return Accessibility.ProtectedOrInternal; + default: + return Accessibility.Private; + } + } + #endregion + + #region Type Parameter Constraints + void AddConstraints(DefaultUnresolvedTypeParameter tp, GenericParameter g) + { + switch (g.Attributes & GenericParameterAttributes.VarianceMask) { + case GenericParameterAttributes.Contravariant: + tp.Variance = VarianceModifier.Contravariant; + break; + case GenericParameterAttributes.Covariant: + tp.Variance = VarianceModifier.Covariant; + break; + } + + tp.HasReferenceTypeConstraint = g.HasReferenceTypeConstraint; + tp.HasValueTypeConstraint = g.HasNotNullableValueTypeConstraint; + tp.HasDefaultConstructorConstraint = g.HasDefaultConstructorConstraint; + + if (g.HasConstraints) { + foreach (TypeReference constraint in g.Constraints) { + tp.Constraints.Add(ReadTypeReference(constraint)); + } + } + } + #endregion + + #region Read Property + + Accessibility MergePropertyAccessibility (Accessibility left, Accessibility right) + { + if (left == Accessibility.Public || right == Accessibility.Public) + return Accessibility.Public; + + if (left == Accessibility.ProtectedOrInternal || right == Accessibility.ProtectedOrInternal) + return Accessibility.ProtectedOrInternal; + + if (left == Accessibility.Protected && right == Accessibility.Internal || + left == Accessibility.Internal && right == Accessibility.Protected) + return Accessibility.ProtectedOrInternal; + + if (left == Accessibility.Protected || right == Accessibility.Protected) + return Accessibility.Protected; + + if (left == Accessibility.Internal || right == Accessibility.Internal) + return Accessibility.Internal; + + if (left == Accessibility.ProtectedAndInternal || right == Accessibility.ProtectedAndInternal) + return Accessibility.ProtectedAndInternal; + + return left; + } + + [CLSCompliant(false)] + public IUnresolvedProperty ReadProperty(PropertyDefinition property, IUnresolvedTypeDefinition parentType, SymbolKind propertyType = SymbolKind.Property) + { + if (property == null) + throw new ArgumentNullException("property"); + if (parentType == null) + throw new ArgumentNullException("parentType"); + DefaultUnresolvedProperty p = new DefaultUnresolvedProperty(parentType, property.Name); + p.SymbolKind = propertyType; + TranslateModifiers(property.GetMethod ?? property.SetMethod, p); + if (property.GetMethod != null && property.SetMethod != null) + p.Accessibility = MergePropertyAccessibility (GetAccessibility (property.GetMethod.Attributes), GetAccessibility (property.SetMethod.Attributes)); + + p.ReturnType = ReadTypeReference(property.PropertyType, typeAttributes: property); + + p.Getter = ReadMethod(property.GetMethod, parentType, SymbolKind.Accessor, p); + p.Setter = ReadMethod(property.SetMethod, parentType, SymbolKind.Accessor, p); + + if (property.HasParameters) { + foreach (ParameterDefinition par in property.Parameters) { + p.Parameters.Add(ReadParameter(par)); + } + } + AddAttributes(property, p); + + var accessor = p.Getter ?? p.Setter; + if (accessor != null && accessor.IsExplicitInterfaceImplementation) { + if (ShortenInterfaceImplNames) + p.Name = property.Name.Substring(property.Name.LastIndexOf('.') + 1); + p.IsExplicitInterfaceImplementation = true; + foreach (var mr in accessor.ExplicitInterfaceImplementations) { + p.ExplicitInterfaceImplementations.Add(new AccessorOwnerMemberReference(mr)); + } + } + + FinishReadMember(p, property); + return p; + } + #endregion + + #region Read Event + [CLSCompliant(false)] + public IUnresolvedEvent ReadEvent(EventDefinition ev, IUnresolvedTypeDefinition parentType) + { + if (ev == null) + throw new ArgumentNullException("ev"); + if (parentType == null) + throw new ArgumentNullException("parentType"); + + DefaultUnresolvedEvent e = new DefaultUnresolvedEvent(parentType, ev.Name); + TranslateModifiers(ev.AddMethod, e); + e.ReturnType = ReadTypeReference(ev.EventType, typeAttributes: ev); + + e.AddAccessor = ReadMethod(ev.AddMethod, parentType, SymbolKind.Accessor, e); + e.RemoveAccessor = ReadMethod(ev.RemoveMethod, parentType, SymbolKind.Accessor, e); + e.InvokeAccessor = ReadMethod(ev.InvokeMethod, parentType, SymbolKind.Accessor, e); + + AddAttributes(ev, e); + + var accessor = e.AddAccessor ?? e.RemoveAccessor ?? e.InvokeAccessor; + if (accessor != null && accessor.IsExplicitInterfaceImplementation) { + if (ShortenInterfaceImplNames) + e.Name = ev.Name.Substring(ev.Name.LastIndexOf('.') + 1); + e.IsExplicitInterfaceImplementation = true; + foreach (var mr in accessor.ExplicitInterfaceImplementations) { + e.ExplicitInterfaceImplementations.Add(new AccessorOwnerMemberReference(mr)); + } + } + + FinishReadMember(e, ev); + + return e; + } + #endregion + + #region FinishReadMember / Interning + void FinishReadMember(AbstractUnresolvedMember member, MemberReference cecilDefinition) + { + member.ApplyInterningProvider(interningProvider); + member.Freeze(); + RegisterCecilObject(member, cecilDefinition); + } + #endregion + + #region Type system translation table + readonly Dictionary typeSystemTranslationTable; + + void RegisterCecilObject(IUnresolvedEntity typeSystemObject, MemberReference cecilObject) + { + if (OnEntityLoaded != null) + OnEntityLoaded(typeSystemObject, cecilObject); + + AddToTypeSystemTranslationTable(typeSystemObject, cecilObject); + } + + void AddToTypeSystemTranslationTable(object typeSystemObject, object cecilObject) + { + if (typeSystemTranslationTable != null) { + // When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently + lock (typeSystemTranslationTable) { + typeSystemTranslationTable[typeSystemObject] = cecilObject; + } + } + } + + T InternalGetCecilObject (object typeSystemObject) where T : class + { + if (typeSystemObject == null) + throw new ArgumentNullException ("typeSystemObject"); + if (!HasCecilReferences) + throw new NotSupportedException ("This instance contains no cecil references."); + object result; + lock (typeSystemTranslationTable) { + if (!typeSystemTranslationTable.TryGetValue (typeSystemObject, out result)) + return null; + } + return result as T; + } + + [CLSCompliant(false)] + public AssemblyDefinition GetCecilObject (IUnresolvedAssembly content) + { + return InternalGetCecilObject (content); + } + + [CLSCompliant(false)] + public TypeDefinition GetCecilObject (IUnresolvedTypeDefinition type) + { + if (type == null) + throw new ArgumentNullException ("type"); + return InternalGetCecilObject (type); + } + + [CLSCompliant(false)] + public MethodDefinition GetCecilObject (IUnresolvedMethod method) + { + return InternalGetCecilObject (method); + } + + [CLSCompliant(false)] + public FieldDefinition GetCecilObject (IUnresolvedField field) + { + return InternalGetCecilObject (field); + } + + [CLSCompliant(false)] + public EventDefinition GetCecilObject (IUnresolvedEvent evt) + { + return InternalGetCecilObject (evt); + } + + [CLSCompliant(false)] + public PropertyDefinition GetCecilObject (IUnresolvedProperty property) + { + return InternalGetCecilObject (property); + } + #endregion + } +} diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index a084a5d3c..cdd76b823 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -414,10 +414,6 @@ {984cc812-9470-4a13-aff9-cc44068d666c} ICSharpCode.Decompiler - - {53dca265-3c3c-42f9-b647-f72ba678122b} - ICSharpCode.NRefactory.CSharp - {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 299829a76..c2906ad1c 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -169,10 +169,10 @@ namespace ICSharpCode.ILSpy } else if (e.Key == Key.M && e.KeyboardDevice.Modifiers == ModifierKeys.Control) { searchModeComboBox.SelectedIndex = (int)SearchMode.Member; e.Handled = true; - } else if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control) { + } /*else if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control) { searchModeComboBox.SelectedIndex = (int)SearchMode.Literal; e.Handled = true; - } + }*/ } void SearchBox_PreviewKeyDown(object sender, KeyEventArgs e) @@ -277,8 +277,8 @@ namespace ICSharpCode.ILSpy if (terms[0].StartsWith("e:", StringComparison.Ordinal)) return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Event); - if (terms[0].StartsWith("c:", StringComparison.Ordinal)) - return new LiteralSearchStrategy(terms[0].Substring(2)); + //if (terms[0].StartsWith("c:", StringComparison.Ordinal)) + // return new LiteralSearchStrategy(terms[0].Substring(2)); } switch (mode) @@ -289,8 +289,8 @@ namespace ICSharpCode.ILSpy return new TypeSearchStrategy(terms); case SearchMode.Member: return new MemberSearchStrategy(terms); - case SearchMode.Literal: - return new LiteralSearchStrategy(terms); + //case SearchMode.Literal: + // return new LiteralSearchStrategy(terms); case SearchMode.Method: return new MemberSearchStrategy(terms, MemberSearchKind.Method); case SearchMode.Field: @@ -358,6 +358,6 @@ namespace ICSharpCode.ILSpy Field, Property, Event, - Literal + //Literal } } \ No newline at end of file diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index 695265c73..cfae22b0a 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -155,7 +155,7 @@ namespace ICSharpCode.ILSpy } } } - + /* class LiteralSearchStrategy : AbstractSearchStrategy { readonly TypeCode searchTermLiteralType; @@ -326,7 +326,7 @@ namespace ICSharpCode.ILSpy } return false; } - } + }*/ enum MemberSearchKind {