mirror of https://github.com/icsharpcode/ILSpy.git
17 changed files with 1 additions and 6640 deletions
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,69 +0,0 @@
@@ -1,69 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.PatternMatching; |
||||
|
||||
namespace ICSharpCode.Decompiler.Ast |
||||
{ |
||||
/// <summary>
|
||||
/// Allows storing comments inside IEnumerable{Statement}. Used in the AstMethodBuilder.
|
||||
/// CommentStatement nodes are replaced with regular comments later on.
|
||||
/// </summary>
|
||||
class CommentStatement : Statement |
||||
{ |
||||
string comment; |
||||
|
||||
public CommentStatement(string comment) |
||||
{ |
||||
if (comment == null) |
||||
throw new ArgumentNullException("comment"); |
||||
this.comment = comment; |
||||
} |
||||
|
||||
public override void AcceptVisitor(IAstVisitor visitor) |
||||
{ |
||||
} |
||||
|
||||
public override T AcceptVisitor<T>(IAstVisitor<T> visitor) |
||||
{ |
||||
return default(T); |
||||
} |
||||
|
||||
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) |
||||
{ |
||||
return default(S); |
||||
} |
||||
|
||||
public static void ReplaceAll(AstNode tree) |
||||
{ |
||||
foreach (var cs in tree.Descendants.OfType<CommentStatement>()) { |
||||
cs.Parent.InsertChildBefore(cs, new Comment(cs.comment), Roles.Comment); |
||||
cs.Remove(); |
||||
} |
||||
} |
||||
|
||||
protected override bool DoMatch(AstNode other, Match match) |
||||
{ |
||||
CommentStatement o = other as CommentStatement; |
||||
return o != null && MatchString(comment, o.comment); |
||||
} |
||||
} |
||||
} |
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using ICSharpCode.Decompiler.Ast; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler |
||||
{ |
||||
public class DecompilerContext |
||||
{ |
||||
public ModuleDefinition CurrentModule; |
||||
public CancellationToken CancellationToken; |
||||
public TypeDefinition CurrentType; |
||||
public MethodDefinition CurrentMethod; |
||||
public DecompilerSettings Settings = new DecompilerSettings(); |
||||
public bool CurrentMethodIsAsync; |
||||
|
||||
public DecompilerContext(ModuleDefinition currentModule) |
||||
{ |
||||
if (currentModule == null) |
||||
throw new ArgumentNullException(nameof(currentModule)); |
||||
this.CurrentModule = currentModule; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Used to pass variable names from a method to its anonymous methods.
|
||||
/// </summary>
|
||||
internal List<string> ReservedVariableNames = new List<string>(); |
||||
|
||||
public DecompilerContext Clone() |
||||
{ |
||||
DecompilerContext ctx = (DecompilerContext)MemberwiseClone(); |
||||
ctx.ReservedVariableNames = new List<string>(ctx.ReservedVariableNames); |
||||
return ctx; |
||||
} |
||||
} |
||||
} |
@ -1,347 +0,0 @@
@@ -1,347 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
using ICSharpCode.Decompiler.ILAst; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.Ast |
||||
{ |
||||
public class NameVariables |
||||
{ |
||||
static readonly Dictionary<string, string> typeNameToVariableNameDict = new Dictionary<string, string> { |
||||
{ "System.Boolean", "flag" }, |
||||
{ "System.Byte", "b" }, |
||||
{ "System.SByte", "b" }, |
||||
{ "System.Int16", "num" }, |
||||
{ "System.Int32", "num" }, |
||||
{ "System.Int64", "num" }, |
||||
{ "System.UInt16", "num" }, |
||||
{ "System.UInt32", "num" }, |
||||
{ "System.UInt64", "num" }, |
||||
{ "System.Single", "num" }, |
||||
{ "System.Double", "num" }, |
||||
{ "System.Decimal", "num" }, |
||||
{ "System.String", "text" }, |
||||
{ "System.Object", "obj" }, |
||||
{ "System.Char", "c" } |
||||
}; |
||||
|
||||
|
||||
public static void AssignNamesToVariables(DecompilerContext context, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> variables, ILBlock methodBody) |
||||
{ |
||||
NameVariables nv = new NameVariables(); |
||||
nv.context = context; |
||||
nv.fieldNamesInCurrentType = context.CurrentType.Fields.Select(f => f.Name).ToList(); |
||||
// First mark existing variable names as reserved.
|
||||
foreach (string name in context.ReservedVariableNames) |
||||
nv.AddExistingName(name); |
||||
foreach (var p in parameters) |
||||
nv.AddExistingName(p.Name); |
||||
foreach (var v in variables) { |
||||
if (v.IsGenerated) { |
||||
// don't introduce names for variables generated by ILSpy - keep "expr"/"arg"
|
||||
nv.AddExistingName(v.Name); |
||||
} else if (v.OriginalVariable != null && context.Settings.UseDebugSymbols) { |
||||
string varName = v.OriginalVariable.Name; |
||||
if (string.IsNullOrEmpty(varName) || varName.StartsWith("V_", StringComparison.Ordinal) || !IsValidName(varName)) |
||||
{ |
||||
// don't use the name from the debug symbols if it looks like a generated name
|
||||
v.Name = null; |
||||
} else { |
||||
// use the name from the debug symbols
|
||||
// (but ensure we don't use the same name for two variables)
|
||||
v.Name = nv.GetAlternativeName(varName); |
||||
} |
||||
} else { |
||||
v.Name = null; |
||||
} |
||||
} |
||||
// Now generate names:
|
||||
foreach (ILVariable p in parameters) { |
||||
if (string.IsNullOrEmpty(p.Name)) |
||||
p.Name = nv.GenerateNameForVariable(p, methodBody); |
||||
} |
||||
foreach (ILVariable varDef in variables) { |
||||
if (string.IsNullOrEmpty(varDef.Name)) |
||||
varDef.Name = nv.GenerateNameForVariable(varDef, methodBody); |
||||
} |
||||
} |
||||
|
||||
static bool IsValidName(string varName) |
||||
{ |
||||
if (string.IsNullOrEmpty(varName)) |
||||
return false; |
||||
if (!(char.IsLetter(varName[0]) || varName[0] == '_')) |
||||
return false; |
||||
for (int i = 1; i < varName.Length; i++) { |
||||
if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_')) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
DecompilerContext context; |
||||
List<string> fieldNamesInCurrentType; |
||||
Dictionary<string, int> typeNames = new Dictionary<string, int>(); |
||||
|
||||
public void AddExistingName(string name) |
||||
{ |
||||
if (string.IsNullOrEmpty(name)) |
||||
return; |
||||
int number; |
||||
string nameWithoutDigits = SplitName(name, out number); |
||||
int existingNumber; |
||||
if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) { |
||||
typeNames[nameWithoutDigits] = Math.Max(number, existingNumber); |
||||
} else { |
||||
typeNames.Add(nameWithoutDigits, number); |
||||
} |
||||
} |
||||
|
||||
string SplitName(string name, out int number) |
||||
{ |
||||
// First, identify whether the name already ends with a number:
|
||||
int pos = name.Length; |
||||
while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9') |
||||
pos--; |
||||
if (pos < name.Length) { |
||||
if (int.TryParse(name.Substring(pos), out number)) { |
||||
return name.Substring(0, pos); |
||||
} |
||||
} |
||||
number = 1; |
||||
return name; |
||||
} |
||||
|
||||
const char maxLoopVariableName = 'n'; |
||||
|
||||
public string GetAlternativeName(string oldVariableName) |
||||
{ |
||||
if (oldVariableName.Length == 1 && oldVariableName[0] >= 'i' && oldVariableName[0] <= maxLoopVariableName) { |
||||
for (char c = 'i'; c <= maxLoopVariableName; c++) { |
||||
if (!typeNames.ContainsKey(c.ToString())) { |
||||
typeNames.Add(c.ToString(), 1); |
||||
return c.ToString(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int number; |
||||
string nameWithoutDigits = SplitName(oldVariableName, out number); |
||||
|
||||
if (!typeNames.ContainsKey(nameWithoutDigits)) { |
||||
typeNames.Add(nameWithoutDigits, number - 1); |
||||
} |
||||
int count = ++typeNames[nameWithoutDigits]; |
||||
if (count != 1) { |
||||
return nameWithoutDigits + count.ToString(); |
||||
} else { |
||||
return nameWithoutDigits; |
||||
} |
||||
} |
||||
|
||||
string GenerateNameForVariable(ILVariable variable, ILBlock methodBody) |
||||
{ |
||||
string proposedName = null; |
||||
if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) { |
||||
// test whether the variable might be a loop counter
|
||||
bool isLoopCounter = false; |
||||
foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive<ILWhileLoop>()) { |
||||
ILExpression expr = loop.Condition; |
||||
while (expr != null && expr.Code == ILCode.LogicNot) |
||||
expr = expr.Arguments[0]; |
||||
if (expr != null) { |
||||
switch (expr.Code) { |
||||
case ILCode.Clt: |
||||
case ILCode.Clt_Un: |
||||
case ILCode.Cgt: |
||||
case ILCode.Cgt_Un: |
||||
case ILCode.Cle: |
||||
case ILCode.Cle_Un: |
||||
case ILCode.Cge: |
||||
case ILCode.Cge_Un: |
||||
ILVariable loadVar; |
||||
if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) { |
||||
isLoopCounter = true; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if (isLoopCounter) { |
||||
// For loop variables, use i,j,k,l,m,n
|
||||
for (char c = 'i'; c <= maxLoopVariableName; c++) { |
||||
if (!typeNames.ContainsKey(c.ToString())) { |
||||
proposedName = c.ToString(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (string.IsNullOrEmpty(proposedName)) { |
||||
var proposedNameForStores = |
||||
(from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>() |
||||
where expr.Code == ILCode.Stloc && expr.Operand == variable |
||||
select GetNameFromExpression(expr.Arguments.Single()) |
||||
).Except(fieldNamesInCurrentType).ToList(); |
||||
if (proposedNameForStores.Count == 1) { |
||||
proposedName = proposedNameForStores[0]; |
||||
} |
||||
} |
||||
if (string.IsNullOrEmpty(proposedName)) { |
||||
var proposedNameForLoads = |
||||
(from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>() |
||||
from i in Enumerable.Range(0, expr.Arguments.Count) |
||||
let arg = expr.Arguments[i] |
||||
where arg.Code == ILCode.Ldloc && arg.Operand == variable |
||||
select GetNameForArgument(expr, i) |
||||
).Except(fieldNamesInCurrentType).ToList(); |
||||
if (proposedNameForLoads.Count == 1) { |
||||
proposedName = proposedNameForLoads[0]; |
||||
} |
||||
} |
||||
if (string.IsNullOrEmpty(proposedName)) { |
||||
proposedName = GetNameByType(variable.Type); |
||||
} |
||||
|
||||
// remove any numbers from the proposed name
|
||||
int number; |
||||
proposedName = SplitName(proposedName, out number); |
||||
|
||||
if (!typeNames.ContainsKey(proposedName)) { |
||||
typeNames.Add(proposedName, 0); |
||||
} |
||||
int count = ++typeNames[proposedName]; |
||||
if (count > 1) { |
||||
return proposedName + count.ToString(); |
||||
} else { |
||||
return proposedName; |
||||
} |
||||
} |
||||
|
||||
static string GetNameFromExpression(ILExpression expr) |
||||
{ |
||||
switch (expr.Code) { |
||||
case ILCode.Ldfld: |
||||
case ILCode.Ldsfld: |
||||
return CleanUpVariableName(((FieldReference)expr.Operand).Name); |
||||
case ILCode.Call: |
||||
case ILCode.Callvirt: |
||||
case ILCode.CallGetter: |
||||
case ILCode.CallvirtGetter: |
||||
MethodReference mr = (MethodReference)expr.Operand; |
||||
if (mr.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && mr.Parameters.Count == 0) { |
||||
// use name from properties, but not from indexers
|
||||
return CleanUpVariableName(mr.Name.Substring(4)); |
||||
} else if (mr.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) { |
||||
// use name from Get-methods
|
||||
return CleanUpVariableName(mr.Name.Substring(3)); |
||||
} |
||||
break; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static string GetNameForArgument(ILExpression parent, int i) |
||||
{ |
||||
switch (parent.Code) { |
||||
case ILCode.Stfld: |
||||
case ILCode.Stsfld: |
||||
if (i == parent.Arguments.Count - 1) // last argument is stored value
|
||||
return CleanUpVariableName(((FieldReference)parent.Operand).Name); |
||||
else |
||||
break; |
||||
case ILCode.Call: |
||||
case ILCode.Callvirt: |
||||
case ILCode.Newobj: |
||||
case ILCode.CallGetter: |
||||
case ILCode.CallvirtGetter: |
||||
case ILCode.CallSetter: |
||||
case ILCode.CallvirtSetter: |
||||
MethodReference methodRef = (MethodReference)parent.Operand; |
||||
if (methodRef.Parameters.Count == 1 && i == parent.Arguments.Count - 1) { |
||||
// argument might be value of a setter
|
||||
if (methodRef.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase)) { |
||||
return CleanUpVariableName(methodRef.Name.Substring(4)); |
||||
} else if (methodRef.Name.StartsWith("Set", StringComparison.OrdinalIgnoreCase) && methodRef.Name.Length >= 4 && char.IsUpper(methodRef.Name[3])) { |
||||
return CleanUpVariableName(methodRef.Name.Substring(3)); |
||||
} |
||||
} |
||||
MethodDefinition methodDef = methodRef.Resolve(); |
||||
if (methodDef != null) { |
||||
var p = methodDef.Parameters.ElementAtOrDefault((parent.Code != ILCode.Newobj && methodDef.HasThis) ? i - 1 : i); |
||||
if (p != null && !string.IsNullOrEmpty(p.Name)) |
||||
return CleanUpVariableName(p.Name); |
||||
} |
||||
break; |
||||
case ILCode.Ret: |
||||
return "result"; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
string GetNameByType(TypeReference type) |
||||
{ |
||||
type = TypeAnalysis.UnpackModifiers(type); |
||||
|
||||
GenericInstanceType git = type as GenericInstanceType; |
||||
if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) { |
||||
type = ((GenericInstanceType)type).GenericArguments[0]; |
||||
} |
||||
|
||||
string name; |
||||
if (type.IsArray) { |
||||
name = "array"; |
||||
} else if (type.IsPointer || type.IsByReference) { |
||||
name = "ptr"; |
||||
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) { |
||||
name = "ex"; |
||||
} else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { |
||||
name = type.Name; |
||||
// remove the 'I' for interfaces
|
||||
if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) |
||||
name = name.Substring(1); |
||||
name = CleanUpVariableName(name); |
||||
} |
||||
return name; |
||||
} |
||||
|
||||
static string CleanUpVariableName(string name) |
||||
{ |
||||
// remove the backtick (generics)
|
||||
int pos = name.IndexOf('`'); |
||||
if (pos >= 0) |
||||
name = name.Substring(0, pos); |
||||
|
||||
// remove field prefix:
|
||||
if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal)) |
||||
name = name.Substring(2); |
||||
else if (name.Length > 1 && name[0] == '_' && (char.IsLetter(name[1]) || name[1] == '_')) |
||||
name = name.Substring(1); |
||||
|
||||
if (name.Length == 0) |
||||
return "obj"; |
||||
else |
||||
return char.ToLower(name[0]) + name.Substring(1); |
||||
} |
||||
} |
||||
} |
@ -1,373 +0,0 @@
@@ -1,373 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.Decompiler; |
||||
using ICSharpCode.Decompiler.ILAst; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.Ast |
||||
{ |
||||
public class TextTokenWriter : TokenWriter |
||||
{ |
||||
readonly ITextOutput output; |
||||
readonly DecompilerContext context; |
||||
readonly Stack<AstNode> nodeStack = new Stack<AstNode>(); |
||||
int braceLevelWithinType = -1; |
||||
bool inDocumentationComment = false; |
||||
bool firstUsingDeclaration; |
||||
bool lastUsingDeclaration; |
||||
|
||||
TextLocation? lastEndOfLine; |
||||
|
||||
public bool FoldBraces = false; |
||||
|
||||
public TextTokenWriter(ITextOutput output, DecompilerContext context) |
||||
{ |
||||
if (output == null) |
||||
throw new ArgumentNullException("output"); |
||||
if (context == null) |
||||
throw new ArgumentNullException("context"); |
||||
this.output = output; |
||||
this.context = context; |
||||
} |
||||
|
||||
public override void WriteIdentifier(Identifier identifier) |
||||
{ |
||||
if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { |
||||
output.Write('@'); |
||||
} |
||||
|
||||
var definition = GetCurrentDefinition(); |
||||
if (definition != null) { |
||||
output.WriteDefinition(identifier.Name, definition, false); |
||||
return; |
||||
} |
||||
|
||||
object memberRef = GetCurrentMemberReference(); |
||||
|
||||
if (memberRef != null) { |
||||
output.WriteReference(identifier.Name, memberRef); |
||||
return; |
||||
} |
||||
|
||||
definition = GetCurrentLocalDefinition(); |
||||
if (definition != null) { |
||||
output.WriteDefinition(identifier.Name, definition); |
||||
return; |
||||
} |
||||
|
||||
memberRef = GetCurrentLocalReference(); |
||||
if (memberRef != null) { |
||||
output.WriteReference(identifier.Name, memberRef, true); |
||||
return; |
||||
} |
||||
|
||||
if (firstUsingDeclaration) { |
||||
output.MarkFoldStart(defaultCollapsed: true); |
||||
firstUsingDeclaration = false; |
||||
} |
||||
|
||||
output.Write(identifier.Name); |
||||
} |
||||
|
||||
MemberReference GetCurrentMemberReference() |
||||
{ |
||||
AstNode node = nodeStack.Peek(); |
||||
MemberReference memberRef = node.Annotation<MemberReference>(); |
||||
if (memberRef == null && node.Role == Roles.TargetExpression && (node.Parent is InvocationExpression || node.Parent is ObjectCreateExpression)) { |
||||
memberRef = node.Parent.Annotation<MemberReference>(); |
||||
} |
||||
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && memberRef != null) { |
||||
var declaringType = memberRef.DeclaringType.Resolve(); |
||||
if (declaringType != null && declaringType.IsDelegate()) |
||||
return null; |
||||
} |
||||
return FilterMemberReference(memberRef); |
||||
} |
||||
|
||||
MemberReference FilterMemberReference(MemberReference memberRef) |
||||
{ |
||||
if (memberRef == null) |
||||
return null; |
||||
|
||||
if (context.Settings.AutomaticEvents && memberRef is FieldDefinition) { |
||||
var field = (FieldDefinition)memberRef; |
||||
return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? memberRef; |
||||
} |
||||
|
||||
return memberRef; |
||||
} |
||||
|
||||
object GetCurrentLocalReference() |
||||
{ |
||||
AstNode node = nodeStack.Peek(); |
||||
ILVariable variable = node.Annotation<ILVariable>(); |
||||
if (variable != null) { |
||||
if (variable.OriginalParameter != null) |
||||
return variable.OriginalParameter; |
||||
//if (variable.OriginalVariable != null)
|
||||
// return variable.OriginalVariable;
|
||||
return variable; |
||||
} |
||||
|
||||
var gotoStatement = node as GotoStatement; |
||||
if (gotoStatement != null) |
||||
{ |
||||
var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null); |
||||
if (method != null) |
||||
return method.ToString() + gotoStatement.Label; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
object GetCurrentLocalDefinition() |
||||
{ |
||||
AstNode node = nodeStack.Peek(); |
||||
if (node is Identifier && node.Parent != null) |
||||
node = node.Parent; |
||||
|
||||
var parameterDef = node.Annotation<ParameterDefinition>(); |
||||
if (parameterDef != null) |
||||
return parameterDef; |
||||
|
||||
if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) { |
||||
var variable = node.Annotation<ILVariable>(); |
||||
if (variable != null) { |
||||
if (variable.OriginalParameter != null) |
||||
return variable.OriginalParameter; |
||||
//if (variable.OriginalVariable != null)
|
||||
// return variable.OriginalVariable;
|
||||
return variable; |
||||
} |
||||
} |
||||
|
||||
var label = node as LabelStatement; |
||||
if (label != null) { |
||||
var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null); |
||||
if (method != null) |
||||
return method.ToString() + label.Label; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
object GetCurrentDefinition() |
||||
{ |
||||
if (nodeStack == null || nodeStack.Count == 0) |
||||
return null; |
||||
|
||||
var node = nodeStack.Peek(); |
||||
if (node is Identifier) |
||||
node = node.Parent; |
||||
if (IsDefinition(node)) |
||||
return node.Annotation<MemberReference>(); |
||||
|
||||
return null; |
||||
} |
||||
|
||||
public override void WriteKeyword(Role role, string keyword) |
||||
{ |
||||
output.Write(keyword); |
||||
} |
||||
|
||||
public override void WriteToken(Role role, string token) |
||||
{ |
||||
// Attach member reference to token only if there's no identifier in the current node.
|
||||
MemberReference memberRef = GetCurrentMemberReference(); |
||||
var node = nodeStack.Peek(); |
||||
if (memberRef != null && node.GetChildByRole(Roles.Identifier).IsNull) |
||||
output.WriteReference(token, memberRef); |
||||
else |
||||
output.Write(token); |
||||
} |
||||
|
||||
public override void Space() |
||||
{ |
||||
output.Write(' '); |
||||
} |
||||
|
||||
public void OpenBrace(BraceStyle style) |
||||
{ |
||||
if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration) |
||||
braceLevelWithinType++; |
||||
if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces) { |
||||
output.MarkFoldStart(defaultCollapsed: braceLevelWithinType == 1); |
||||
} |
||||
output.WriteLine(); |
||||
output.WriteLine("{"); |
||||
output.Indent(); |
||||
} |
||||
|
||||
public void CloseBrace(BraceStyle style) |
||||
{ |
||||
output.Unindent(); |
||||
output.Write('}'); |
||||
if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces) |
||||
output.MarkFoldEnd(); |
||||
if (braceLevelWithinType >= 0) |
||||
braceLevelWithinType--; |
||||
} |
||||
|
||||
public override void Indent() |
||||
{ |
||||
output.Indent(); |
||||
} |
||||
|
||||
public override void Unindent() |
||||
{ |
||||
output.Unindent(); |
||||
} |
||||
|
||||
public override void NewLine() |
||||
{ |
||||
if (lastUsingDeclaration) { |
||||
output.MarkFoldEnd(); |
||||
lastUsingDeclaration = false; |
||||
} |
||||
lastEndOfLine = output.Location; |
||||
output.WriteLine(); |
||||
} |
||||
|
||||
public override void WriteComment(CommentType commentType, string content) |
||||
{ |
||||
switch (commentType) { |
||||
case CommentType.SingleLine: |
||||
output.Write("//"); |
||||
output.WriteLine(content); |
||||
break; |
||||
case CommentType.MultiLine: |
||||
output.Write("/*"); |
||||
output.Write(content); |
||||
output.Write("*/"); |
||||
break; |
||||
case CommentType.Documentation: |
||||
bool isLastLine = !(nodeStack.Peek().NextSibling is Comment); |
||||
if (!inDocumentationComment && !isLastLine) { |
||||
inDocumentationComment = true; |
||||
output.MarkFoldStart("///" + content, true); |
||||
} |
||||
output.Write("///"); |
||||
output.Write(content); |
||||
if (inDocumentationComment && isLastLine) { |
||||
inDocumentationComment = false; |
||||
output.MarkFoldEnd(); |
||||
} |
||||
output.WriteLine(); |
||||
break; |
||||
default: |
||||
output.Write(content); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) |
||||
{ |
||||
// pre-processor directive must start on its own line
|
||||
output.Write('#'); |
||||
output.Write(type.ToString().ToLowerInvariant()); |
||||
if (!string.IsNullOrEmpty(argument)) { |
||||
output.Write(' '); |
||||
output.Write(argument); |
||||
} |
||||
output.WriteLine(); |
||||
} |
||||
|
||||
public override void WritePrimitiveValue(object value, string literalValue = null) |
||||
{ |
||||
new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, literalValue); |
||||
} |
||||
|
||||
public override void WritePrimitiveType(string type) |
||||
{ |
||||
output.Write(type); |
||||
if (type == "new") { |
||||
output.Write("()"); |
||||
} |
||||
} |
||||
|
||||
Stack<TextLocation> startLocations = new Stack<TextLocation>(); |
||||
Stack<MethodDebugSymbols> symbolsStack = new Stack<MethodDebugSymbols>(); |
||||
|
||||
public override void StartNode(AstNode node) |
||||
{ |
||||
if (nodeStack.Count == 0) { |
||||
if (IsUsingDeclaration(node)) { |
||||
firstUsingDeclaration = !IsUsingDeclaration(node.PrevSibling); |
||||
lastUsingDeclaration = !IsUsingDeclaration(node.NextSibling); |
||||
} else { |
||||
firstUsingDeclaration = false; |
||||
lastUsingDeclaration = false; |
||||
} |
||||
} |
||||
nodeStack.Push(node); |
||||
startLocations.Push(output.Location); |
||||
|
||||
if (node is EntityDeclaration && node.Annotation<MemberReference>() != null && node.GetChildByRole(Roles.Identifier).IsNull) |
||||
output.WriteDefinition("", node.Annotation<MemberReference>(), false); |
||||
|
||||
if (node.Annotation<MethodDebugSymbols>() != null) { |
||||
symbolsStack.Push(node.Annotation<MethodDebugSymbols>()); |
||||
symbolsStack.Peek().StartLocation = startLocations.Peek(); |
||||
} |
||||
} |
||||
|
||||
private bool IsUsingDeclaration(AstNode node) |
||||
{ |
||||
return node is UsingDeclaration || node is UsingAliasDeclaration; |
||||
} |
||||
|
||||
public override void EndNode(AstNode node) |
||||
{ |
||||
if (nodeStack.Pop() != node) |
||||
throw new InvalidOperationException(); |
||||
|
||||
var startLocation = startLocations.Pop(); |
||||
|
||||
// code mappings
|
||||
var ranges = node.Annotation<List<ILRange>>(); |
||||
if (symbolsStack.Count > 0 && ranges != null && ranges.Count > 0) { |
||||
// Ignore the newline which was printed at the end of the statement
|
||||
TextLocation endLocation = (node is Statement) ? (lastEndOfLine ?? output.Location) : output.Location; |
||||
symbolsStack.Peek().SequencePoints.Add( |
||||
new SequencePoint() { |
||||
ILRanges = ILRange.OrderAndJoin(ranges).ToArray(), |
||||
StartLocation = startLocation, |
||||
EndLocation = endLocation |
||||
}); |
||||
} |
||||
|
||||
if (node.Annotation<MethodDebugSymbols>() != null) { |
||||
symbolsStack.Peek().EndLocation = output.Location; |
||||
output.AddDebugSymbols(symbolsStack.Pop()); |
||||
} |
||||
} |
||||
|
||||
private static bool IsDefinition(AstNode node) |
||||
{ |
||||
return node is EntityDeclaration |
||||
|| (node is VariableInitializer && node.Parent is FieldDeclaration) |
||||
|| node is FixedVariableInitializer; |
||||
} |
||||
} |
||||
} |
@ -1,784 +0,0 @@
@@ -1,784 +0,0 @@
|
||||
// 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.Decompiler.CSharp.Resolver; |
||||
using ICSharpCode.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.CSharp.TypeSystem; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Analysis |
||||
{ |
||||
/// <summary>
|
||||
/// Represents a node in the control flow graph of a C# method.
|
||||
/// </summary>
|
||||
public class ControlFlowNode |
||||
{ |
||||
public readonly Statement PreviousStatement; |
||||
public readonly Statement NextStatement; |
||||
|
||||
public readonly ControlFlowNodeType Type; |
||||
|
||||
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>(); |
||||
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>(); |
||||
|
||||
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 |
||||
{ |
||||
/// <summary>
|
||||
/// Unknown node type
|
||||
/// </summary>
|
||||
None, |
||||
/// <summary>
|
||||
/// Node in front of a statement
|
||||
/// </summary>
|
||||
StartNode, |
||||
/// <summary>
|
||||
/// Node between two statements
|
||||
/// </summary>
|
||||
BetweenStatements, |
||||
/// <summary>
|
||||
/// Node at the end of a statement list
|
||||
/// </summary>
|
||||
EndNode, |
||||
/// <summary>
|
||||
/// Node representing the position before evaluating the condition of a loop.
|
||||
/// </summary>
|
||||
LoopCondition |
||||
} |
||||
|
||||
public class ControlFlowEdge |
||||
{ |
||||
public readonly ControlFlowNode From; |
||||
public readonly ControlFlowNode To; |
||||
public readonly ControlFlowEdgeType Type; |
||||
|
||||
List<TryCatchStatement> 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<TryCatchStatement>(); |
||||
jumpOutOfTryFinally.Add(tryFinally); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether this control flow edge is leaving any try-finally statements.
|
||||
/// </summary>
|
||||
public bool IsLeavingTryFinally { |
||||
get { return jumpOutOfTryFinally != null; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the try-finally statements that this control flow edge is leaving.
|
||||
/// </summary>
|
||||
public IEnumerable<TryCatchStatement> TryFinallyStatements { |
||||
get { return jumpOutOfTryFinally ?? Enumerable.Empty<TryCatchStatement>(); } |
||||
} |
||||
} |
||||
|
||||
public enum ControlFlowEdgeType |
||||
{ |
||||
/// <summary>
|
||||
/// Regular control flow.
|
||||
/// </summary>
|
||||
Normal, |
||||
/// <summary>
|
||||
/// Conditional control flow (edge taken if condition is true)
|
||||
/// </summary>
|
||||
ConditionTrue, |
||||
/// <summary>
|
||||
/// Conditional control flow (edge taken if condition is false)
|
||||
/// </summary>
|
||||
ConditionFalse, |
||||
/// <summary>
|
||||
/// A jump statement (goto, goto case, break or continue)
|
||||
/// </summary>
|
||||
Jump |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Constructs the control flow graph for C# statements.
|
||||
/// </summary>
|
||||
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<AstNode, CancellationToken, ResolveResult> resolver; |
||||
List<ControlFlowNode> nodes; |
||||
Dictionary<string, ControlFlowNode> labels; |
||||
List<ControlFlowNode> gotoStatements; |
||||
CancellationToken cancellationToken; |
||||
|
||||
internal IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, Func<AstNode, CancellationToken, ResolveResult> resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) |
||||
{ |
||||
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); |
||||
nodeCreationVisitor.builder = this; |
||||
try { |
||||
this.nodes = new List<ControlFlowNode>(); |
||||
this.labels = new Dictionary<string, ControlFlowNode>(); |
||||
this.gotoStatements = new List<ControlFlowNode>(); |
||||
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<TryCatchStatement> targetParentTryCatch = new HashSet<TryCatchStatement>(targetStatement.Ancestors.OfType<TryCatchStatement>()); |
||||
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
|
||||
/// <summary>
|
||||
/// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b").
|
||||
/// </summary>
|
||||
public bool EvaluateOnlyPrimitiveConstants { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Evaluates an expression.
|
||||
/// </summary>
|
||||
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
|
||||
ResolveResult EvaluateConstant(Expression expr) |
||||
{ |
||||
if (expr.IsNull) |
||||
return null; |
||||
if (EvaluateOnlyPrimitiveConstants) { |
||||
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression)) |
||||
return null; |
||||
} |
||||
return resolver(expr, cancellationToken); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Evaluates an expression.
|
||||
/// </summary>
|
||||
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
|
||||
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<ControlFlowNode, ControlFlowNode> |
||||
{ |
||||
// '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<ControlFlowNode> breakTargets = new Stack<ControlFlowNode>(); |
||||
Stack<ControlFlowNode> continueTargets = new Stack<ControlFlowNode>(); |
||||
List<ControlFlowNode> gotoCaseOrDefault = new List<ControlFlowNode>(); |
||||
|
||||
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; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates an end node for <c>stmt</c> and connects <c>from</c> with the new node.
|
||||
/// </summary>
|
||||
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<Statement> 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<ControlFlowNode> sectionStartNodes = new List<ControlFlowNode>(); |
||||
|
||||
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) |
||||
{ |
||||
// <data> <condition> while (cond) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
|
||||
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) |
||||
{ |
||||
// <data> do { <bodyStart> embeddedStmt; <bodyEnd>} <condition> while(cond); <end>
|
||||
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 <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
|
||||
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) |
||||
{ |
||||
// <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end>
|
||||
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); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Debugging helper that exports a control flow graph.
|
||||
/// </summary>
|
||||
//public static GraphVizGraph ExportGraph(IList<ControlFlowNode> nodes)
|
||||
//{
|
||||
// GraphVizGraph g = new GraphVizGraph();
|
||||
// GraphVizNode[] n = new GraphVizNode[nodes.Count];
|
||||
// Dictionary<ControlFlowNode, int> dict = new Dictionary<ControlFlowNode, int>();
|
||||
// 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;
|
||||
//}
|
||||
} |
||||
} |
@ -1,750 +0,0 @@
@@ -1,750 +0,0 @@
|
||||
// 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.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.CSharp.TypeSystem; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Analysis |
||||
{ |
||||
/// <summary>
|
||||
/// Represents the definite assignment status of a variable at a specific location.
|
||||
/// </summary>
|
||||
public enum DefiniteAssignmentStatus |
||||
{ |
||||
/// <summary>
|
||||
/// The variable might be assigned or unassigned.
|
||||
/// </summary>
|
||||
PotentiallyAssigned, |
||||
/// <summary>
|
||||
/// The variable is definitely assigned.
|
||||
/// </summary>
|
||||
DefinitelyAssigned, |
||||
/// <summary>
|
||||
/// The variable is definitely assigned iff the expression results in the value 'true'.
|
||||
/// </summary>
|
||||
AssignedAfterTrueExpression, |
||||
/// <summary>
|
||||
/// The variable is definitely assigned iff the expression results in the value 'false'.
|
||||
/// </summary>
|
||||
AssignedAfterFalseExpression, |
||||
/// <summary>
|
||||
/// The code is unreachable.
|
||||
/// </summary>
|
||||
CodeUnreachable |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment)
|
||||
/// </summary>
|
||||
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<DefiniteAssignmentNode> allNodes = new List<DefiniteAssignmentNode>(); |
||||
readonly Dictionary<Statement, DefiniteAssignmentNode> beginNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>(); |
||||
readonly Dictionary<Statement, DefiniteAssignmentNode> endNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>(); |
||||
readonly Dictionary<Statement, DefiniteAssignmentNode> conditionNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>(); |
||||
readonly Func<AstNode, CancellationToken, ResolveResult> resolve; |
||||
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>(); |
||||
|
||||
string variableName; |
||||
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>(); |
||||
int analyzedRangeStart, analyzedRangeEnd; |
||||
CancellationToken analysisCancellationToken; |
||||
|
||||
Queue<DefiniteAssignmentNode> nodesWithModifiedInput = new Queue<DefiniteAssignmentNode>(); |
||||
|
||||
public DefiniteAssignmentAnalysis(Statement rootStatement, Func<AstNode, CancellationToken, ResolveResult> 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<DefiniteAssignmentNode>()); |
||||
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<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any()); |
||||
// Verify that we put all nodes into the dictionaries:
|
||||
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => beginNodeDict.ContainsKey(stmt))); |
||||
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().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<DefiniteAssignmentNode>()); |
||||
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<DefiniteAssignmentNode>()); |
||||
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); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the unassigned usages of the previously analyzed variable.
|
||||
/// </summary>
|
||||
public IList<IdentifierExpression> UnassignedVariableUses { |
||||
get { |
||||
return unassignedVariableUses.AsReadOnly(); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>By default, both 'start' and 'end' are inclusive.</remarks>
|
||||
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; |
||||
} |
||||
|
||||
/*/// <summary>
|
||||
/// Exports the CFG. This method is intended to help debugging issues related to definite assignment.
|
||||
/// </summary>
|
||||
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); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Evaluates an expression.
|
||||
/// </summary>
|
||||
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
|
||||
ResolveResult EvaluateConstant(Expression expr) |
||||
{ |
||||
return resolve(expr, analysisCancellationToken); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Evaluates an expression.
|
||||
/// </summary>
|
||||
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
|
||||
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<DefiniteAssignmentStatus, DefiniteAssignmentStatus> |
||||
{ |
||||
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
|
||||
} |
||||
} |
||||
} |
@ -1,68 +0,0 @@
@@ -1,68 +0,0 @@
|
||||
// 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.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.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); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,88 +0,0 @@
@@ -1,88 +0,0 @@
|
||||
// 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.Collections.Generic; |
||||
using ICSharpCode.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Resolver |
||||
{ |
||||
/// <summary>
|
||||
/// When an <see cref="IResolveVisitorNavigator"/> 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.
|
||||
/// </summary>
|
||||
public sealed class DetectSkippableNodesNavigator : IResolveVisitorNavigator |
||||
{ |
||||
readonly Dictionary<AstNode, ResolveVisitorNavigationMode> dict = new Dictionary<AstNode, ResolveVisitorNavigationMode>(); |
||||
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; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public ResolveVisitorNavigationMode Scan(AstNode node) |
||||
{ |
||||
ResolveVisitorNavigationMode mode; |
||||
if (dict.TryGetValue(node, out mode)) { |
||||
return mode; |
||||
} else { |
||||
return ResolveVisitorNavigationMode.Skip; |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public void Resolved(AstNode node, ResolveResult result) |
||||
{ |
||||
navigator.Resolved(node, result); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) |
||||
{ |
||||
navigator.ProcessConversion(expression, result, conversion, targetType); |
||||
} |
||||
} |
||||
} |
@ -1,101 +0,0 @@
@@ -1,101 +0,0 @@
|
||||
// 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.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Resolver |
||||
{ |
||||
/// <summary>
|
||||
/// Find all entities that are referenced in the scanned AST.
|
||||
/// </summary>
|
||||
public sealed class FindReferencedEntities : IResolveVisitorNavigator |
||||
{ |
||||
readonly Action<AstNode, IMember> memberReferenceFound; |
||||
readonly Action<AstNode, IType> typeReferenceFound; |
||||
|
||||
/// <summary>
|
||||
/// Creates a new FindReferencedEntities instance that
|
||||
/// looks for entity definitions.
|
||||
/// The visitor will report type definitions and member definitions (not specialized members).
|
||||
/// </summary>
|
||||
public FindReferencedEntities(Action<AstNode, IEntity> 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); |
||||
}; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a new FindReferencedEntities instance that
|
||||
/// looks for types and members.
|
||||
/// The visitor will report parameterized types and potentially specialized members.
|
||||
/// </summary>
|
||||
public FindReferencedEntities(Action<AstNode, IType> typeReferenceFound, Action<AstNode, IMember> 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); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,103 +0,0 @@
@@ -1,103 +0,0 @@
|
||||
// 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 ICSharpCode.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Resolver |
||||
{ |
||||
/// <summary>
|
||||
/// Allows controlling which nodes are resolved by the resolve visitor.
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolveVisitor"/>
|
||||
public interface IResolveVisitorNavigator |
||||
{ |
||||
/// <summary>
|
||||
/// Asks the navigator whether to scan, skip, or resolve a node.
|
||||
/// </summary>
|
||||
ResolveVisitorNavigationMode Scan(AstNode node); |
||||
|
||||
/// <summary>
|
||||
/// Notifies the navigator that a node was resolved.
|
||||
/// </summary>
|
||||
/// <param name="node">The node that was resolved</param>
|
||||
/// <param name="result">Resolve result</param>
|
||||
void Resolved(AstNode node, ResolveResult result); |
||||
|
||||
/// <summary>
|
||||
/// Notifies the navigator that an implicit conversion was applied.
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression that was resolved.</param>
|
||||
/// <param name="result">The resolve result of the expression.</param>
|
||||
/// <param name="conversion">The conversion applied to the expressed.</param>
|
||||
/// <param name="targetType">The target type of the conversion.</param>
|
||||
void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Represents the operation mode of the resolve visitor.
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolveVisitor"/>
|
||||
public enum ResolveVisitorNavigationMode |
||||
{ |
||||
/// <summary>
|
||||
/// Scan into the children of the current node, without resolving the current node.
|
||||
/// </summary>
|
||||
Scan, |
||||
/// <summary>
|
||||
/// Skip the current node - do not scan into it; do not resolve it.
|
||||
/// </summary>
|
||||
Skip, |
||||
/// <summary>
|
||||
/// Resolve the current node.
|
||||
/// Subnodes which are not required for resolving the current node
|
||||
/// will ask the navigator again whether they should be resolved.
|
||||
/// </summary>
|
||||
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); |
||||
} |
||||
} |
||||
} |
@ -1,77 +0,0 @@
@@ -1,77 +0,0 @@
|
||||
// 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.Decompiler.CSharp.Syntax; |
||||
using ICSharpCode.Decompiler.Semantics; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Resolver |
||||
{ |
||||
/// <summary>
|
||||
/// <see cref="IResolveVisitorNavigator"/> implementation that resolves a list of nodes.
|
||||
/// We will skip all nodes which are not the target nodes or ancestors of the target nodes.
|
||||
/// </summary>
|
||||
public class NodeListResolveVisitorNavigator : IResolveVisitorNavigator |
||||
{ |
||||
readonly Dictionary<AstNode, ResolveVisitorNavigationMode> dict = new Dictionary<AstNode, ResolveVisitorNavigationMode>(); |
||||
|
||||
/// <summary>
|
||||
/// Creates a new NodeListResolveVisitorNavigator that resolves the specified nodes.
|
||||
/// </summary>
|
||||
public NodeListResolveVisitorNavigator(params AstNode[] nodes) |
||||
: this((IEnumerable<AstNode>)nodes) |
||||
{ |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a new NodeListResolveVisitorNavigator that resolves the specified nodes.
|
||||
/// </summary>
|
||||
public NodeListResolveVisitorNavigator(IEnumerable<AstNode> 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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
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) |
||||
{ |
||||
} |
||||
} |
||||
} |
@ -1,864 +0,0 @@
@@ -1,864 +0,0 @@
|
||||
//
|
||||
// ObservableAstVisitor.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Krüger <mkrueger@novell.com>
|
||||
//
|
||||
// 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.Decompiler.CSharp.Syntax |
||||
{ |
||||
public class ObservableAstVisitor : IAstVisitor |
||||
{ |
||||
void Visit<T>(Action<T> enter, Action<T> 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<SyntaxTree> EnterSyntaxTree, LeaveSyntaxTree; |
||||
|
||||
void IAstVisitor.VisitSyntaxTree(SyntaxTree unit) |
||||
{ |
||||
Visit(EnterSyntaxTree, LeaveSyntaxTree, unit); |
||||
} |
||||
|
||||
public event Action<Comment> EnterComment, LeaveComment; |
||||
|
||||
void IAstVisitor.VisitComment(Comment comment) |
||||
{ |
||||
Visit(EnterComment, LeaveComment, comment); |
||||
} |
||||
|
||||
public event Action<NewLineNode> EnterNewLine, LeaveNewLine; |
||||
|
||||
void IAstVisitor.VisitNewLine(NewLineNode newLineNode) |
||||
{ |
||||
Visit(EnterNewLine, LeaveNewLine, newLineNode); |
||||
} |
||||
|
||||
public event Action<WhitespaceNode> EnterWhitespace, LeaveWhitespace; |
||||
|
||||
void IAstVisitor.VisitWhitespace(WhitespaceNode whitespace) |
||||
{ |
||||
Visit(EnterWhitespace, LeaveWhitespace, whitespace); |
||||
} |
||||
|
||||
public event Action<TextNode> EnterText, LeaveText; |
||||
|
||||
void IAstVisitor.VisitText(TextNode textNode) |
||||
{ |
||||
Visit(EnterText, LeaveText, textNode); |
||||
} |
||||
|
||||
public event Action<PreProcessorDirective> EnterPreProcessorDirective, LeavePreProcessorDirective; |
||||
void IAstVisitor.VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) |
||||
{ |
||||
Visit(EnterPreProcessorDirective, LeavePreProcessorDirective, preProcessorDirective); |
||||
} |
||||
|
||||
public event Action<DocumentationReference> EnterDocumentationReference, LeaveDocumentationReference; |
||||
|
||||
void IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) |
||||
{ |
||||
Visit(EnterDocumentationReference, LeaveDocumentationReference, documentationReference); |
||||
} |
||||
|
||||
public event Action<Identifier> EnterIdentifier, LeaveIdentifier; |
||||
|
||||
void IAstVisitor.VisitIdentifier(Identifier identifier) |
||||
{ |
||||
Visit(EnterIdentifier, LeaveIdentifier, identifier); |
||||
} |
||||
|
||||
public event Action<CSharpTokenNode> EnterCSharpTokenNode, LeaveCSharpTokenNode; |
||||
|
||||
void IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode token) |
||||
{ |
||||
Visit(EnterCSharpTokenNode, LeaveCSharpTokenNode, token); |
||||
} |
||||
|
||||
public event Action<PrimitiveType> EnterPrimitiveType, LeavePrimitiveType; |
||||
|
||||
void IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType) |
||||
{ |
||||
Visit(EnterPrimitiveType, LeavePrimitiveType, primitiveType); |
||||
} |
||||
|
||||
public event Action<ComposedType> EnterComposedType, LeaveComposedType; |
||||
|
||||
void IAstVisitor.VisitComposedType(ComposedType composedType) |
||||
{ |
||||
Visit(EnterComposedType, LeaveComposedType, composedType); |
||||
} |
||||
|
||||
public event Action<SimpleType> EnterSimpleType, LeaveSimpleType; |
||||
|
||||
void IAstVisitor.VisitSimpleType(SimpleType simpleType) |
||||
{ |
||||
Visit(EnterSimpleType, LeaveSimpleType, simpleType); |
||||
} |
||||
|
||||
public event Action<MemberType> EnterMemberType, LeaveMemberType; |
||||
|
||||
void IAstVisitor.VisitMemberType(MemberType memberType) |
||||
{ |
||||
Visit(EnterMemberType, LeaveMemberType, memberType); |
||||
} |
||||
|
||||
public event Action<Attribute> EnterAttribute, LeaveAttribute; |
||||
|
||||
void IAstVisitor.VisitAttribute(Attribute attribute) |
||||
{ |
||||
Visit(EnterAttribute, LeaveAttribute, attribute); |
||||
} |
||||
|
||||
public event Action<AttributeSection> EnterAttributeSection, LeaveAttributeSection; |
||||
|
||||
void IAstVisitor.VisitAttributeSection(AttributeSection attributeSection) |
||||
{ |
||||
Visit(EnterAttributeSection, LeaveAttributeSection, attributeSection); |
||||
} |
||||
|
||||
public event Action<DelegateDeclaration> EnterDelegateDeclaration, LeaveDelegateDeclaration; |
||||
|
||||
void IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) |
||||
{ |
||||
Visit(EnterDelegateDeclaration, LeaveDelegateDeclaration, delegateDeclaration); |
||||
} |
||||
|
||||
public event Action<NamespaceDeclaration> EnterNamespaceDeclaration, LeaveNamespaceDeclaration; |
||||
|
||||
void IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) |
||||
{ |
||||
Visit(EnterNamespaceDeclaration, LeaveNamespaceDeclaration, namespaceDeclaration); |
||||
} |
||||
|
||||
public event Action<TypeDeclaration> EnterTypeDeclaration, LeaveTypeDeclaration; |
||||
|
||||
void IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration) |
||||
{ |
||||
Visit(EnterTypeDeclaration, LeaveTypeDeclaration, typeDeclaration); |
||||
} |
||||
|
||||
public event Action<TypeParameterDeclaration> EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration; |
||||
|
||||
void IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) |
||||
{ |
||||
Visit(EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration, typeParameterDeclaration); |
||||
} |
||||
|
||||
public event Action<EnumMemberDeclaration> EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration; |
||||
|
||||
void IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) |
||||
{ |
||||
Visit(EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration, enumMemberDeclaration); |
||||
} |
||||
|
||||
public event Action<UsingDeclaration> EnterUsingDeclaration, LeaveUsingDeclaration; |
||||
|
||||
void IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration) |
||||
{ |
||||
Visit(EnterUsingDeclaration, LeaveUsingDeclaration, usingDeclaration); |
||||
} |
||||
|
||||
public event Action<UsingAliasDeclaration> EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration; |
||||
|
||||
void IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) |
||||
{ |
||||
Visit(EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration, usingDeclaration); |
||||
} |
||||
|
||||
public event Action<ExternAliasDeclaration> EnterExternAliasDeclaration, LeaveExternAliasDeclaration; |
||||
|
||||
void IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) |
||||
{ |
||||
Visit(EnterExternAliasDeclaration, LeaveExternAliasDeclaration, externAliasDeclaration); |
||||
} |
||||
|
||||
public event Action<ConstructorDeclaration> EnterConstructorDeclaration, LeaveConstructorDeclaration; |
||||
|
||||
void IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) |
||||
{ |
||||
Visit(EnterConstructorDeclaration, LeaveConstructorDeclaration, constructorDeclaration); |
||||
} |
||||
|
||||
public event Action<ConstructorInitializer> EnterConstructorInitializer, LeaveConstructorInitializer; |
||||
|
||||
void IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer) |
||||
{ |
||||
Visit(EnterConstructorInitializer, LeaveConstructorInitializer, constructorInitializer); |
||||
} |
||||
|
||||
public event Action<DestructorDeclaration> EnterDestructorDeclaration, LeaveDestructorDeclaration; |
||||
|
||||
void IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) |
||||
{ |
||||
Visit(EnterDestructorDeclaration, LeaveDestructorDeclaration, destructorDeclaration); |
||||
} |
||||
|
||||
public event Action<EventDeclaration> EnterEventDeclaration, LeaveEventDeclaration; |
||||
|
||||
void IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) |
||||
{ |
||||
Visit(EnterEventDeclaration, LeaveEventDeclaration, eventDeclaration); |
||||
} |
||||
|
||||
public event Action<CustomEventDeclaration> EnterCustomEventDeclaration, LeaveCustomEventDeclaration; |
||||
|
||||
void IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) |
||||
{ |
||||
Visit(EnterCustomEventDeclaration, LeaveCustomEventDeclaration, eventDeclaration); |
||||
} |
||||
|
||||
public event Action<FieldDeclaration> EnterFieldDeclaration, LeaveFieldDeclaration; |
||||
|
||||
void IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) |
||||
{ |
||||
Visit(EnterFieldDeclaration, LeaveFieldDeclaration, fieldDeclaration); |
||||
} |
||||
|
||||
public event Action<FixedFieldDeclaration> EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration; |
||||
|
||||
void IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) |
||||
{ |
||||
Visit(EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration, fixedFieldDeclaration); |
||||
} |
||||
|
||||
public event Action<FixedVariableInitializer> EnterFixedVariableInitializer, LeaveFixedVariableInitializer; |
||||
|
||||
void IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) |
||||
{ |
||||
Visit(EnterFixedVariableInitializer, LeaveFixedVariableInitializer, fixedVariableInitializer); |
||||
} |
||||
|
||||
public event Action<IndexerDeclaration> EnterIndexerDeclaration, LeaveIndexerDeclaration; |
||||
|
||||
void IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) |
||||
{ |
||||
Visit(EnterIndexerDeclaration, LeaveIndexerDeclaration, indexerDeclaration); |
||||
} |
||||
|
||||
public event Action<MethodDeclaration> EnterMethodDeclaration, LeaveMethodDeclaration; |
||||
|
||||
void IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) |
||||
{ |
||||
Visit(EnterMethodDeclaration, LeaveMethodDeclaration, methodDeclaration); |
||||
} |
||||
|
||||
public event Action<OperatorDeclaration> EnterOperatorDeclaration, LeaveOperatorDeclaration; |
||||
|
||||
void IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) |
||||
{ |
||||
Visit(EnterOperatorDeclaration, LeaveOperatorDeclaration, operatorDeclaration); |
||||
} |
||||
|
||||
public event Action<PropertyDeclaration> EnterPropertyDeclaration, LeavePropertyDeclaration; |
||||
|
||||
void IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) |
||||
{ |
||||
Visit(EnterPropertyDeclaration, LeavePropertyDeclaration, propertyDeclaration); |
||||
} |
||||
|
||||
public event Action<Accessor> EnterAccessor, LeaveAccessor; |
||||
|
||||
void IAstVisitor.VisitAccessor(Accessor accessor) |
||||
{ |
||||
Visit(EnterAccessor, LeaveAccessor, accessor); |
||||
} |
||||
|
||||
public event Action<VariableInitializer> EnterVariableInitializer, LeaveVariableInitializer; |
||||
|
||||
void IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer) |
||||
{ |
||||
Visit(EnterVariableInitializer, LeaveVariableInitializer, variableInitializer); |
||||
} |
||||
|
||||
public event Action<ParameterDeclaration> EnterParameterDeclaration, LeaveParameterDeclaration; |
||||
|
||||
void IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) |
||||
{ |
||||
Visit(EnterParameterDeclaration, LeaveParameterDeclaration, parameterDeclaration); |
||||
} |
||||
|
||||
public event Action<Constraint> EnterConstraint, LeaveConstraint; |
||||
|
||||
void IAstVisitor.VisitConstraint(Constraint constraint) |
||||
{ |
||||
Visit(EnterConstraint, LeaveConstraint, constraint); |
||||
} |
||||
|
||||
public event Action<BlockStatement> EnterBlockStatement, LeaveBlockStatement; |
||||
|
||||
void IAstVisitor.VisitBlockStatement(BlockStatement blockStatement) |
||||
{ |
||||
Visit(EnterBlockStatement, LeaveBlockStatement, blockStatement); |
||||
} |
||||
|
||||
public event Action<ExpressionStatement> EnterExpressionStatement, LeaveExpressionStatement; |
||||
|
||||
void IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) |
||||
{ |
||||
Visit(EnterExpressionStatement, LeaveExpressionStatement, expressionStatement); |
||||
} |
||||
|
||||
public event Action<BreakStatement> EnterBreakStatement, LeaveBreakStatement; |
||||
|
||||
void IAstVisitor.VisitBreakStatement(BreakStatement breakStatement) |
||||
{ |
||||
Visit(EnterBreakStatement, LeaveBreakStatement, breakStatement); |
||||
} |
||||
|
||||
public event Action<CheckedStatement> EnterCheckedStatement, LeaveCheckedStatement; |
||||
|
||||
void IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement) |
||||
{ |
||||
Visit(EnterCheckedStatement, LeaveCheckedStatement, checkedStatement); |
||||
} |
||||
|
||||
public event Action<ContinueStatement> EnterContinueStatement, LeaveContinueStatement; |
||||
|
||||
void IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement) |
||||
{ |
||||
Visit(EnterContinueStatement, LeaveContinueStatement, continueStatement); |
||||
} |
||||
|
||||
public event Action<DoWhileStatement> EnterDoWhileStatement, LeaveDoWhileStatement; |
||||
|
||||
void IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement) |
||||
{ |
||||
Visit(EnterDoWhileStatement, LeaveDoWhileStatement, doWhileStatement); |
||||
} |
||||
|
||||
public event Action<EmptyStatement> EnterEmptyStatement, LeaveEmptyStatement; |
||||
|
||||
void IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) |
||||
{ |
||||
Visit(EnterEmptyStatement, LeaveEmptyStatement, emptyStatement); |
||||
} |
||||
|
||||
public event Action<FixedStatement> EnterFixedStatement, LeaveFixedStatement; |
||||
|
||||
void IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) |
||||
{ |
||||
Visit(EnterFixedStatement, LeaveFixedStatement, fixedStatement); |
||||
} |
||||
|
||||
public event Action<ForeachStatement> EnterForeachStatement, LeaveForeachStatement; |
||||
|
||||
void IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement) |
||||
{ |
||||
Visit(EnterForeachStatement, LeaveForeachStatement, foreachStatement); |
||||
} |
||||
|
||||
public event Action<ForStatement> EnterForStatement, LeaveForStatement; |
||||
|
||||
void IAstVisitor.VisitForStatement(ForStatement forStatement) |
||||
{ |
||||
Visit(EnterForStatement, LeaveForStatement, forStatement); |
||||
} |
||||
|
||||
public event Action<GotoCaseStatement> EnterGotoCaseStatement, LeaveGotoCaseStatement; |
||||
|
||||
void IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) |
||||
{ |
||||
Visit(EnterGotoCaseStatement, LeaveGotoCaseStatement, gotoCaseStatement); |
||||
} |
||||
|
||||
public event Action<GotoDefaultStatement> EnterGotoDefaultStatement, LeaveGotoDefaultStatement; |
||||
|
||||
void IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) |
||||
{ |
||||
Visit(EnterGotoDefaultStatement, LeaveGotoDefaultStatement, gotoDefaultStatement); |
||||
} |
||||
|
||||
public event Action<GotoStatement> EnterGotoStatement, LeaveGotoStatement; |
||||
|
||||
void IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement) |
||||
{ |
||||
Visit(EnterGotoStatement, LeaveGotoStatement, gotoStatement); |
||||
} |
||||
|
||||
public event Action<IfElseStatement> EnterIfElseStatement, LeaveIfElseStatement; |
||||
|
||||
void IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement) |
||||
{ |
||||
Visit(EnterIfElseStatement, LeaveIfElseStatement, ifElseStatement); |
||||
} |
||||
|
||||
public event Action<LabelStatement> EnterLabelStatement, LeaveLabelStatement; |
||||
|
||||
void IAstVisitor.VisitLabelStatement(LabelStatement labelStatement) |
||||
{ |
||||
Visit(EnterLabelStatement, LeaveLabelStatement, labelStatement); |
||||
} |
||||
|
||||
public event Action<LockStatement> EnterLockStatement, LeaveLockStatement; |
||||
|
||||
void IAstVisitor.VisitLockStatement(LockStatement lockStatement) |
||||
{ |
||||
Visit(EnterLockStatement, LeaveLockStatement, lockStatement); |
||||
} |
||||
|
||||
public event Action<ReturnStatement> EnterReturnStatement, LeaveReturnStatement; |
||||
|
||||
void IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement) |
||||
{ |
||||
Visit(EnterReturnStatement, LeaveReturnStatement, returnStatement); |
||||
} |
||||
|
||||
public event Action<SwitchStatement> EnterSwitchStatement, LeaveSwitchStatement; |
||||
|
||||
void IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement) |
||||
{ |
||||
Visit(EnterSwitchStatement, LeaveSwitchStatement, switchStatement); |
||||
} |
||||
|
||||
public event Action<SwitchSection> EnterSwitchSection, LeaveSwitchSection; |
||||
|
||||
void IAstVisitor.VisitSwitchSection(SwitchSection switchSection) |
||||
{ |
||||
Visit(EnterSwitchSection, LeaveSwitchSection, switchSection); |
||||
} |
||||
|
||||
public event Action<CaseLabel> EnterCaseLabel, LeaveCaseLabel; |
||||
|
||||
void IAstVisitor.VisitCaseLabel(CaseLabel caseLabel) |
||||
{ |
||||
Visit(EnterCaseLabel, LeaveCaseLabel, caseLabel); |
||||
} |
||||
|
||||
public event Action<ThrowStatement> EnterThrowStatement, LeaveThrowStatement; |
||||
|
||||
void IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement) |
||||
{ |
||||
Visit(EnterThrowStatement, LeaveThrowStatement, throwStatement); |
||||
} |
||||
|
||||
public event Action<TryCatchStatement> EnterTryCatchStatement, LeaveTryCatchStatement; |
||||
|
||||
void IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) |
||||
{ |
||||
Visit(EnterTryCatchStatement, LeaveTryCatchStatement, tryCatchStatement); |
||||
} |
||||
|
||||
public event Action<CatchClause> EnterCatchClause, LeaveCatchClause; |
||||
|
||||
void IAstVisitor.VisitCatchClause(CatchClause catchClause) |
||||
{ |
||||
Visit(EnterCatchClause, LeaveCatchClause, catchClause); |
||||
} |
||||
|
||||
public event Action<UncheckedStatement> EnterUncheckedStatement, LeaveUncheckedStatement; |
||||
|
||||
void IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement) |
||||
{ |
||||
Visit(EnterUncheckedStatement, LeaveUncheckedStatement, uncheckedStatement); |
||||
} |
||||
|
||||
public event Action<UnsafeStatement> EnterUnsafeStatement, LeaveUnsafeStatement; |
||||
|
||||
void IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement) |
||||
{ |
||||
Visit(EnterUnsafeStatement, LeaveUnsafeStatement, unsafeStatement); |
||||
} |
||||
|
||||
public event Action<UsingStatement> EnterUsingStatement, LeaveUsingStatement; |
||||
|
||||
void IAstVisitor.VisitUsingStatement(UsingStatement usingStatement) |
||||
{ |
||||
Visit(EnterUsingStatement, LeaveUsingStatement, usingStatement); |
||||
} |
||||
|
||||
public event Action<VariableDeclarationStatement> EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement; |
||||
|
||||
void IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) |
||||
{ |
||||
Visit(EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement, variableDeclarationStatement); |
||||
} |
||||
|
||||
public event Action<WhileStatement> EnterWhileStatement, LeaveWhileStatement; |
||||
|
||||
void IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) |
||||
{ |
||||
Visit(EnterWhileStatement, LeaveWhileStatement, whileStatement); |
||||
} |
||||
|
||||
public event Action<YieldBreakStatement> EnterYieldBreakStatement, LeaveYieldBreakStatement; |
||||
|
||||
void IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) |
||||
{ |
||||
Visit(EnterYieldBreakStatement, LeaveYieldBreakStatement, yieldBreakStatement); |
||||
} |
||||
|
||||
public event Action<YieldReturnStatement> EnterYieldReturnStatement, LeaveYieldReturnStatement; |
||||
|
||||
void IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement) |
||||
{ |
||||
Visit(EnterYieldReturnStatement, LeaveYieldReturnStatement, yieldStatement); |
||||
} |
||||
|
||||
public event Action<AnonymousMethodExpression> EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression; |
||||
|
||||
void IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) |
||||
{ |
||||
Visit(EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression, anonymousMethodExpression); |
||||
} |
||||
|
||||
public event Action<LambdaExpression> EnterLambdaExpression, LeaveLambdaExpression; |
||||
|
||||
void IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression) |
||||
{ |
||||
Visit(EnterLambdaExpression, LeaveLambdaExpression, lambdaExpression); |
||||
} |
||||
|
||||
public event Action<AssignmentExpression> EnterAssignmentExpression, LeaveAssignmentExpression; |
||||
|
||||
void IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression) |
||||
{ |
||||
Visit(EnterAssignmentExpression, LeaveAssignmentExpression, assignmentExpression); |
||||
} |
||||
|
||||
public event Action<BaseReferenceExpression> EnterBaseReferenceExpression, LeaveBaseReferenceExpression; |
||||
|
||||
void IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) |
||||
{ |
||||
Visit(EnterBaseReferenceExpression, LeaveBaseReferenceExpression, baseReferenceExpression); |
||||
} |
||||
|
||||
public event Action<BinaryOperatorExpression> EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression; |
||||
|
||||
void IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) |
||||
{ |
||||
Visit(EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression, binaryOperatorExpression); |
||||
} |
||||
|
||||
public event Action<CastExpression> EnterCastExpression, LeaveCastExpression; |
||||
|
||||
void IAstVisitor.VisitCastExpression(CastExpression castExpression) |
||||
{ |
||||
Visit(EnterCastExpression, LeaveCastExpression, castExpression); |
||||
} |
||||
|
||||
public event Action<CheckedExpression> EnterCheckedExpression, LeaveCheckedExpression; |
||||
|
||||
void IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression) |
||||
{ |
||||
Visit(EnterCheckedExpression, LeaveCheckedExpression, checkedExpression); |
||||
} |
||||
|
||||
public event Action<ConditionalExpression> EnterConditionalExpression, LeaveConditionalExpression; |
||||
|
||||
void IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression) |
||||
{ |
||||
Visit(EnterConditionalExpression, LeaveConditionalExpression, conditionalExpression); |
||||
} |
||||
|
||||
public event Action<IdentifierExpression> EnterIdentifierExpression, LeaveIdentifierExpression; |
||||
|
||||
void IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression) |
||||
{ |
||||
Visit(EnterIdentifierExpression, LeaveIdentifierExpression, identifierExpression); |
||||
} |
||||
|
||||
public event Action<IndexerExpression> EnterIndexerExpression, LeaveIndexerExpression; |
||||
|
||||
void IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression) |
||||
{ |
||||
Visit(EnterIndexerExpression, LeaveIndexerExpression, indexerExpression); |
||||
} |
||||
|
||||
public event Action<InvocationExpression> EnterInvocationExpression, LeaveInvocationExpression; |
||||
|
||||
void IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression) |
||||
{ |
||||
Visit(EnterInvocationExpression, LeaveInvocationExpression, invocationExpression); |
||||
} |
||||
|
||||
public event Action<DirectionExpression> EnterDirectionExpression, LeaveDirectionExpression; |
||||
|
||||
void IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression) |
||||
{ |
||||
Visit(EnterDirectionExpression, LeaveDirectionExpression, directionExpression); |
||||
} |
||||
|
||||
public event Action<MemberReferenceExpression> EnterMemberReferenceExpression, LeaveMemberReferenceExpression; |
||||
|
||||
void IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) |
||||
{ |
||||
Visit(EnterMemberReferenceExpression, LeaveMemberReferenceExpression, memberReferenceExpression); |
||||
} |
||||
|
||||
public event Action<NullReferenceExpression> EnterNullReferenceExpression, LeaveNullReferenceExpression; |
||||
|
||||
void IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) |
||||
{ |
||||
Visit(EnterNullReferenceExpression, LeaveNullReferenceExpression, nullReferenceExpression); |
||||
} |
||||
|
||||
public event Action<ObjectCreateExpression> EnterObjectCreateExpression, LeaveObjectCreateExpression; |
||||
|
||||
void IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) |
||||
{ |
||||
Visit(EnterObjectCreateExpression, LeaveObjectCreateExpression, objectCreateExpression); |
||||
} |
||||
|
||||
public event Action<OutVarDeclarationExpression> EnterOutVarDeclarationExpression, LeaveOutVarDeclarationExpression; |
||||
|
||||
void IAstVisitor.VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression) |
||||
{ |
||||
Visit(EnterOutVarDeclarationExpression, LeaveOutVarDeclarationExpression, outVarDeclarationExpression); |
||||
} |
||||
|
||||
public event Action<AnonymousTypeCreateExpression> EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression; |
||||
|
||||
void IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) |
||||
{ |
||||
Visit(EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression, anonymousTypeCreateExpression); |
||||
} |
||||
|
||||
public event Action<ArrayCreateExpression> EnterArrayCreateExpression, LeaveArrayCreateExpression; |
||||
|
||||
void IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arraySCreateExpression) |
||||
{ |
||||
Visit(EnterArrayCreateExpression, LeaveArrayCreateExpression, arraySCreateExpression); |
||||
} |
||||
|
||||
public event Action<ParenthesizedExpression> EnterParenthesizedExpression, LeaveParenthesizedExpression; |
||||
|
||||
void IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) |
||||
{ |
||||
Visit(EnterParenthesizedExpression, LeaveParenthesizedExpression, parenthesizedExpression); |
||||
} |
||||
|
||||
public event Action<PointerReferenceExpression> EnterPointerReferenceExpression, LeavePointerReferenceExpression; |
||||
|
||||
void IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) |
||||
{ |
||||
Visit(EnterPointerReferenceExpression, LeavePointerReferenceExpression, pointerReferenceExpression); |
||||
} |
||||
|
||||
public event Action<PrimitiveExpression> EnterPrimitiveExpression, LeavePrimitiveExpression; |
||||
|
||||
void IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) |
||||
{ |
||||
Visit(EnterPrimitiveExpression, LeavePrimitiveExpression, primitiveExpression); |
||||
} |
||||
|
||||
public event Action<SizeOfExpression> EnterSizeOfExpression, LeaveSizeOfExpression; |
||||
|
||||
void IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) |
||||
{ |
||||
Visit(EnterSizeOfExpression, LeaveSizeOfExpression, sizeOfExpression); |
||||
} |
||||
|
||||
public event Action<StackAllocExpression> EnterStackAllocExpression, LeaveStackAllocExpression; |
||||
|
||||
void IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) |
||||
{ |
||||
Visit(EnterStackAllocExpression, LeaveStackAllocExpression, stackAllocExpression); |
||||
} |
||||
|
||||
public event Action<ThisReferenceExpression> EnterThisReferenceExpression, LeaveThisReferenceExpression; |
||||
|
||||
void IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) |
||||
{ |
||||
Visit(EnterThisReferenceExpression, LeaveThisReferenceExpression, thisReferenceExpression); |
||||
} |
||||
|
||||
public event Action<TypeOfExpression> EnterTypeOfExpression, LeaveTypeOfExpression; |
||||
|
||||
void IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression) |
||||
{ |
||||
Visit(EnterTypeOfExpression, LeaveTypeOfExpression, typeOfExpression); |
||||
} |
||||
|
||||
public event Action<TypeReferenceExpression> EnterTypeReferenceExpression, LeaveTypeReferenceExpression; |
||||
|
||||
void IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) |
||||
{ |
||||
Visit(EnterTypeReferenceExpression, LeaveTypeReferenceExpression, typeReferenceExpression); |
||||
} |
||||
|
||||
public event Action<UnaryOperatorExpression> EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression; |
||||
|
||||
void IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) |
||||
{ |
||||
Visit(EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression, unaryOperatorExpression); |
||||
} |
||||
|
||||
public event Action<UncheckedExpression> EnterUncheckedExpression, LeaveUncheckedExpression; |
||||
|
||||
void IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression) |
||||
{ |
||||
Visit(EnterUncheckedExpression, LeaveUncheckedExpression, uncheckedExpression); |
||||
} |
||||
|
||||
public event Action<QueryExpression> EnterQueryExpression, LeaveQueryExpression; |
||||
|
||||
void IAstVisitor.VisitQueryExpression(QueryExpression queryExpression) |
||||
{ |
||||
Visit(EnterQueryExpression, LeaveQueryExpression, queryExpression); |
||||
} |
||||
|
||||
public event Action<QueryContinuationClause> EnterQueryContinuationClause, LeaveQueryContinuationClause; |
||||
|
||||
void IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) |
||||
{ |
||||
Visit(EnterQueryContinuationClause, LeaveQueryContinuationClause, queryContinuationClause); |
||||
} |
||||
|
||||
public event Action<QueryFromClause> EnterQueryFromClause, LeaveQueryFromClause; |
||||
|
||||
void IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause) |
||||
{ |
||||
Visit(EnterQueryFromClause, LeaveQueryFromClause, queryFromClause); |
||||
} |
||||
|
||||
public event Action<QueryLetClause> EnterQueryLetClause, LeaveQueryLetClause; |
||||
|
||||
void IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause) |
||||
{ |
||||
Visit(EnterQueryLetClause, LeaveQueryLetClause, queryLetClause); |
||||
} |
||||
|
||||
public event Action<QueryWhereClause> EnterQueryWhereClause, LeaveQueryWhereClause; |
||||
|
||||
void IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause) |
||||
{ |
||||
Visit(EnterQueryWhereClause, LeaveQueryWhereClause, queryWhereClause); |
||||
} |
||||
|
||||
public event Action<QueryJoinClause> EnterQueryJoinClause, LeaveQueryJoinClause; |
||||
|
||||
void IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause) |
||||
{ |
||||
Visit(EnterQueryJoinClause, LeaveQueryJoinClause, queryJoinClause); |
||||
} |
||||
|
||||
public event Action<QueryOrderClause> EnterQueryOrderClause, LeaveQueryOrderClause; |
||||
|
||||
void IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause) |
||||
{ |
||||
Visit(EnterQueryOrderClause, LeaveQueryOrderClause, queryOrderClause); |
||||
} |
||||
|
||||
public event Action<QueryOrdering> EnterQueryOrdering, LeaveQueryOrdering; |
||||
|
||||
void IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering) |
||||
{ |
||||
Visit(EnterQueryOrdering, LeaveQueryOrdering, queryOrdering); |
||||
} |
||||
|
||||
public event Action<QuerySelectClause> EnterQuerySelectClause, LeaveQuerySelectClause; |
||||
|
||||
void IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause) |
||||
{ |
||||
Visit(EnterQuerySelectClause, LeaveQuerySelectClause, querySelectClause); |
||||
} |
||||
|
||||
public event Action<QueryGroupClause> EnterQueryGroupClause, LeaveQueryGroupClause; |
||||
|
||||
void IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause) |
||||
{ |
||||
Visit(EnterQueryGroupClause, LeaveQueryGroupClause, queryGroupClause); |
||||
} |
||||
|
||||
public event Action<AsExpression> EnterAsExpression, LeaveAsExpression; |
||||
|
||||
void IAstVisitor.VisitAsExpression(AsExpression asExpression) |
||||
{ |
||||
Visit(EnterAsExpression, LeaveAsExpression, asExpression); |
||||
} |
||||
|
||||
public event Action<IsExpression> EnterIsExpression, LeaveIsExpression; |
||||
|
||||
void IAstVisitor.VisitIsExpression(IsExpression isExpression) |
||||
{ |
||||
Visit(EnterIsExpression, LeaveIsExpression, isExpression); |
||||
} |
||||
|
||||
public event Action<DefaultValueExpression> EnterDefaultValueExpression, LeaveDefaultValueExpression; |
||||
|
||||
void IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) |
||||
{ |
||||
Visit(EnterDefaultValueExpression, LeaveDefaultValueExpression, defaultValueExpression); |
||||
} |
||||
|
||||
public event Action<UndocumentedExpression> EnterUndocumentedExpression, LeaveUndocumentedExpression; |
||||
|
||||
void IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) |
||||
{ |
||||
Visit(EnterUndocumentedExpression, LeaveUndocumentedExpression, undocumentedExpression); |
||||
} |
||||
|
||||
public event Action<ArrayInitializerExpression> EnterArrayInitializerExpression, LeaveArrayInitializerExpression; |
||||
|
||||
void IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) |
||||
{ |
||||
Visit(EnterArrayInitializerExpression, LeaveArrayInitializerExpression, arrayInitializerExpression); |
||||
} |
||||
|
||||
public event Action<ArraySpecifier> EnterArraySpecifier, LeaveArraySpecifier; |
||||
|
||||
void IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier) |
||||
{ |
||||
Visit(EnterArraySpecifier, LeaveArraySpecifier, arraySpecifier); |
||||
} |
||||
|
||||
public event Action<NamedArgumentExpression> EnterNamedArgumentExpression, LeaveNamedArgumentExpression; |
||||
|
||||
void IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) |
||||
{ |
||||
Visit(EnterNamedArgumentExpression, LeaveNamedArgumentExpression, namedArgumentExpression); |
||||
} |
||||
|
||||
public event Action<NamedExpression> EnterNamedExpression, LeaveNamedExpression; |
||||
|
||||
void IAstVisitor.VisitNamedExpression(NamedExpression namedExpression) |
||||
{ |
||||
Visit(EnterNamedExpression, LeaveNamedExpression, namedExpression); |
||||
} |
||||
|
||||
void IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) |
||||
{ |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue