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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
// 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 @@ |
|||||||
//
|
|
||||||
// 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