/// <summary> /// Returns the target variable or signal, or null /// </summary> /// <returns>The target variable or signal.</returns> /// <param name="self">The item to examine.</param> public static DataElement GetTarget(this ASTItem self) { if (self == null) { return(null); } if (self is DataElement) { return((DataElement)self); } if (self is IdentifierExpression) { return(((IdentifierExpression)self).Target); } if (self is MemberReferenceExpression) { return(((MemberReferenceExpression)self).Target); } if (self is WrappingExpression) { return(GetTarget(((WrappingExpression)self).Expression)); } if (self is CustomExpression) { return(((CustomExpression)self).GetTarget()); } return(null); }
/// <summary> /// Returns all the leaves in the sequence originating in the given item /// </summary> /// <returns>The leaves.</returns> /// <param name="self">The items to enumerate.</param> /// <param name="visitor">The visitor method. Return <c>false</c> to prevent entering this node.</param> public static IEnumerable <ASTItem> LeavesOnly(this ASTItem self, Func <ASTItem, VisitorState, bool> visitor = null) { visitor = visitor ?? ((a, b) => true); var work = new List <ASTItem>(); ASTItem last = null; foreach (var n in All(self, (el, state) => { if (state == VisitorState.Enter) { last = el; } if (state == VisitorState.Leave) { if (el == last) { work.Add(el); } last = null; } return(visitor(el, state)); })) { foreach (var el in work) { yield return(el); } work.Clear(); } foreach (var el in work) { yield return(el); } }
/// <summary> /// Visits all elements in depth-first-search, using post-order vistits /// </summary> /// <returns>The sequence of elements in depth-first post-order.</returns> /// <param name="self">The item to enumerate.</param> /// <param name="visitor">The visitor method. Return <c>false</c> to prevent entering this node.</param> public static IEnumerable <ASTItem> DepthFirstPostOrder(this ASTItem self, Func <ASTItem, VisitorState, bool> visitor = null) { visitor = visitor ?? ((a, b) => true); var work = new List <ASTItem>(); foreach (var n in All(self, (el, state) => { if (state == VisitorState.Leave) { work.Add(el); } return(visitor(el, state)); })) { foreach (var el in work) { yield return(el); } work.Clear(); } foreach (var el in work) { yield return(el); } }
/// <summary> /// Enumerates all elements in the item, and optionally applies the visitor function /// </summary> /// <param name="self">The item to traverse.</param> /// <param name="visitor">The visitor method. Return <c>false</c> to prevent entering this node.</param> public static IEnumerable <ASTItem> All(this ASTItem self, Func <ASTItem, VisitorState, bool> visitor = null) { if (self is Network) { return(All((Network)self, visitor)); } if (self is Process) { return(All((Process)self, visitor)); } if (self is Bus) { return(All((Bus)self, visitor)); } if (self is Method) { return(All((Method)self, visitor)); } if (self is Statement) { return(All((Statement)self, visitor)); } if (self is Expression) { return(All((Expression)self, visitor)); } throw new Exception($"Unable to visit expression of type {self.GetType().FullName}"); }
/// <summary> /// Starts a new local scope /// </summary> /// <param name="scope">The item starting the scope.</param> public void StartScope(ASTItem scope) { if (scope == null) { throw new ArgumentNullException(nameof(scope)); } Scopes.Add(new KeyValuePair <ASTItem, Dictionary <string, Variable> >(scope, new Dictionary <string, Variable>())); }
/// <summary> /// Reverse walks the tree to find the next parent of the given type or null /// </summary> /// <returns>The nearest parent or null.</returns> /// <param name="self">The item ot get the parent for.</param> /// <param name="parentType">The data type to look for.</param> public static ASTItem GetNearestParent(this ASTItem self, Type parentType) { if (self == null) { return(null); } var p = self.Parent; while (p != null && !parentType.IsAssignableFrom(p.GetType())) { p = p.Parent; } return(p); }
/// <summary> /// Closes the current local scope /// </summary> /// <param name="scope">The scope to close.</param> public void FinishScope(ASTItem scope) { if (scope == null) { throw new ArgumentNullException(nameof(scope)); } if (Scopes.Last().Key != scope) { throw new Exception("PopScope had incorrect scope"); } if (scope is BlockStatement) { ((BlockStatement)scope).Variables = Scopes.Last().Value.Values.ToArray(); } Scopes.RemoveAt(Scopes.Count - 1); }
/// <summary> /// Sets the target value /// </summary> /// <returns>The target variable or signal.</returns> /// <param name="self">The item to set the element on.</param> /// <param name="target">The value to set</param> public static void SetTarget(this ASTItem self, DataElement target) { if (self is IdentifierExpression) { ((IdentifierExpression)self).Target = target; } else if (self is MemberReferenceExpression) { ((MemberReferenceExpression)self).Target = target; } else if (self is WrappingExpression) { SetTarget(((WrappingExpression)self).Expression, target); } else if (self is Expression && self != ((Expression)self).GetUnwrapped()) { SetTarget(((Expression)self).GetUnwrapped(), target); } else { throw new Exception($"Unable to set target on item of type {self.GetType().FullName}"); } }
/// <summary> /// Reverse walks the tree to find the next parent of the given type or null /// </summary> /// <returns>The nearest parent or null.</returns> /// <param name="self">The item ot get the parent for.</param> /// <typeparam name="T">The data type to look for.</typeparam> public static T GetNearestParent <T>(this ASTItem self) where T : ASTItem { return((T)GetNearestParent(self, typeof(T))); }
/// <summary> /// Locates the target for an expression. /// </summary> /// <returns>The data element.</returns> /// <param name="network">The top-level network.</param> /// <param name="proc">The process where the method is located.</param> /// <param name="method">The method where the statement is found.</param> /// <param name="statement">The statement where the expression is found.</param> /// <param name="expression">The expression to examine.</param> protected ASTItem TryLocateElement(NetworkState network, ProcessState proc, MethodState method, Statement statement, ICSharpCode.Decompiler.CSharp.Syntax.Expression expression) { if (expression is ICSharpCode.Decompiler.CSharp.Syntax.InvocationExpression) { var e = expression as ICSharpCode.Decompiler.CSharp.Syntax.InvocationExpression; var target = e.Target; return(LocateDataElement(network, proc, method, statement, target)); } else if (expression is ICSharpCode.Decompiler.CSharp.Syntax.IndexerExpression) { var e = expression as ICSharpCode.Decompiler.CSharp.Syntax.IndexerExpression; var target = e.Target; return(LocateDataElement(network, proc, method, statement, target)); } else if (expression is ICSharpCode.Decompiler.CSharp.Syntax.IdentifierExpression) { var e = expression as ICSharpCode.Decompiler.CSharp.Syntax.IdentifierExpression; var name = e.Identifier; Variable variable; if (method != null && method.TryGetVariable(name, out variable)) { return(variable); } if (method != null) { var p = method.Parameters.FirstOrDefault(x => x.Name == name); if (p != null) { return(p); } } if (proc.Variables.TryGetValue(name, out variable)) { return(variable); } Signal signal; if (proc != null && proc.Signals.TryGetValue(name, out signal)) { return(signal); } return(null); } else if (expression is ICSharpCode.Decompiler.CSharp.Syntax.MemberReferenceExpression) { var e = expression as ICSharpCode.Decompiler.CSharp.Syntax.MemberReferenceExpression; ASTItem current = null; var parts = new List <string>(); ICSharpCode.Decompiler.CSharp.Syntax.Expression ec = e; while (ec != null) { if (ec is ICSharpCode.Decompiler.CSharp.Syntax.MemberReferenceExpression) { parts.Add(((ICSharpCode.Decompiler.CSharp.Syntax.MemberReferenceExpression)ec).MemberName); ec = ((ICSharpCode.Decompiler.CSharp.Syntax.MemberReferenceExpression)ec).Target; } else if (ec is ICSharpCode.Decompiler.CSharp.Syntax.ThisReferenceExpression) { //parts.Add("this"); ec = null; break; } else if (ec is ICSharpCode.Decompiler.CSharp.Syntax.IdentifierExpression) { parts.Add(((ICSharpCode.Decompiler.CSharp.Syntax.IdentifierExpression)ec).Identifier); ec = null; break; } else if (ec is ICSharpCode.Decompiler.CSharp.Syntax.TypeReferenceExpression) { TypeDefinition dc = null; if (method != null) { dc = method.SourceMethod.DeclaringType; } else if (proc != null) { dc = proc.CecilType.Resolve(); } var ecs = ec.ToString(); if (dc != null) { if (ecs == dc.FullName || ecs == dc.Name) { ec = null; //parts.Add("this"); break; } var targetproc = network.Processes.FirstOrDefault(x => x.CecilType.Name == ecs || x.CecilType.FullName == ecs); if (targetproc != null) { // This is a static reference current = null; //targetproc; break; } var bt = LoadTypeByName(ecs, dc.Module); if (bt == null) { bt = LoadTypeByName(dc.FullName + "." + ecs, method.SourceMethod.Module); } if (bt == null) { bt = LoadTypeByName(dc.Namespace + "." + ecs, method.SourceMethod.Module); } // In some cases dc.Namespace is empty ... if (bt == null && proc != null && proc.SourceType != null) { bt = LoadTypeByName(proc.SourceType.Namespace + "." + ecs, method.SourceMethod.Module); } if (bt != null && parts.Count == 1) { var br = bt.Resolve(); var px = br.Fields.FirstOrDefault(x => x.Name == parts[0]); // Enum flags are encoded as constants if (br.IsEnum) { if (px == null) { throw new Exception($"Unable to find enum value {parts[0]} in {br.FullName}"); } return(new Constant() { CecilType = bt, DefaultValue = px, Source = expression }); } else { // This is a constant of sorts var pe = network.ConstantLookup.Keys.FirstOrDefault(x => x.Name == parts[0]); if (pe != null) { return(network.ConstantLookup[pe]); } return(network.ConstantLookup[px] = new Constant() { CecilType = px.FieldType, Name = parts[0], Source = px, Parent = network }); } //parts.AddRange(bt.FullName.Split('.').Reverse()); } break; } // Likely a static reference, which is stored as a global constant ec = null; } else { throw new Exception($"Unexpected element in reference chain: {ec.GetType().FullName}"); } } parts.Reverse(); var fullname = string.Join(".", parts); if (parts.First() == "this") { parts.RemoveAt(0); if (proc == null) { throw new Exception("Attempting to do a resolve of \this\" but no process context is provided"); } current = proc; } var first = true; foreach (var el in parts) { var isIsFirst = first; first = false; if (current == null) { var pe = network.ConstantLookup.Keys.FirstOrDefault(x => x.Name == el); if (pe != null) { current = network.ConstantLookup[pe]; continue; } } if (current is MethodState || (isIsFirst && current == null)) { //if (method.LocalRenames.ContainsKey(el)) // el = method.LocalRenames[el]; var mt = current as MethodState ?? method; if (mt != null) { Variable temp; if (mt.TryGetVariable(el, out temp)) { current = temp; continue; } var p = mt.Parameters.FirstOrDefault(x => x.Name == el); if (p != null) { current = p; continue; } if (mt.ReturnVariable != null && !string.IsNullOrWhiteSpace(mt.ReturnVariable.Name) && el == mt.ReturnVariable.Name) { current = mt.ReturnVariable; continue; } } } if (current is ProcessState || (isIsFirst && current == null)) { var pr = current as ProcessState ?? proc; if (pr != null) { if (pr.BusInstances.ContainsKey(el)) { current = pr.BusInstances[el]; continue; } if (pr.Signals.ContainsKey(el)) { current = pr.Signals[el]; continue; } if (pr.Variables.ContainsKey(el)) { current = pr.Variables[el]; continue; } if (pr.Methods != null) { var p = pr.Methods.FirstOrDefault(x => x.Name == el); if (p != null) { current = p; continue; } } } } if (current is Bus) { current = ((Bus)current).Signals.FirstOrDefault(x => x.Name == el); if (current != null) { continue; } } if (current is Variable) { var fi = ((Variable)current).CecilType.Resolve().Fields.FirstOrDefault(x => x.Name == el); if (fi != null) { current = new Variable() { Name = el, Parent = current, Source = fi, CecilType = fi.FieldType, DefaultValue = null }; continue; } var pi = ((Variable)current).CecilType.Resolve().Properties.FirstOrDefault(x => x.Name == el); if (pi != null) { current = new Variable() { Name = el, Parent = current, Source = fi, CecilType = fi.FieldType, DefaultValue = null }; continue; } } if (el == "Length" && (current is DataElement) && ((DataElement)current).CecilType.IsArrayType()) { return(new Constant() { ArrayLengthSource = current as DataElement, CecilType = LoadType(typeof(int)), DefaultValue = null, Source = (current as DataElement).Source }); } throw new Exception($"Failed lookup at {el} in {fullname}"); } if (current == null) { throw new Exception($"Failed to fully resolve {fullname}"); } return(current); } else { throw new Exception($"Unable to find a data element for an expression of type {expression.GetType().FullName}"); } }