// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; using System.Reflection; namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { /// /// Base class for variable details /// public class VariableDetails { /// /// Type of variable /// public Type Type = typeof(Unreached); /// /// Name of variable (can be set by ssa) /// public string Name; /// /// Real name of variable /// public string RealName; /// /// Value of variable /// public object Constant = Unreached.UnreachedConstant; /// /// Block that variable is initialized in /// public Block DefinedBlock; /// /// Returns true if the two variable details have the same constant and same type /// /// /// /// public static bool SameConstantAndType(VariableDetails varDetailsA, VariableDetails varDetailsB) { if (varDetailsA == null) { if (varDetailsB != null) { return false; } return true; } return ((varDetailsA.Constant == null && varDetailsB.Constant == null) || (varDetailsA.Constant != null && varDetailsA.Constant.Equals(varDetailsB.Constant))) && varDetailsA.Type == varDetailsB.Type; } } /// /// Class that stores the details of variable analysis /// public class VariableAnalysisDetails : VariableDetails { internal VariableAnalysisDetails() { this.AssociatedAsts = new List(); } /// /// The Asts associated with the variables /// public List AssociatedAsts { get; internal set; } internal List AssignedBlocks = new List(); } /// /// This class is used to find all the variables in an ast /// public class FindAllVariablesVisitor : AstVisitor { /// /// Visit an Ast and gets details about all the variables /// /// /// public static Dictionary Visit(Ast ast) { if (!(ast is ScriptBlockAst || ast is FunctionMemberAst || ast is FunctionDefinitionAst)) { return null; } var visitor = new FindAllVariablesVisitor(); visitor.InitializeVariables(ast); // Visit the body before the parameters so we don't allocate any tuple slots for parameters // if we won't be optimizing because of a call to new-variable/remove-variable, etc. if (ast is ScriptBlockAst) { (ast as ScriptBlockAst).Visit(visitor); } else if (ast is FunctionMemberAst) { (ast as FunctionMemberAst).Body.Visit(visitor); } else if (ast is FunctionDefinitionAst) { (ast as FunctionDefinitionAst).Body.Visit(visitor); } if (ast is FunctionMemberAst && (ast as FunctionMemberAst).Parameters != null) { visitor.VisitParameters((ast as FunctionMemberAst).Parameters); } else if (ast is FunctionDefinitionAst && (ast as FunctionDefinitionAst).Parameters != null) { visitor.VisitParameters((ast as FunctionDefinitionAst).Parameters); } return visitor._variables; } internal readonly Dictionary _variables = new Dictionary(StringComparer.OrdinalIgnoreCase); internal void InitializeVariables(Ast ast) { _variables.Add("true", new VariableAnalysisDetails { Name = "true", RealName = "true", Type = typeof(bool) }); _variables.Add("false", new VariableAnalysisDetails { Name = "false", RealName = "true", Type = typeof(bool) }); if (ast is FunctionMemberAst) { TypeDefinitionAst psClass = AssignmentTarget.FindClassAncestor(ast); if (psClass != null) { _variables.Add("this", new VariableAnalysisDetails { Name = "this", RealName = "this", Constant = SpecialVars.ThisVariable }); } } } internal void VisitParameters(ReadOnlyCollection parameters) { foreach (ParameterAst t in parameters) { var variableExpressionAst = t.Name; var varPath = variableExpressionAst.VariablePath; var variableName = AssignmentTarget.GetUnaliasedVariableName(varPath); VariableAnalysisDetails analysisDetails; if (_variables.TryGetValue(variableName, out analysisDetails)) { // Forget whatever type we deduced in the body, we'll revisit that type after walking // the flow graph. We should see the parameter type for the variable first. analysisDetails.Type = t.StaticType; } else { NoteVariable(variableName, t.StaticType); } } } /// /// Visit datastatement /// /// /// public override AstVisitAction VisitDataStatement(DataStatementAst dataStatementAst) { if (dataStatementAst != null && dataStatementAst.Variable != null) { NoteVariable(dataStatementAst.Variable, null); } return AstVisitAction.Continue; } /// /// Visit SwitchStatement /// /// /// public override AstVisitAction VisitSwitchStatement(SwitchStatementAst switchStatementAst) { NoteVariable(SpecialVars.@switch, typeof(IEnumerator)); return AstVisitAction.Continue; } /// /// Visit Foreach statement /// /// /// public override AstVisitAction VisitForEachStatement(ForEachStatementAst forEachStatementAst) { NoteVariable(SpecialVars.@foreach, typeof(IEnumerator)); return AstVisitAction.Continue; } /// /// Visit VariableExpression /// /// /// public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) { if (variableExpressionAst == null) { return AstVisitAction.Continue; } NoteVariable(AssignmentTarget.GetUnaliasedVariableName(variableExpressionAst.VariablePath), null); return AstVisitAction.Continue; } /// /// Visit UsingExpression /// /// /// public override AstVisitAction VisitUsingExpression(UsingExpressionAst usingExpressionAst) { // On the local machine, we may have set the index because of a call to ScriptBlockToPowerShell or Invoke-Command. // On the remote machine, the index probably isn't set yet, so we set it here, mostly to avoid another pass // over the ast. We assert below to ensure we're setting to the same value in both the local and remote cases. // Cannot access the RuntimeUsingIndex //if (usingExpressionAst.RuntimeUsingIndex == -1) //{ // usingExpressionAst.RuntimeUsingIndex = _runtimeUsingIndex; //} //System.Diagnostics.Debug.Assert(usingExpressionAst.RuntimeUsingIndex == _runtimeUsingIndex, "Logic error in visiting using expressions."); return AstVisitAction.Continue; } /// /// Visit Command /// /// /// public override AstVisitAction VisitCommand(CommandAst commandAst) { return AstVisitAction.Continue; } /// /// Visit Function /// /// /// public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { // We don't want to discover any variables in nested functions - they get their own scope. return AstVisitAction.SkipChildren; } /// /// Visit ScriptBlock /// /// /// public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst) { // We don't want to discover any variables in script block expressions - they get their own scope. return AstVisitAction.Continue; } /// /// Visit Trap /// /// /// public override AstVisitAction VisitTrap(TrapStatementAst trapStatementAst) { // We don't want to discover any variables in traps - they get their own scope. return AstVisitAction.SkipChildren; } // Return true if the variable is newly allocated and should be allocated in the locals tuple. internal void NoteVariable(string variableName, Type type) { if (!_variables.ContainsKey(variableName)) { var details = new VariableAnalysisDetails { Name = variableName, Type = type }; _variables.Add(variableName, details); } } } class LoopGotoTargets { internal LoopGotoTargets(string label, Block breakTarget, Block continueTarget) { this.Label = label; this.BreakTarget = breakTarget; this.ContinueTarget = continueTarget; } internal string Label { get; private set; } internal Block BreakTarget { get; private set; } internal Block ContinueTarget { get; private set; } } /// /// Represents unreached variable /// public class Unreached { internal static object UnreachedConstant = new object(); } /// /// Represent undetermined variable /// public class Undetermined { internal static object UndeterminedConstant = new object(); } /// /// Class that represents a Phi function /// public class Phi : VariableDetails { internal VariableTarget[] Operands; /// /// Constructor /// /// /// internal Phi(String VarName, Block block) { Name = VarName; Operands = new VariableTarget[block._predecessors.Count()]; for (int i = 0; i < Operands.Length; i += 1) { Operands[i] = new VariableTarget(); } RealName = Name; DefinedBlock = block; } } /// /// A block in the data flow graph /// public class Block { /// /// Asts in the block /// public LinkedList _asts = new LinkedList(); internal readonly List _successors = new List(); /// /// Predecessor blocks /// public List _predecessors = new List(); internal readonly HashSet SSASuccessors = new HashSet(); internal List DominatorSuccessors = new List(); internal static int count; internal int PostOrder; internal HashSet dominanceFrontierSet = new HashSet(); internal List Phis = new List(); private static Dictionary> SSADictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); internal static Dictionary VariablesDictionary; internal static Dictionary InternalVariablesDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary Counters = new Dictionary(); internal object _visitData; internal bool _visited; internal bool _throws; internal bool _returns; internal bool _unreachable; // Only Entry block, that can be constructed via NewEntryBlock() is reachable initially. // all other blocks are unreachable. // reachability of block should be proved with FlowsTo() calls. public Block() { this._unreachable = true; } public static Block NewEntryBlock() { return new Block(unreachable: false); } private Block(bool unreachable) { this._unreachable = unreachable; } /// /// Tell flow analysis that this block can flow to next block. /// /// internal void FlowsTo(Block next) { if (_successors.IndexOf(next) < 0) { if (!_unreachable) { next._unreachable = false; } _successors.Add(next); next._predecessors.Add(this); } } internal void AddAst(object ast) { _asts.AddLast(ast); } internal void AddFirstAst(object ast) { _asts.AddFirst(ast); } /// /// Insert phi nodes at dominance frontier of each set. /// /// internal static void InsertPhiNodes(Dictionary Variables) { foreach (var variable in Variables.Keys.ToList()) { List everOnWorkList = new List(); List workList = new List(); List hasPhiAlready = new List(); everOnWorkList.AddRange(Variables[variable].AssignedBlocks); workList.AddRange(Variables[variable].AssignedBlocks); while (workList.Count != 0) { Block block = workList[workList.Count - 1]; workList.RemoveAt(workList.Count - 1); foreach (Block frontier in block.dominanceFrontierSet) { if (!hasPhiAlready.Contains(frontier)) { // INSERT PHI FUNCTION frontier.Phis.Add(new Phi(variable, frontier)); hasPhiAlready.Add(frontier); } if (!everOnWorkList.Contains(frontier)) { workList.Add(frontier); everOnWorkList.Add(frontier); } } } } } /// /// Fill in the DominanceFrontiersSet of each block. /// /// /// internal static void DominanceFrontiers(List Blocks, Block Entry) { Block[] dominators = SetDominators(Entry, Blocks); foreach (Block block in Blocks) { if (block._predecessors.Count >= 2) { foreach (Block pred in block._predecessors) { Block runner = pred; while (runner != dominators[block.PostOrder]) { runner.dominanceFrontierSet.Add(block); runner = dominators[block.PostOrder]; } } } } } /// /// Returns the immediate dominator of each block. The array returned is /// indexed by postorder number of each block. /// Based on https://www.cs.rice.edu/~keith/Embed/dom.pdf /// /// /// /// internal static Block[] SetDominators(Block entry, List Blocks) { Block[] dominators = new Block[Blocks.Count]; foreach (var block in Blocks) { dominators[block.PostOrder] = null; } dominators[entry.PostOrder] = entry; bool updated = true; while (updated) { updated = false; foreach (var block in Blocks) { if (block == entry) { continue; } if (block._predecessors.Count == 0) { continue; } Block dom = block._predecessors[0]; // Get first processed node foreach (var pred in block._predecessors) { if (dominators[pred.PostOrder] != null) { dom = pred; break; } } Block firstSelected = dom; foreach (var pred in block._predecessors) { if (firstSelected != pred && dominators[pred.PostOrder] != null) { // The order is reversed so we have to subtract from total dom = Blocks[Blocks.Count - Intersect(pred, dom, dominators) - 1]; } } if (dominators[block.PostOrder] != dom) { dominators[block.PostOrder] = dom; updated = true; } } } // Construct dominator tree foreach (var block in Blocks) { dominators[block.PostOrder].DominatorSuccessors.Add(block); } return dominators; } /// /// Given two nodes, return the postorder number of the closest node /// that dominates both. /// /// /// /// /// internal static int Intersect(Block block1, Block block2, Block[] Dominators) { int b1 = block1.PostOrder; int b2 = block2.PostOrder; while (b1 != b2) { while (b1 < b2) { b1 = Dominators[b1].PostOrder; } while (b2 < b1) { b2 = Dominators[b2].PostOrder; } } return b1; } /// /// Generate new name for each assignment using the counters. /// /// /// /// internal static string GenerateNewName(string VarName, Block DefinedBlock) { int i = Counters[VarName]; Counters[VarName] += 1; string newName = String.Format(CultureInfo.CurrentCulture, "{0}{1}", VarName, i); var varAnalysis = new VariableAnalysisDetails { Name = newName, DefinedBlock = DefinedBlock, RealName = VarName }; SSADictionary[VarName].Push(varAnalysis); InternalVariablesDictionary[newName] = varAnalysis; return newName; } /// /// Rename each variable (each assignment will give variable a new name). /// Also rename the phi function in each block as well /// /// internal static void RenameVariables(Block block) { if (block._visited) { return; } block._visited = true; // foreach phi node generate new name foreach (var phi in block.Phis) { phi.Name = GenerateNewName(phi.RealName, block); } foreach (object ast in block._asts) { AssignmentTarget assignTarget = ast as AssignmentTarget; VariableTarget varTarget = null; if (assignTarget != null && !String.IsNullOrEmpty(assignTarget.Name)) { assignTarget.Name = GenerateNewName(assignTarget.Name, block); varTarget = assignTarget._rightHandSideVariable; } varTarget = (varTarget != null) ? varTarget : ast as VariableTarget; if (varTarget != null) { // If stack is empty then variable was not initialized; if (SSADictionary[varTarget.RealName].Count == 0) { VariablesDictionary[VariableAnalysis.AnalysisDictionaryKey(varTarget.VarAst)] = new VariableAnalysisDetails { Name = varTarget.Name, DefinedBlock = null, Type = varTarget.Type, RealName = null }; } else { VariableAnalysisDetails previous = SSADictionary[varTarget.RealName].Peek(); varTarget.Name = previous.Name; if (previous.DefinedBlock != null) { previous.DefinedBlock.SSASuccessors.Add(block); } VariablesDictionary[VariableAnalysis.AnalysisDictionaryKey(varTarget.VarAst)] = previous; } } } foreach (var successor in block._successors) { int index = successor._predecessors.IndexOf(block); foreach (var phi in successor.Phis) { // Stacks may be empty when variable was not initialized on all paths; if (SSADictionary[phi.RealName].Count != 0) { VariableAnalysisDetails previous = SSADictionary[phi.RealName].Peek(); if (previous.DefinedBlock != null) { previous.DefinedBlock.SSASuccessors.Add(successor); } phi.Operands[index] = new VariableTarget(previous); } else { phi.Operands[index] = new VariableTarget(); phi.Operands[index].Name = GenerateNewName(phi.RealName, null); } } } // Preorder travel along the dominator tree foreach (var successor in block.DominatorSuccessors) { RenameVariables(successor); } foreach (var phi in block.Phis) { SSADictionary[phi.RealName].Pop(); } foreach (object ast in block._asts) { AssignmentTarget assignTarget = ast as AssignmentTarget; if (assignTarget != null && !String.IsNullOrEmpty(assignTarget.RealName)) { SSADictionary[assignTarget.RealName].Pop(); } } } /// /// Initialize SSA /// /// /// /// internal static void InitializeSSA(Dictionary VariableAnalysis, Block Entry, List Blocks) { VariablesDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var block in Blocks) { List _unreachables = new List(); foreach (var pred in block._predecessors) { if (pred._unreachable) { _unreachables.Add(pred); pred._successors.Remove(block); } } foreach (var pred in _unreachables) { block._predecessors.Remove(pred); } } InternalVariablesDictionary.Clear(); SSADictionary.Clear(); Counters.Clear(); DominanceFrontiers(Blocks, Entry); InsertPhiNodes(VariableAnalysis); foreach (var key in VariableAnalysis.Keys.ToList()) { SSADictionary[key] = new Stack(); Counters[key] = 0; } RenameVariables(Entry); } /// /// Sparse simple constant algorithm using use-def chain from SSA graph. /// /// /// /// /// internal static Tuple, Dictionary> SparseSimpleConstants( Dictionary Variables, Block Entry, List Classes) { List blocks = GenerateReverseDepthFirstOrder(Entry); // Populate unreached variable with type from variables foreach (var block in blocks) { foreach (var ast in block._asts) { VariableTarget varTarget = (ast is AssignmentTarget) ? (ast as AssignmentTarget)._rightHandSideVariable : (ast as VariableTarget); if (varTarget != null && varTarget.Type == typeof(Unreached) && Variables.ContainsKey(varTarget.Name)) { varTarget.Type = Variables[varTarget.Name].Type; } } } InitializeSSA(Variables, Entry, blocks); LinkedList> workLists = new LinkedList>(); foreach (var block in blocks) { // Add a worklist from a block to itself to force analysis when variable is initialized but not used. // This is useful in the case where the variable is used in the inner function workLists.AddLast(Tuple.Create(block, block)); foreach (var ssaSucc in block.SSASuccessors) { if (ssaSucc != block) { workLists.AddLast(Tuple.Create(block, ssaSucc)); } } } // Initialize variables in internal dictionary to undetermined foreach (var key in InternalVariablesDictionary.Keys.ToList()) { InternalVariablesDictionary[key].Constant = Undetermined.UndeterminedConstant; InternalVariablesDictionary[key].Type = typeof(Undetermined); } // Initialize DefinedBlock of phi function. If it is null then that phi function is not initialized; foreach (var block in blocks) { foreach (var phi in block.Phis) { foreach (var operand in phi.Operands) { phi.DefinedBlock = operand.DefinedBlock; if (phi.DefinedBlock == null) { break; } } InternalVariablesDictionary[phi.Name].DefinedBlock = phi.DefinedBlock; } } while (workLists.Count != 0) { Tuple SSAEdge = workLists.Last.Value; workLists.RemoveLast(); Block start = SSAEdge.Item1; Block end = SSAEdge.Item2; HashSet defVariables = new HashSet(); foreach (var obj in start._asts) { var assigned = obj as AssignmentTarget; if (assigned != null && assigned.Name != null && InternalVariablesDictionary.ContainsKey(assigned.Name)) { VariableAnalysisDetails varAnalysis = InternalVariablesDictionary[assigned.Name]; // For cases where the type or constant of the rhs is initialized not through assignment. if (assigned._rightHandSideVariable != null && !InternalVariablesDictionary.ContainsKey(assigned._rightHandSideVariable.Name) && Variables.ContainsKey(assigned._rightHandSideVariable.Name)) { VariableAnalysisDetails rhsAnalysis = Variables[assigned._rightHandSideVariable.Name]; assigned.Type = rhsAnalysis.Type; assigned.Constant = rhsAnalysis.Constant; } varAnalysis.Constant = assigned.Constant; varAnalysis.Type = assigned.Type; defVariables.Add(assigned.Name); } } foreach (var phi in start.Phis) { EvaluatePhiConstant(phi); EvaluatePhiType(phi); defVariables.Add(phi.Name); } bool updated = false; List varTargets = new List(); varTargets.AddRange(end._asts.Where(item => item is VariableTarget).Cast()); foreach (var phi in end.Phis) { varTargets.AddRange(phi.Operands); } foreach (var varTarget in varTargets) { if (defVariables.Contains(varTarget.Name)) { var analysisDetails = InternalVariablesDictionary[varTarget.Name]; if (!VariableDetails.SameConstantAndType(varTarget, analysisDetails)) { updated = true; } varTarget.Constant = analysisDetails.Constant; varTarget.Type = analysisDetails.Type; VariablesDictionary[VariableAnalysis.AnalysisDictionaryKey(varTarget.VarAst)] = InternalVariablesDictionary[varTarget.Name]; } } foreach (var ast in end._asts) { var assigned = ast as AssignmentTarget; if (assigned != null && assigned.Name != null && assigned._leftHandSideVariable != null) { // Handle cases like $b = $a after we found out the value of $a if (assigned._rightHandSideVariable != null && defVariables.Contains(assigned._rightHandSideVariable.Name)) { // Ignore assignments like $a = $a if (!String.Equals(assigned._rightHandSideVariable.Name, assigned.Name, StringComparison.OrdinalIgnoreCase)) { if (!InternalVariablesDictionary.ContainsKey(assigned._rightHandSideVariable.Name)) { continue; } var analysisDetails = InternalVariablesDictionary[assigned._rightHandSideVariable.Name]; if (!VariableDetails.SameConstantAndType(assigned, analysisDetails)) { updated = true; } assigned.Constant = analysisDetails.Constant; assigned.Type = analysisDetails.Type; } continue; } //TODO // Handle cases like $b = $a.SomeMethod or $a.SomeType after we found out the value of $a if (assigned._rightAst != null) { CommandExpressionAst cmeAst = assigned._rightAst as CommandExpressionAst; MemberExpressionAst memAst = (cmeAst != null) ? (cmeAst.Expression as MemberExpressionAst) : null; // Don't handle the this case because this is handled in assignment target if (memAst != null && memAst.Expression is VariableExpressionAst) { VariableAnalysisDetails analysis = VariablesDictionary[VariableAnalysis.AnalysisDictionaryKey(memAst.Expression as VariableExpressionAst)]; TypeDefinitionAst psClass = Classes.FirstOrDefault(item => String.Equals(item.Name, analysis.Type?.FullName, StringComparison.OrdinalIgnoreCase)); Type possibleType = AssignmentTarget.GetTypeFromMemberExpressionAst(memAst, analysis, psClass); if (possibleType != null && possibleType != assigned.Type) { assigned.Type = possibleType; updated = true; continue; } } } } } if (updated) { foreach (var block in end.SSASuccessors) { // Add to the front instead of the end since we are removing from end. workLists.AddFirst(Tuple.Create(end, block)); } } } return Tuple.Create(VariablesDictionary, InternalVariablesDictionary); } /// /// Evaluate the constant value at a phi node /// /// internal static void EvaluatePhiConstant(Phi phi) { int allUnreached = 0; object constantSoFar = null; bool first = true; if (phi.Operands.Length == 0 || phi.DefinedBlock == null) { return; } foreach (var operand in phi.Operands) { var operandDetails = InternalVariablesDictionary[operand.Name]; if (operandDetails.Constant == Undetermined.UndeterminedConstant) { constantSoFar = Undetermined.UndeterminedConstant; break; } else { if (first) { first = false; constantSoFar = operandDetails.Constant; if (constantSoFar == Unreached.UnreachedConstant) { allUnreached += 1; } } else { if (operandDetails.Constant == Unreached.UnreachedConstant) { allUnreached += 1; } else if ((operandDetails.Constant == null && operandDetails.Constant != constantSoFar) || (operandDetails.Constant != null && !operandDetails.Constant.Equals(constantSoFar))) { constantSoFar = Undetermined.UndeterminedConstant; break; } } } } if (allUnreached == phi.Operands.Length) { constantSoFar = Unreached.UnreachedConstant; } InternalVariablesDictionary[phi.Name].Constant = constantSoFar; return; } /// /// Evaluate the type of a phi node. /// /// internal static void EvaluatePhiType(Phi phi) { int allUnreached = 0; Type typeSoFar = null; bool first = true; if (phi.Operands.Length == 0 || phi.DefinedBlock == null) { return; } foreach (var operand in phi.Operands) { var operandDetails = InternalVariablesDictionary[operand.Name]; if (operandDetails.Type == typeof(Undetermined)) { typeSoFar = typeof(Undetermined); break; } else { if (first) { first = false; typeSoFar = operandDetails.Type; if (typeSoFar == typeof(Unreached)) { allUnreached += 1; } } else { if (operandDetails.Type == typeof(Unreached)) { allUnreached += 1; } else if (typeSoFar != operandDetails.Type) { typeSoFar = typeof(Undetermined); break; } } } } if (allUnreached == phi.Operands.Length) { typeSoFar = typeof(Unreached); } InternalVariablesDictionary[phi.Name].Type = typeSoFar; return; } internal static List GenerateReverseDepthFirstOrder(Block block) { count = 0; List result = new List(); VisitDepthFirstOrder(block, result); result.Reverse(); for (int i = 0; i < result.Count; i++) { result[i]._visitData = null; } return result; } internal static void VisitDepthFirstOrder(Block block, List visitData) { if (ReferenceEquals(block._visitData, visitData)) return; block._visitData = visitData; foreach (Block succ in block._successors) { VisitDepthFirstOrder(succ, visitData); } visitData.Add(block); block.PostOrder = count; count += 1; } } /// /// Class used to store a variable /// public class VariableTarget : VariableDetails { internal VariableExpressionAst VarAst; /// /// Constructor that takes in a VariableExpressionAst /// /// public VariableTarget(VariableExpressionAst varExpression) { if (varExpression != null) { Name = AssignmentTarget.GetUnaliasedVariableName(varExpression.VariablePath); VarAst = varExpression; RealName = Name; } } /// /// Constructor that takes in a VariableDetails /// Used for phi operator /// /// public VariableTarget(VariableDetails VarDetails) { if (VarDetails != null) { Name = VarDetails.Name; RealName = VarDetails.RealName; DefinedBlock = VarDetails.DefinedBlock; } } /// /// Empty constructor /// public VariableTarget() { } } class AssignmentTarget : VariableDetails { internal readonly ExpressionAst _targetAst; internal readonly StatementAst _rightAst; internal VariableTarget _rightHandSideVariable; internal VariableExpressionAst _leftHandSideVariable; public AssignmentTarget(ExpressionAst ast) { this._targetAst = ast; SetVariableName(); } public AssignmentTarget(AssignmentStatementAst ast) { this._targetAst = ast.Left; this._rightAst = ast.Right; if (_rightAst != null) { Constant = Undetermined.UndeterminedConstant; Type = typeof(Undetermined); } CommandExpressionAst cmExAst = _rightAst as CommandExpressionAst; if (cmExAst != null) { ExpressionAst exprAst = cmExAst.Expression; Type = exprAst.StaticType; if (exprAst is ConvertExpressionAst) { ConvertExpressionAst convertAst = exprAst as ConvertExpressionAst; Type = DeepestRelatedDerivedClass(convertAst.StaticType, Type); if (convertAst.Child is ConstantExpressionAst) { Constant = (convertAst.Child as ConstantExpressionAst).Value; } } else if (exprAst is BinaryExpressionAst) { BinaryExpressionAst binAst = exprAst as BinaryExpressionAst; if (binAst != null && binAst.Operator == TokenKind.As && binAst.Right is TypeExpressionAst) { Type = DeepestRelatedDerivedClass((binAst.Right as TypeExpressionAst).TypeName.GetReflectionType(), binAst.Left.StaticType); if (binAst.Left is ConstantExpressionAst) { Constant = (binAst.Left as ConstantExpressionAst).Value; } else if (binAst.Left is VariableExpressionAst) { _rightHandSideVariable = new VariableTarget(binAst.Left as VariableExpressionAst); } } } else if (exprAst is ConstantExpressionAst) { Constant = (cmExAst.Expression as ConstantExpressionAst).Value; } else if (exprAst is VariableExpressionAst) { _rightHandSideVariable = new VariableTarget(cmExAst.Expression as VariableExpressionAst); if (String.Equals((exprAst as VariableExpressionAst).VariablePath.UserPath, "this", StringComparison.OrdinalIgnoreCase)) { Constant = SpecialVars.ThisVariable; } } //Store the type info for variable assignment from .Net type else if (exprAst is MemberExpressionAst) { Type = DeepestRelatedDerivedClass(Type, GetTypeFromMemberExpressionAst(exprAst as MemberExpressionAst)); } } // We'll consider case where there is only 1 pipeline element for now else if (_rightAst is PipelineAst && (_rightAst as PipelineAst).PipelineElements.Count == 1) { #region Process New-Object command CommandAst cmdAst = (_rightAst as PipelineAst).PipelineElements[0] as CommandAst; if (cmdAst != null && cmdAst.CommandElements.Count > 1) { StringConstantExpressionAst stringAst = cmdAst.CommandElements[0] as StringConstantExpressionAst; if (stringAst != null && String.Equals(stringAst.Value, "new-object", StringComparison.OrdinalIgnoreCase)) { CommandParameterAst secondElement = cmdAst.CommandElements[1] as CommandParameterAst; StringConstantExpressionAst typeName = null; if (secondElement != null) { if (String.Equals(secondElement.ParameterName, "TypeName", StringComparison.OrdinalIgnoreCase)) { if (secondElement.Argument != null) { typeName = secondElement.Argument as StringConstantExpressionAst; } else { if (cmdAst.CommandElements.Count > 2) { typeName = cmdAst.CommandElements[2] as StringConstantExpressionAst; } } } } else { typeName = cmdAst.CommandElements[1] as StringConstantExpressionAst; } if (typeName != null) { Type = System.Type.GetType(typeName.Value) ?? typeof(object); } } } #endregion } SetVariableName(); } public AssignmentTarget(string variableName, Type type) { this.Name = variableName; this.Type = type; } /// /// Get the type from member expression ast in the form of $variable.field/method /// type is the type of variable. Class is the class that matches the type /// /// /// /// /// internal static Type GetTypeFromMemberExpressionAst(MemberExpressionAst memAst, VariableAnalysisDetails analysis, TypeDefinitionAst psClass) { if (memAst != null && memAst.Expression is VariableExpressionAst && memAst.Member is StringConstantExpressionAst && !String.Equals((memAst.Expression as VariableExpressionAst).VariablePath.UserPath, "this", StringComparison.OrdinalIgnoreCase)) { string fieldName = (memAst.Member as StringConstantExpressionAst).Value; if (psClass == null && analysis.Constant == SpecialVars.ThisVariable) { psClass = AssignmentTarget.FindClassAncestor(memAst); } if (psClass != null) { Type typeFromClass = AssignmentTarget.GetTypeFromClass(psClass, memAst); { if (typeFromClass != null) { return typeFromClass; } } } // If the type is not a ps class or there are some types of the same name. if (analysis != null && analysis.Type != null && analysis.Type != typeof(object) && analysis.Type != typeof(Unreached) && analysis.Type != typeof(Undetermined)) { if (memAst is InvokeMemberExpressionAst) { return AssignmentTarget.GetTypeFromInvokeMemberAst(analysis.Type, memAst as InvokeMemberExpressionAst, fieldName, false); } else { return AssignmentTarget.GetPropertyOrFieldTypeFromMemberExpressionAst(analysis.Type, fieldName); } } } return null; } /// /// Given a memberAst, try to return the type of the expression. This assumes that the memberAst is of the form /// [Type]::method/field or $this.method/field /// /// /// internal static Type GetTypeFromMemberExpressionAst(MemberExpressionAst memberAst) { Type result = null; StringConstantExpressionAst stringAst = memberAst.Member as StringConstantExpressionAst; if (stringAst == null) { return result; } if (memberAst is InvokeMemberExpressionAst) { #region RHS is InvokeMemberExpressionAst InvokeMemberExpressionAst imeAst = memberAst as InvokeMemberExpressionAst; if (imeAst.Expression is TypeExpressionAst) { string methodName = stringAst.Value; Type type = (imeAst.Expression as TypeExpressionAst).TypeName.GetReflectionType(); if (String.Equals(methodName, "new", StringComparison.OrdinalIgnoreCase)) { result = type; } else if (type != null && type != typeof(object)) { // isStatic is true result = GetTypeFromInvokeMemberAst(type, imeAst, methodName, true); } else { // Check for classes TypeDefinitionAst psClass = FindClass(memberAst, (imeAst.Expression as TypeExpressionAst).TypeName.FullName); if (psClass != null) { MemberAst funcMemberAst = psClass.Members.FirstOrDefault(item => item is FunctionMemberAst && (item as FunctionMemberAst).IsStatic && String.Equals(item.Name, methodName, StringComparison.OrdinalIgnoreCase)); if (funcMemberAst != null) { result = (funcMemberAst as FunctionMemberAst).ReturnType.TypeName.GetReflectionType(); } } } } #endregion } else { #region RHS is MemberExpressionAst //syntax like $a=[System.AppDomain]::CurrentDomain string fieldName = stringAst.Value; if (memberAst.Expression is TypeExpressionAst) { Type expressionType = (memberAst.Expression is TypeExpressionAst) ? (memberAst.Expression as TypeExpressionAst).TypeName.GetReflectionType() : null; if (expressionType != null) { result = GetPropertyOrFieldTypeFromMemberExpressionAst(expressionType, fieldName); } else { // check for class type TypeDefinitionAst psClass = FindClass(memberAst, (memberAst.Expression as TypeExpressionAst).TypeName.FullName); if (psClass != null) { MemberAst memAst = psClass.Members.FirstOrDefault(item => String.Equals(item.Name, fieldName, StringComparison.OrdinalIgnoreCase)); if (memAst != null && memAst is PropertyMemberAst && (memAst as PropertyMemberAst).IsStatic) { result = (memAst as PropertyMemberAst).PropertyType.TypeName.GetReflectionType(); } } } } #endregion } if (result != null) { return result; } #region RHS contains $this variable // For the case where variable is $this if (memberAst.Expression is VariableExpressionAst && String.Equals((memberAst.Expression as VariableExpressionAst).VariablePath.UserPath, "this", StringComparison.OrdinalIgnoreCase)) { // Check that we are in a class TypeDefinitionAst psClass = FindClassAncestor(memberAst); // Is static is false for this case result = GetTypeFromClass(psClass, memberAst); } return result; #endregion } /// /// Get the type from an invoke member expression ast /// /// /// /// /// /// internal static Type GetTypeFromInvokeMemberAst(Type type, InvokeMemberExpressionAst imeAst, string methodName, bool isStatic) { Type result = null; MethodInfo[] methods = (isStatic) ? type.GetMethods(BindingFlags.Public | BindingFlags.Static) : type.GetMethods(BindingFlags.Public | BindingFlags.Instance); int argCounts = (imeAst.Arguments != null) ? imeAst.Arguments.Count : 0; MethodInfo[] possibleMethods = methods.Where(method => String.Equals(method.Name, methodName, StringComparison.OrdinalIgnoreCase) && method.GetParameters().Length == argCounts).ToArray(); if (possibleMethods.Length != 0) { Type first = possibleMethods[0].ReturnType; if (first != typeof(void) && first != null && possibleMethods.All(method => method.ReturnType == first)) { result = first; } } return result; } internal static Type GetPropertyOrFieldTypeFromMemberExpressionAst(Type type, string fieldName) { PropertyInfo property = type.GetProperty(fieldName); Type result = null; if (property != null) { result = property.PropertyType; } else { FieldInfo field = type.GetField(fieldName); if (field != null) { result = field.FieldType; } } return result; } /// /// Checks whether a class with the name name exists in the script that contains ast /// /// /// /// internal static TypeDefinitionAst FindClass(Ast ast, string name) { Ast parent = ast.Parent; while (parent.Parent != null) { parent = parent.Parent; } Ast classAst = parent.Find(item => item is TypeDefinitionAst && String.Equals((item as TypeDefinitionAst).Name, name, StringComparison.OrdinalIgnoreCase), true); TypeDefinitionAst psClass = (classAst != null) ? (classAst as TypeDefinitionAst) : null; if (psClass != null && psClass.IsClass) { return psClass; } return null; } /// /// Finds the closest class ancestor of ast /// /// /// internal static TypeDefinitionAst FindClassAncestor(Ast ast) { Ast parent = ast.Parent; TypeDefinitionAst psClass = null; while (parent != null) { if (parent is TypeDefinitionAst) { psClass = parent as TypeDefinitionAst; break; } parent = parent.Parent; } if (psClass != null && psClass.IsClass) { return psClass; } return null; } /// /// Get the type for memberexpressionast assuming that the variable is a class /// /// /// /// internal static Type GetTypeFromClass(TypeDefinitionAst psClass, MemberExpressionAst memberExpressionAst) { Type result = null; if (psClass != null) { MemberAst memAst = psClass.Members.FirstOrDefault(item => String.Equals(item.Name, (memberExpressionAst.Member as StringConstantExpressionAst).Value, StringComparison.OrdinalIgnoreCase)); if (memAst != null) { if (memAst is PropertyMemberAst) { result = (memAst as PropertyMemberAst).PropertyType.TypeName.GetReflectionType(); } else if (memAst is FunctionMemberAst) { result = (memAst as FunctionMemberAst).ReturnType.TypeName.GetReflectionType(); } } } return result; } private void SetVariableName() { ExpressionAst lhs = (_targetAst is ConvertExpressionAst) ? (_targetAst as ConvertExpressionAst).Child : _targetAst; _leftHandSideVariable = lhs as VariableExpressionAst; if (_leftHandSideVariable == null) { return; } Name = GetUnaliasedVariableName(_leftHandSideVariable.VariablePath); RealName = Name; } /// /// Given 2 types, return the type that is the subclass of both. /// /// /// /// public static Type DeepestRelatedDerivedClass(Type FirstType, Type SecondType) { if (FirstType == null || SecondType == null) { return typeof(object); } if (FirstType.GetTypeInfo().IsSubclassOf(SecondType) || FirstType == SecondType) { return FirstType; } else if (SecondType.GetTypeInfo().IsSubclassOf(FirstType)) { return SecondType; } return typeof(object); } internal static string GetUnaliasedVariableName(string varName) { return varName.Equals(SpecialVars.PSItem, StringComparison.OrdinalIgnoreCase) ? SpecialVars.Underbar : varName; } [Flags] internal enum VariablePathFlags { None = 0x00, Local = 0x01, Script = 0x02, Global = 0x04, Private = 0x08, Variable = 0x10, Function = 0x20, DriveQualified = 0x40, Unqualified = 0x80, // If any of these bits are set, the path does not represent an unscoped variable. UnscopedVariableMask = Local | Script | Global | Private | Function | DriveQualified, } internal static string GetUnaliasedVariableName(VariablePath varPath) { VariablePathFlags knownFlags = VariablePathFlags.None; string path = varPath.ToString(); int currentCharIndex = 0; int lastScannedColon = -1; string candidateScope = null; string candidateScopeUpper = null; string unqualifiedPath; VariablePathFlags _flags = VariablePathFlags.None; VariablePathFlags candidateFlags = VariablePathFlags.Unqualified; if (varPath.IsDriveQualified) { knownFlags = VariablePathFlags.DriveQualified; } scanScope: switch (path[0]) { case 'g': case 'G': candidateScope = "lobal"; candidateScopeUpper = "LOBAL"; candidateFlags = VariablePathFlags.Global; break; case 'l': case 'L': candidateScope = "ocal"; candidateScopeUpper = "OCAL"; candidateFlags = VariablePathFlags.Local; break; case 'p': case 'P': candidateScope = "rivate"; candidateScopeUpper = "RIVATE"; candidateFlags = VariablePathFlags.Private; break; case 's': case 'S': candidateScope = "cript"; candidateScopeUpper = "CRIPT"; candidateFlags = VariablePathFlags.Script; break; case 'v': case 'V': if (knownFlags == VariablePathFlags.None) { // If we see 'variable:', our namespaceId will be empty, and // we'll also need to scan for the scope again. candidateScope = "ariable"; candidateScopeUpper = "ARIABLE"; candidateFlags = VariablePathFlags.Variable; } break; } if (candidateScope != null) { currentCharIndex += 1; // First character already matched. int j; for (j = 0; currentCharIndex < path.Length && j < candidateScope.Length; ++j, ++currentCharIndex) { if (path[currentCharIndex] != candidateScope[j] && path[currentCharIndex] != candidateScopeUpper[j]) { break; } } if (j == candidateScope.Length && currentCharIndex < path.Length && path[currentCharIndex] == ':') { if (_flags == VariablePathFlags.None) { _flags = VariablePathFlags.Variable; } _flags |= candidateFlags; lastScannedColon = currentCharIndex; currentCharIndex += 1; // If saw 'variable:', we need to look for a scope after 'variable:'. if (candidateFlags == VariablePathFlags.Variable) { knownFlags = VariablePathFlags.Variable; candidateScope = candidateScopeUpper = null; candidateFlags = VariablePathFlags.None; goto scanScope; } } } if (_flags == VariablePathFlags.None) { lastScannedColon = path.IndexOf(':', currentCharIndex); // No colon, or a colon as the first character means we have // a simple variable, otherwise it's a drive. if (lastScannedColon > 0) { _flags = VariablePathFlags.DriveQualified; } } if (lastScannedColon == -1) { unqualifiedPath = path; } else { unqualifiedPath = path.Substring(lastScannedColon + 1); } if (_flags == VariablePathFlags.None) { _flags = VariablePathFlags.Unqualified | VariablePathFlags.Variable; } return GetUnaliasedVariableName(unqualifiedPath); } } /// /// interface /// public interface IFlowGraph : ICustomAstVisitor { /// void Init(); /// void Start(); /// void Stop(); /// Block Current { get; set; } /// Block Entry { get; set; } /// Block Exit { get; set; } /// IFlowGraph Decorator { get; set; } } /// /// Data flow graph /// public class FlowGraph : IFlowGraph { internal Block _entryBlock; internal Block _exitBlock; internal Block _currentBlock; internal readonly List _loopTargets = new List(); /// /// Start the data flow graph. /// public void Start() { Init(); _currentBlock = _entryBlock; } /// /// Stop the data flow graph. /// public void Stop() { _currentBlock.FlowsTo(_exitBlock); } /// /// The current block /// public Block Current { get { return _currentBlock; } set { _currentBlock = value; } } /// /// The entry to the data flow /// public Block Entry { get { return _entryBlock; } set { _entryBlock = value; } } /// /// The exit of the data flow /// public Block Exit { get { return _exitBlock; } set { _exitBlock = value; } } /// /// Initialize the data flow construction /// public void Init() { _entryBlock = Block.NewEntryBlock(); _exitBlock = new Block(); } /// /// The decorator /// public IFlowGraph Decorator { get; set; } /// /// Visit error statement /// /// /// public object VisitErrorStatement(ErrorStatementAst errorStatementAst) { return null; } /// /// Visit error expression /// /// /// public object VisitErrorExpression(ErrorExpressionAst errorExpressionAst) { return null; } /// /// Visit script block /// /// /// public object VisitScriptBlock(ScriptBlockAst scriptBlockAst) { if (scriptBlockAst == null) return null; _currentBlock = _entryBlock; if (scriptBlockAst.DynamicParamBlock != null) { scriptBlockAst.DynamicParamBlock.Visit(this.Decorator); } if (scriptBlockAst.BeginBlock != null) { scriptBlockAst.BeginBlock.Visit(this.Decorator); } if (scriptBlockAst.ProcessBlock != null) { scriptBlockAst.ProcessBlock.Visit(this.Decorator); } if (scriptBlockAst.EndBlock != null) { scriptBlockAst.EndBlock.Visit(this.Decorator); } _currentBlock.FlowsTo(_exitBlock); return null; } /// /// Visit param block /// /// /// public object VisitParamBlock(ParamBlockAst paramBlockAst) { return null; } /// /// Visit named block /// /// /// public object VisitNamedBlock(NamedBlockAst namedBlockAst) { if (namedBlockAst == null) return null; // Don't visit traps - they get their own scope return VisitStatementBlock(namedBlockAst.Statements); } /// /// Visit type constraint /// /// /// public object VisitTypeConstraint(TypeConstraintAst typeConstraintAst) { System.Diagnostics.Debug.Assert(false, "Code is unreachable"); return null; } /// /// Visit attribute /// /// /// public object VisitAttribute(AttributeAst attributeAst) { System.Diagnostics.Debug.Assert(false, "Code is unreachable"); return null; } /// /// Visit named attribute /// /// /// public object VisitNamedAttributeArgument(NamedAttributeArgumentAst namedAttributeArgumentAst) { System.Diagnostics.Debug.Assert(false, "Code is unreachable"); return null; } /// /// Visit parameter /// /// /// public object VisitParameter(ParameterAst parameterAst) { // Nothing to do now, we've already allocated parameters in the first pass looking for all variable names. System.Diagnostics.Debug.Assert(false, "Code is unreachable"); return null; } /// /// Visit function /// /// /// public object VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { // Don't recurse into the function definition, it's variables are distinct from the script block // we're currently analyzing. return null; } /// /// Visit statementblock /// /// /// public object VisitStatementBlock(StatementBlockAst statementBlockAst) { if (statementBlockAst == null) return null; // Don't visit traps - they get their own scope return VisitStatementBlock(statementBlockAst.Statements); } /// /// Visit list of statement /// /// /// object VisitStatementBlock(ReadOnlyCollection statements) { foreach (var stmt in statements) { stmt.Visit(this.Decorator); } return null; } /// /// Visit if statement /// /// /// public object VisitIfStatement(IfStatementAst ifStmtAst) { if (ifStmtAst == null) return null; Block afterStmt = new Block(); if (ifStmtAst.ElseClause == null) { // There is no else, flow can go straight to afterStmt. _currentBlock.FlowsTo(afterStmt); } int clauseCount = ifStmtAst.Clauses.Count; for (int i = 0; i < clauseCount; i++) { var clause = ifStmtAst.Clauses[i]; bool isLastClause = (i == (clauseCount - 1) && ifStmtAst.ElseClause == null); Block clauseBlock = new Block(); Block nextBlock = isLastClause ? afterStmt : new Block(); clause.Item1.Visit(this); _currentBlock.FlowsTo(clauseBlock); _currentBlock.FlowsTo(nextBlock); _currentBlock = clauseBlock; clause.Item2.Visit(this); _currentBlock.FlowsTo(afterStmt); _currentBlock = nextBlock; } if (ifStmtAst.ElseClause != null) { ifStmtAst.ElseClause.Visit(this); _currentBlock.FlowsTo(afterStmt); } _currentBlock = afterStmt; return null; } /// /// Visit trap /// /// /// public object VisitTrap(TrapStatementAst trapStatementAst) { if (trapStatementAst == null) return null; trapStatementAst.Body.Visit(this.Decorator); return null; } /// /// Visit switch statement /// /// /// public object VisitSwitchStatement(SwitchStatementAst switchStatementAst) { if (switchStatementAst == null) return null; Action generateCondition = () => { switchStatementAst.Condition.Visit(this.Decorator); // $switch is set after evaluating the condition. _currentBlock.AddAst(new AssignmentTarget(SpecialVars.@switch, typeof(IEnumerator))); }; Action switchBodyGenerator = () => { bool hasDefault = (switchStatementAst.Default != null); Block afterStmt = new Block(); int clauseCount = switchStatementAst.Clauses.Count; for (int i = 0; i < clauseCount; i++) { var clause = switchStatementAst.Clauses[i]; Block clauseBlock = new Block(); bool isLastClause = (i == (clauseCount - 1) && !hasDefault); Block nextBlock = isLastClause ? afterStmt : new Block(); clause.Item1.Visit(this.Decorator); _currentBlock.FlowsTo(nextBlock); _currentBlock.FlowsTo(clauseBlock); _currentBlock = clauseBlock; clause.Item2.Visit(this.Decorator); if (!isLastClause) { _currentBlock.FlowsTo(nextBlock); _currentBlock = nextBlock; } } if (hasDefault) { // If any clause was executed, we skip the default, so there is always a branch over the default. _currentBlock.FlowsTo(afterStmt); switchStatementAst.Default.Visit(this.Decorator); } _currentBlock.FlowsTo(afterStmt); _currentBlock = afterStmt; }; GenerateWhileLoop(switchStatementAst.Label, generateCondition, switchBodyGenerator); return null; } /// /// Visit data statement /// /// /// public object VisitDataStatement(DataStatementAst dataStatementAst) { if (dataStatementAst == null) return null; dataStatementAst.Body.Visit(this.Decorator); if (dataStatementAst.Variable != null) { _currentBlock.AddAst(new AssignmentTarget(dataStatementAst.Variable, null)); } return null; } internal void GenerateWhileLoop(string loopLabel, Action generateCondition, Action generateLoopBody, Ast continueAction = null) { // We model the flow graph like this (if continueAction is null, the first part is slightly different): // goto L // :ContinueTarget // continueAction // :L // if (condition) // { // loop body // // break -> goto BreakTarget // // continue -> goto ContinueTarget // goto ContinueTarget // } // :BreakTarget var continueBlock = new Block(); if (continueAction != null) { var blockAfterContinue = new Block(); // Represent the goto over the condition before the first iteration. _currentBlock.FlowsTo(blockAfterContinue); _currentBlock = continueBlock; continueAction.Visit(this.Decorator); _currentBlock.FlowsTo(blockAfterContinue); _currentBlock = blockAfterContinue; } else { _currentBlock.FlowsTo(continueBlock); _currentBlock = continueBlock; } var bodyBlock = new Block(); var breakBlock = new Block(); // Condition can be null from an uncommon for loop: for() {} if (generateCondition != null) { generateCondition(); _currentBlock.FlowsTo(breakBlock); } _loopTargets.Add(new LoopGotoTargets(loopLabel ?? "", breakBlock, continueBlock)); _currentBlock.FlowsTo(bodyBlock); _currentBlock = bodyBlock; generateLoopBody(); _currentBlock.FlowsTo(continueBlock); _currentBlock = breakBlock; _loopTargets.RemoveAt(_loopTargets.Count - 1); } internal void GenerateDoLoop(LoopStatementAst loopStatement) { // We model the flow graph like this: // :RepeatTarget // loop body // // break -> goto BreakTarget // // continue -> goto ContinueTarget // :ContinueTarget // if (condition) // { // goto RepeatTarget // } // :BreakTarget var continueBlock = new Block(); var bodyBlock = new Block(); var breakBlock = new Block(); var gotoRepeatTargetBlock = new Block(); _loopTargets.Add(new LoopGotoTargets(loopStatement.Label ?? "", breakBlock, continueBlock)); _currentBlock.FlowsTo(bodyBlock); _currentBlock = bodyBlock; loopStatement.Body.Visit(this.Decorator); _currentBlock.FlowsTo(continueBlock); _currentBlock = continueBlock; loopStatement.Condition.Visit(this.Decorator); _currentBlock.FlowsTo(breakBlock); _currentBlock.FlowsTo(gotoRepeatTargetBlock); _currentBlock = gotoRepeatTargetBlock; _currentBlock.FlowsTo(bodyBlock); _currentBlock = breakBlock; _loopTargets.RemoveAt(_loopTargets.Count - 1); } /// /// Visit foreach statement /// /// /// public object VisitForEachStatement(ForEachStatementAst forEachStatementAst) { if (forEachStatementAst == null) return null; var afterFor = new Block(); Action generateCondition = () => { forEachStatementAst.Condition.Visit(this.Decorator); // The loop might not be executed, so add flow around the loop. _currentBlock.FlowsTo(afterFor); // $foreach and the iterator variable are set after evaluating the condition. _currentBlock.AddAst(new AssignmentTarget(SpecialVars.@foreach, typeof(IEnumerator))); _currentBlock.AddAst(new AssignmentTarget(forEachStatementAst.Variable)); }; GenerateWhileLoop(forEachStatementAst.Label, generateCondition, () => forEachStatementAst.Body.Visit(this.Decorator)); _currentBlock.FlowsTo(afterFor); _currentBlock = afterFor; return null; } /// /// Visit do while statement /// /// /// public object VisitDoWhileStatement(DoWhileStatementAst doWhileStatementAst) { GenerateDoLoop(doWhileStatementAst); return null; } /// /// Visit do until statement /// /// /// public object VisitDoUntilStatement(DoUntilStatementAst doUntilStatementAst) { GenerateDoLoop(doUntilStatementAst); return null; } /// /// Visit for statement /// /// /// public object VisitForStatement(ForStatementAst forStatementAst) { if (forStatementAst == null) return null; if (forStatementAst.Initializer != null) { forStatementAst.Initializer.Visit(this.Decorator); } var generateCondition = forStatementAst.Condition != null ? () => forStatementAst.Condition.Visit(this.Decorator) : (Action)null; GenerateWhileLoop(forStatementAst.Label, generateCondition, () => forStatementAst.Body.Visit(this.Decorator), forStatementAst.Iterator); return null; } /// /// Visit while statement /// /// /// public object VisitWhileStatement(WhileStatementAst whileStatementAst) { if (whileStatementAst == null) return null; GenerateWhileLoop(whileStatementAst.Label, () => whileStatementAst.Condition.Visit(this.Decorator), () => whileStatementAst.Body.Visit(this.Decorator)); return null; } /// /// Visit catch clause /// /// /// public object VisitCatchClause(CatchClauseAst catchClauseAst) { if (catchClauseAst == null) return null; catchClauseAst.Body.Visit(this.Decorator); return null; } /// /// Visit try statement /// /// /// public object VisitTryStatement(TryStatementAst tryStatementAst) { if (tryStatementAst == null) return null; // We don't attempt to accurately model flow in a try catch because every statement // can flow to each catch. Instead, we'll assume the try block is not executed (because the very first statement // may throw), and have the data flow assume the block before the try is all that can reach the catches and finally. var blockBeforeTry = _currentBlock; _currentBlock = new Block(); blockBeforeTry.FlowsTo(_currentBlock); tryStatementAst.Body.Visit(this.Decorator); Block lastBlockInTry = _currentBlock; var finallyFirstBlock = tryStatementAst.Finally == null ? null : new Block(); Block finallyLastBlock = null; // This is the first block after all the catches and finally (if present). var afterTry = new Block(); bool isCatchAllPresent = false; foreach (var catchAst in tryStatementAst.CatchClauses) { if (catchAst.IsCatchAll) { isCatchAllPresent = true; } // Any statement in the try block could throw and reach the catch, so assume the worst (from a data // flow perspective) and make the predecessor to the catch the block before entering the try. _currentBlock = new Block(); blockBeforeTry.FlowsTo(_currentBlock); catchAst.Visit(this.Decorator); _currentBlock.FlowsTo(finallyFirstBlock ?? afterTry); } if (finallyFirstBlock != null) { lastBlockInTry.FlowsTo(finallyFirstBlock); _currentBlock = finallyFirstBlock; tryStatementAst.Finally.Visit(this.Decorator); _currentBlock.FlowsTo(afterTry); finallyLastBlock = _currentBlock; // For finally block, there are 2 cases: when try-body throw and when it doesn't. // For these two cases value of 'finallyLastBlock._throws' would be different. if (!isCatchAllPresent) { // This flow exist only, if there is no catch for all exceptions. blockBeforeTry.FlowsTo(finallyFirstBlock); var rethrowAfterFinallyBlock = new Block(); finallyLastBlock.FlowsTo(rethrowAfterFinallyBlock); rethrowAfterFinallyBlock._throws = true; rethrowAfterFinallyBlock.FlowsTo(_exitBlock); } // This flow always exists. finallyLastBlock.FlowsTo(afterTry); } else { lastBlockInTry.FlowsTo(afterTry); } _currentBlock = afterTry; return null; } void BreakOrContinue(ExpressionAst label, Func fieldSelector) { Block targetBlock = null; if (label != null) { label.Visit(this.Decorator); if (_loopTargets.Any()) { var labelStrAst = label as StringConstantExpressionAst; if (labelStrAst != null) { targetBlock = (from t in _loopTargets where t.Label.Equals(labelStrAst.Value, StringComparison.OrdinalIgnoreCase) select fieldSelector(t)).LastOrDefault(); } } } else if (_loopTargets.Count > 0) { targetBlock = fieldSelector(_loopTargets.Last()); } if (targetBlock == null) { _currentBlock.FlowsTo(_exitBlock); _currentBlock._throws = true; } else { _currentBlock.FlowsTo(targetBlock); } // The next block is unreachable, but is necessary to keep the flow graph correct. _currentBlock = new Block(); } /// /// Visit break statement /// /// /// public object VisitBreakStatement(BreakStatementAst breakStatementAst) { if (breakStatementAst == null) return null; BreakOrContinue(breakStatementAst.Label, t => t.BreakTarget); return null; } /// /// Visit continue statement /// /// /// public object VisitContinueStatement(ContinueStatementAst continueStatementAst) { if (continueStatementAst == null) return null; BreakOrContinue(continueStatementAst.Label, t => t.ContinueTarget); return null; } internal Block ControlFlowStatement(PipelineBaseAst pipelineAst) { if (pipelineAst != null) { pipelineAst.Visit(this.Decorator); } _currentBlock.FlowsTo(_exitBlock); var lastBlockInStatement = _currentBlock; // The next block is unreachable, but is necessary to keep the flow graph correct. _currentBlock = new Block(); return lastBlockInStatement; } /// /// Visit return statement /// /// /// public object VisitReturnStatement(ReturnStatementAst returnStatementAst) { if (returnStatementAst == null) return null; ControlFlowStatement(returnStatementAst.Pipeline)._returns = true; return null; } /// /// Visit exit statement /// /// /// public object VisitExitStatement(ExitStatementAst exitStatementAst) { if (exitStatementAst == null) return null; ControlFlowStatement(exitStatementAst.Pipeline)._throws = true; return null; } /// /// Visit throw statement /// /// /// public object VisitThrowStatement(ThrowStatementAst throwStatementAst) { if (throwStatementAst == null) return null; ControlFlowStatement(throwStatementAst.Pipeline)._throws = true; return null; } /// /// Visit assignmentstatement /// /// /// public object VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) { if (assignmentStatementAst == null) return null; assignmentStatementAst.Right.Visit(this.Decorator); return null; } /// /// Visit pipeline /// /// /// public object VisitPipeline(PipelineAst pipelineAst) { if (pipelineAst == null) return null; bool invokesCommand = false; foreach (var command in pipelineAst.PipelineElements) { command.Visit(this.Decorator); if (command is CommandAst) { invokesCommand = true; } foreach (var redir in command.Redirections) { redir.Visit(this.Decorator); } } // Because non-local gotos are supported, we must model them in the flow graph. We can't detect them // in general, so we must be pessimistic and assume any command invocation could result in non-local // break or continue, so add the appropriate edges to our graph. These edges occur after visiting // the command elements because command arguments could create new blocks, and we won't have executed // the command yet. if (invokesCommand && _loopTargets.Any()) { foreach (var loopTarget in _loopTargets) { _currentBlock.FlowsTo(loopTarget.BreakTarget); _currentBlock.FlowsTo(loopTarget.ContinueTarget); } // The rest of the block is potentially unreachable, so split the current block. var newBlock = new Block(); _currentBlock.FlowsTo(newBlock); _currentBlock = newBlock; } return null; } /// /// Visit command /// /// /// public object VisitCommand(CommandAst commandAst) { if (commandAst == null) return null; //Add the check for Tee-Object -Variable var if (string.Equals(commandAst.GetCommandName(), "Tee-Object", StringComparison.OrdinalIgnoreCase)) { foreach (CommandElementAst ceAst in commandAst.CommandElements) { if (ceAst is CommandParameterAst) { string paramName = (ceAst as CommandParameterAst).ParameterName; if (string.Equals(paramName, "Variable", StringComparison.OrdinalIgnoreCase)) { int index = commandAst.CommandElements.IndexOf(ceAst); if (commandAst.CommandElements.Count > (index + 1)) { CommandElementAst paramConstant = commandAst.CommandElements[index + 1]; if (paramConstant is StringConstantExpressionAst) { //If common parameters are used, create a variable target and store the variable value _currentBlock.AddAst(new AssignmentTarget((paramConstant as StringConstantExpressionAst).Value, typeof(string))); } } } } } } else { foreach (CommandElementAst ceAst in commandAst.CommandElements) { if (ceAst is CommandParameterAst) { string paramName = (ceAst as CommandParameterAst).ParameterName; if (string.Equals(paramName, "ErrorVariable", StringComparison.OrdinalIgnoreCase) || string.Equals(paramName, "WarningVariable", StringComparison.OrdinalIgnoreCase) || string.Equals(paramName, "PipelineVariable ", StringComparison.OrdinalIgnoreCase) || string.Equals(paramName, "OutVariable", StringComparison.OrdinalIgnoreCase)) { int index = commandAst.CommandElements.IndexOf(ceAst); if (commandAst.CommandElements.Count > (index + 1)) { CommandElementAst paramConstant = commandAst.CommandElements[index + 1]; if (paramConstant is StringConstantExpressionAst) { //If common parameters are used, create a variable target and store the variable value _currentBlock.AddAst(new AssignmentTarget((paramConstant as StringConstantExpressionAst).Value, typeof(string))); } } } } ceAst.Visit(this.Decorator); } } return null; } /// /// Visit command expression /// /// /// public object VisitCommandExpression(CommandExpressionAst commandExpressionAst) { if (commandExpressionAst == null) return null; commandExpressionAst.Expression.Visit(this.Decorator); return null; } /// /// Visit command parameter /// /// /// public object VisitCommandParameter(CommandParameterAst commandParameterAst) { if (commandParameterAst == null) return null; if (commandParameterAst.Argument != null) { commandParameterAst.Argument.Visit(this.Decorator); } return null; } /// /// Visit file direction /// /// /// public object VisitFileRedirection(FileRedirectionAst fileRedirectionAst) { if (fileRedirectionAst == null) return null; fileRedirectionAst.Location.Visit(this.Decorator); return null; } /// /// Visit merging redirection /// /// /// public object VisitMergingRedirection(MergingRedirectionAst mergingRedirectionAst) { return null; } /// /// Visit binary expression /// /// /// public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) { if (binaryExpressionAst == null) return null; if (binaryExpressionAst.Operator == TokenKind.And || binaryExpressionAst.Operator == TokenKind.Or) { // Logical and/or are short circuit operators, so we need to simulate the control flow. The // left operand is always evaluated, visit it's expression in the current block. binaryExpressionAst.Left.Visit(this.Decorator); // The right operand is conditionally evaluated. We aren't generating any code here, just // modeling the flow graph, so we just visit the right operand in a new block, and have // both the current and new blocks both flow to a post-expression block. var targetBlock = new Block(); var nextBlock = new Block(); _currentBlock.FlowsTo(targetBlock); _currentBlock.FlowsTo(nextBlock); _currentBlock = nextBlock; binaryExpressionAst.Right.Visit(this.Decorator); _currentBlock.FlowsTo(targetBlock); _currentBlock = targetBlock; } else { binaryExpressionAst.Left.Visit(this.Decorator); binaryExpressionAst.Right.Visit(this.Decorator); } return null; } /// /// Visit unary expression /// /// /// public object VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst) { if (unaryExpressionAst == null) return null; unaryExpressionAst.Child.Visit(this.Decorator); return null; } /// /// Visit convert expression /// /// /// public object VisitConvertExpression(ConvertExpressionAst convertExpressionAst) { if (convertExpressionAst == null) return null; convertExpressionAst.Child.Visit(this.Decorator); return null; } /// /// Visit constant expression /// /// /// public object VisitConstantExpression(ConstantExpressionAst constantExpressionAst) { return null; } /// /// Visit string constant expression /// /// /// public object VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst) { return null; } /// /// Visit sub expression /// /// /// public object VisitSubExpression(SubExpressionAst subExpressionAst) { if (subExpressionAst == null) return null; subExpressionAst.SubExpression.Visit(this.Decorator); return null; } /// /// Visit using expression /// /// /// public object VisitUsingExpression(UsingExpressionAst usingExpressionAst) { // The SubExpression is not visited, we treat this expression like it is a constant that is replaced // before the script block is executed return null; } /// /// Visit variable expression /// /// /// public object VisitVariableExpression(VariableExpressionAst variableExpressionAst) { return null; } /// /// Visit type expression /// /// /// public object VisitTypeExpression(TypeExpressionAst typeExpressionAst) { return null; } /// /// Visit Member Expression /// /// /// public object VisitMemberExpression(MemberExpressionAst memberExpressionAst) { if (memberExpressionAst == null) return null; memberExpressionAst.Expression.Visit(this.Decorator); memberExpressionAst.Member.Visit(this.Decorator); return null; } /// /// Visit InvokeMemberExpression /// /// /// public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { if (invokeMemberExpressionAst == null) return null; invokeMemberExpressionAst.Expression.Visit(this.Decorator); invokeMemberExpressionAst.Member.Visit(this.Decorator); if (invokeMemberExpressionAst.Arguments != null) { foreach (var arg in invokeMemberExpressionAst.Arguments) { arg.Visit(this.Decorator); } } return null; } /// /// Visit Array Expression /// /// /// public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { if (arrayExpressionAst == null) return null; arrayExpressionAst.SubExpression.Visit(this.Decorator); return null; } /// /// Visit array literal /// /// /// public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst) { if (arrayLiteralAst == null) return null; foreach (var element in arrayLiteralAst.Elements) { element.Visit(this.Decorator); } return null; } /// /// Visit Hash table /// /// /// public object VisitHashtable(HashtableAst hashtableAst) { if (hashtableAst == null) return null; foreach (var pair in hashtableAst.KeyValuePairs) { pair.Item1.Visit(this.Decorator); pair.Item2.Visit(this.Decorator); } return null; } /// /// Visit ScriptBlockExpression /// /// /// public object VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst) { // Don't recurse into the script block, it's variables are distinct from the script block // we're currently analyzing. return null; } /// /// Visit Paren Expression /// /// /// public object VisitParenExpression(ParenExpressionAst parenExpressionAst) { if (parenExpressionAst == null) return null; parenExpressionAst.Pipeline.Visit(this.Decorator); return null; } /// /// Visit Expandable String Expression /// /// /// public object VisitExpandableStringExpression(ExpandableStringExpressionAst expandableStringExpressionAst) { if (expandableStringExpressionAst == null) return null; foreach (var expr in expandableStringExpressionAst.NestedExpressions) { expr.Visit(this.Decorator); } return null; } /// /// Visit Index Expression /// /// /// public object VisitIndexExpression(IndexExpressionAst indexExpressionAst) { if (indexExpressionAst == null) return null; indexExpressionAst.Target.Visit(this.Decorator); indexExpressionAst.Index.Visit(this.Decorator); return null; } /// /// Visit Attributed Expression /// /// /// public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { if (attributedExpressionAst == null) return null; attributedExpressionAst.Child.Visit(this.Decorator); return null; } /// /// Visit Block Statement /// /// /// public object VisitBlockStatement(BlockStatementAst blockStatementAst) { if (blockStatementAst == null) return null; blockStatementAst.Body.Visit(this.Decorator); return null; } } /// /// Analysis base class /// public abstract class AnalysisBase : IFlowGraph { private IFlowGraph _decorated; /// /// Create an analysis base from IFlowgraph decorated. /// /// public AnalysisBase(IFlowGraph decorated) { if (decorated == null) throw new ArgumentNullException("decorated"); this._decorated = decorated; this._decorated.Decorator = this; } /// public virtual void Start() { _decorated.Start(); } /// public virtual void Stop() { _decorated.Stop(); } /// public virtual Block Current { get { return _decorated.Current; } set { _decorated.Current = value; } } /// public virtual Block Entry { get { return _decorated.Entry; } set { _decorated.Entry = value; } } /// public virtual Block Exit { get { return _decorated.Exit; } set { _decorated.Exit = value; } } /// public virtual void Init() { _decorated.Init(); } /// public IFlowGraph Decorator { set { _decorated.Decorator = value; } get { return _decorated.Decorator; } } /// public virtual object VisitErrorStatement(ErrorStatementAst errorStatementAst) { return _decorated.VisitErrorStatement(errorStatementAst); } /// public virtual object VisitErrorExpression(ErrorExpressionAst errorExpressionAst) { return _decorated.VisitErrorExpression(errorExpressionAst); } /// public virtual object VisitScriptBlock(ScriptBlockAst scriptBlockAst) { return _decorated.VisitScriptBlock(scriptBlockAst); } /// public virtual object VisitParamBlock(ParamBlockAst paramBlockAst) { return _decorated.VisitParamBlock(paramBlockAst); } /// public virtual object VisitNamedBlock(NamedBlockAst namedBlockAst) { return _decorated.VisitNamedBlock(namedBlockAst); } /// public virtual object VisitTypeConstraint(TypeConstraintAst typeConstraintAst) { return _decorated.VisitTypeConstraint(typeConstraintAst); } /// public virtual object VisitAttribute(AttributeAst attributeAst) { return _decorated.VisitAttribute(attributeAst); } /// public virtual object VisitNamedAttributeArgument(NamedAttributeArgumentAst namedAttributeArgumentAst) { return _decorated.VisitNamedAttributeArgument(namedAttributeArgumentAst); } /// public virtual object VisitParameter(ParameterAst parameterAst) { return _decorated.VisitParameter(parameterAst); } /// public virtual object VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { return _decorated.VisitFunctionDefinition(functionDefinitionAst); } /// public virtual object VisitStatementBlock(StatementBlockAst statementBlockAst) { return _decorated.VisitStatementBlock(statementBlockAst); } /// public virtual object VisitIfStatement(IfStatementAst ifStmtAst) { return _decorated.VisitIfStatement(ifStmtAst); } /// public virtual object VisitTrap(TrapStatementAst trapStatementAst) { return _decorated.VisitTrap(trapStatementAst); } /// public virtual object VisitSwitchStatement(SwitchStatementAst switchStatementAst) { return _decorated.VisitSwitchStatement(switchStatementAst); } /// public virtual object VisitDataStatement(DataStatementAst dataStatementAst) { return _decorated.VisitDataStatement(dataStatementAst); } /// public virtual object VisitForEachStatement(ForEachStatementAst forEachStatementAst) { return _decorated.VisitForEachStatement(forEachStatementAst); } /// public virtual object VisitDoWhileStatement(DoWhileStatementAst doWhileStatementAst) { return _decorated.VisitDoWhileStatement(doWhileStatementAst); } /// public virtual object VisitDoUntilStatement(DoUntilStatementAst doUntilStatementAst) { return _decorated.VisitDoUntilStatement(doUntilStatementAst); } /// public virtual object VisitForStatement(ForStatementAst forStatementAst) { return _decorated.VisitForStatement(forStatementAst); } /// public virtual object VisitWhileStatement(WhileStatementAst whileStatementAst) { return _decorated.VisitWhileStatement(whileStatementAst); } /// public virtual object VisitCatchClause(CatchClauseAst catchClauseAst) { return _decorated.VisitCatchClause(catchClauseAst); } /// public virtual object VisitTryStatement(TryStatementAst tryStatementAst) { return _decorated.VisitTryStatement(tryStatementAst); } /// public virtual object VisitBreakStatement(BreakStatementAst breakStatementAst) { return _decorated.VisitBreakStatement(breakStatementAst); } /// public virtual object VisitContinueStatement(ContinueStatementAst continueStatementAst) { return _decorated.VisitContinueStatement(continueStatementAst); } /// public virtual object VisitReturnStatement(ReturnStatementAst returnStatementAst) { return _decorated.VisitReturnStatement(returnStatementAst); } /// public virtual object VisitExitStatement(ExitStatementAst exitStatementAst) { return _decorated.VisitExitStatement(exitStatementAst); } /// public virtual object VisitThrowStatement(ThrowStatementAst throwStatementAst) { return _decorated.VisitThrowStatement(throwStatementAst); } /// public virtual object VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) { return _decorated.VisitAssignmentStatement(assignmentStatementAst); } /// public virtual object VisitPipeline(PipelineAst pipelineAst) { return _decorated.VisitPipeline(pipelineAst); } /// public virtual object VisitCommand(CommandAst commandAst) { return _decorated.VisitCommand(commandAst); } /// public virtual object VisitCommandExpression(CommandExpressionAst commandExpressionAst) { return _decorated.VisitCommandExpression(commandExpressionAst); } /// public virtual object VisitCommandParameter(CommandParameterAst commandParameterAst) { return _decorated.VisitCommandParameter(commandParameterAst); } /// public virtual object VisitFileRedirection(FileRedirectionAst fileRedirectionAst) { return _decorated.VisitFileRedirection(fileRedirectionAst); } /// public virtual object VisitMergingRedirection(MergingRedirectionAst mergingRedirectionAst) { return _decorated.VisitMergingRedirection(mergingRedirectionAst); } /// public virtual object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) { return _decorated.VisitBinaryExpression(binaryExpressionAst); } /// public virtual object VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst) { return _decorated.VisitUnaryExpression(unaryExpressionAst); } /// public virtual object VisitConvertExpression(ConvertExpressionAst convertExpressionAst) { return _decorated.VisitConvertExpression(convertExpressionAst); } /// public virtual object VisitConstantExpression(ConstantExpressionAst constantExpressionAst) { return _decorated.VisitConstantExpression(constantExpressionAst); } /// public virtual object VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst) { return _decorated.VisitStringConstantExpression(stringConstantExpressionAst); } /// public virtual object VisitSubExpression(SubExpressionAst subExpressionAst) { return _decorated.VisitSubExpression(subExpressionAst); } /// public virtual object VisitUsingExpression(UsingExpressionAst usingExpressionAst) { return _decorated.VisitUsingExpression(usingExpressionAst); } /// public virtual object VisitVariableExpression(VariableExpressionAst variableExpressionAst) { return _decorated.VisitVariableExpression(variableExpressionAst); } /// public virtual object VisitTypeExpression(TypeExpressionAst typeExpressionAst) { return _decorated.VisitTypeExpression(typeExpressionAst); } /// public virtual object VisitMemberExpression(MemberExpressionAst memberExpressionAst) { return _decorated.VisitMemberExpression(memberExpressionAst); } /// public virtual object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return _decorated.VisitInvokeMemberExpression(invokeMemberExpressionAst); } /// public virtual object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { return _decorated.VisitArrayExpression(arrayExpressionAst); } /// public virtual object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst) { return _decorated.VisitArrayLiteral(arrayLiteralAst); } /// public virtual object VisitHashtable(HashtableAst hashtableAst) { return _decorated.VisitHashtable(hashtableAst); } /// public virtual object VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst) { return _decorated.VisitScriptBlockExpression(scriptBlockExpressionAst); } /// public virtual object VisitParenExpression(ParenExpressionAst parenExpressionAst) { return _decorated.VisitParenExpression(parenExpressionAst); } /// public virtual object VisitExpandableStringExpression(ExpandableStringExpressionAst expandableStringExpressionAst) { return _decorated.VisitExpandableStringExpression(expandableStringExpressionAst); } /// public virtual object VisitIndexExpression(IndexExpressionAst indexExpressionAst) { return _decorated.VisitIndexExpression(indexExpressionAst); } /// public virtual object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { return _decorated.VisitAttributedExpression(attributedExpressionAst); } /// public virtual object VisitBlockStatement(BlockStatementAst blockStatementAst) { return _decorated.VisitBlockStatement(blockStatementAst); } } }