diff --git a/src/DynamoCore/Core/CustomNodeManager.cs b/src/DynamoCore/Core/CustomNodeManager.cs index 9e2dbec7784..830720f7860 100644 --- a/src/DynamoCore/Core/CustomNodeManager.cs +++ b/src/DynamoCore/Core/CustomNodeManager.cs @@ -485,8 +485,7 @@ public bool TryGetInfoFromPath(string path, bool isTestMode, out CustomNodeInfo nodeFactory, nodeGraph.Nodes, nodeGraph.Notes, - workspaceInfo, - nodeGraph.ElementResolver); + workspaceInfo); RegisterCustomNodeWorkspace(newWorkspace); @@ -607,7 +606,7 @@ public WorkspaceModel CreateCustomNode(string name, string category, string desc ID = newId.ToString(), FileName = string.Empty }; - var workspace = new CustomNodeWorkspaceModel(info, nodeFactory, new ElementResolver()); + var workspace = new CustomNodeWorkspaceModel(info, nodeFactory); RegisterCustomNodeWorkspace(workspace); return workspace; diff --git a/src/DynamoCore/Core/NodeGraph.cs b/src/DynamoCore/Core/NodeGraph.cs index 828ef222b67..6ccaaa922fa 100644 --- a/src/DynamoCore/Core/NodeGraph.cs +++ b/src/DynamoCore/Core/NodeGraph.cs @@ -13,8 +13,6 @@ public class NodeGraph public List Nodes { get; private set; } public List Connectors { get; private set; } public List Notes { get; private set; } - public ElementResolver ElementResolver { get; private set; } - private NodeGraph() { } @@ -118,35 +116,12 @@ private static IEnumerable LoadNotesFromXml(XmlDocument xmlDoc) /// public static NodeGraph LoadGraphFromXml(XmlDocument xmlDoc, NodeFactory nodeFactory) { - var elementResolver = LoadElementResolver(xmlDoc); - var nodes = LoadNodesFromXml(xmlDoc, nodeFactory).ToList(); var connectors = LoadConnectorsFromXml(xmlDoc, nodes.ToDictionary(node => node.GUID)).ToList(); var notes = LoadNotesFromXml(xmlDoc).ToList(); - return new NodeGraph { Nodes = nodes, Connectors = connectors, Notes = notes, ElementResolver = elementResolver }; + return new NodeGraph { Nodes = nodes, Connectors = connectors, Notes = notes }; } - private static ElementResolver LoadElementResolver(XmlDocument xmlDoc) - { - var nodes = xmlDoc.GetElementsByTagName("NamespaceResolutionMap"); - - var resolutionMap = new Dictionary>(); - if (nodes.Count > 0) - { - foreach (XmlNode child in nodes[0].ChildNodes) - { - if (child.Attributes != null) - { - XmlAttribute pName = child.Attributes["partialName"]; - XmlAttribute rName = child.Attributes["resolvedName"]; - XmlAttribute aName = child.Attributes["assemblyName"]; - var kvp = new KeyValuePair(rName.Value, aName.Value); - resolutionMap.Add(pName.Value, kvp); - } - } - } - return new ElementResolver(resolutionMap); - } } } diff --git a/src/DynamoCore/DSEngine/CodeCompletionServices.cs b/src/DynamoCore/DSEngine/CodeCompletionServices.cs index 9009e2bb721..365f188f5eb 100644 --- a/src/DynamoCore/DSEngine/CodeCompletionServices.cs +++ b/src/DynamoCore/DSEngine/CodeCompletionServices.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ProtoCore.Namespace; namespace Dynamo.DSEngine.CodeCompletion { @@ -37,10 +38,15 @@ public CodeCompletionServices(ProtoCore.Core core) /// code typed in the code block /// Class name or declared variable /// list of method and property members of the type - internal IEnumerable GetCompletionsOnType(string code, string stringToComplete) + internal IEnumerable GetCompletionsOnType(string code, string stringToComplete, ElementResolver resolver = null) { IEnumerable members = null; + if (resolver != null) + { + stringToComplete = resolver.LookupResolvedName(stringToComplete) ?? stringToComplete; + } + // Determine if the string to be completed is a class var type = GetClassType(stringToComplete); if (type != null) @@ -88,8 +94,9 @@ internal IEnumerable GetGlobals() /// /// current string being typed which is to be completed /// code block node guid to identify current node being typed + /// /// list of classes, global methods and properties that match with string being completed - internal IEnumerable SearchCompletions(string stringToComplete, Guid guid) + internal IEnumerable SearchCompletions(string stringToComplete, Guid guid, ElementResolver resolver = null) { List completions = new List(); @@ -109,7 +116,7 @@ internal IEnumerable SearchCompletions(string stringToComplete, { completions.AddRange(group. Where(x => !x.IsHiddenInLibrary). - Select(x =>CompletionData.ConvertMirrorToCompletionData(x, useFullyQualifiedName: true))); + Select(x =>CompletionData.ConvertMirrorToCompletionData(x, useFullyQualifiedName: true, resolver: resolver))); } else completions.AddRange(group. @@ -165,7 +172,8 @@ internal IEnumerable SearchTypes(string stringToComplete) /// class name in case of constructor or static method, OR /// declared instance variable on which method is invoked /// list of method overload signatures - internal IEnumerable GetFunctionSignatures(string code, string functionName, string functionPrefix) + internal IEnumerable GetFunctionSignatures(string code, string functionName, string functionPrefix, + ElementResolver resolver = null) { IEnumerable candidates = null; @@ -183,6 +191,11 @@ internal IEnumerable GetFunctionSignatures(string code, string f } // Determine if the function prefix is a class name + if (resolver != null) + { + functionPrefix = resolver.LookupResolvedName(functionPrefix) ?? functionPrefix; + } + var type = GetClassType(functionPrefix); if (type != null) { @@ -191,7 +204,7 @@ internal IEnumerable GetFunctionSignatures(string code, string f // If not of class type else { - // Check if the function prefix is a declared identifier + // Check if the function prefix is a typed identifier string typeName = CodeCompletionParser.GetVariableType(code, functionPrefix); if (typeName != null) type = GetClassType(typeName); @@ -266,9 +279,10 @@ internal CompletionData(string text, CompletionType type) } - internal static CompletionData ConvertMirrorToCompletionData(StaticMirror mirror, bool useFullyQualifiedName = false) + internal static CompletionData ConvertMirrorToCompletionData(StaticMirror mirror, bool useFullyQualifiedName = false, + ElementResolver resolver = null) { - MethodMirror method = mirror as MethodMirror; + var method = mirror as MethodMirror; if (method != null) { string methodName = method.MethodName; @@ -279,20 +293,31 @@ internal static CompletionData ConvertMirrorToCompletionData(StaticMirror mirror Stub = signature }; } - PropertyMirror property = mirror as PropertyMirror; + var property = mirror as PropertyMirror; if (property != null) { string propertyName = property.PropertyName; return new CompletionData(propertyName, CompletionType.Property); } - ClassMirror classMirror = mirror as ClassMirror; + var classMirror = mirror as ClassMirror; if (classMirror != null) { - string className = useFullyQualifiedName ? classMirror.ClassName : classMirror.Alias; + string className = useFullyQualifiedName ? GetShortClassName(classMirror, resolver) : classMirror.Alias; return new CompletionData(className, CompletionType.Class); } else throw new ArgumentException("Invalid argument"); } + + private static string GetShortClassName(ClassMirror mirror, ElementResolver resolver) + { + var shortName = string.Empty; + if (resolver != null) + { + shortName = resolver.LookupShortName(mirror.ClassName); + } + + return string.IsNullOrEmpty(shortName) ? mirror.ClassName : shortName; + } } } diff --git a/src/DynamoCore/DSEngine/EngineController.cs b/src/DynamoCore/DSEngine/EngineController.cs index f848989537c..6aa9d948f25 100644 --- a/src/DynamoCore/DSEngine/EngineController.cs +++ b/src/DynamoCore/DSEngine/EngineController.cs @@ -7,6 +7,7 @@ using ProtoCore.AST.AssociativeAST; using ProtoCore.DSASM.Mirror; using ProtoCore.Mirror; +using ProtoCore.Namespace; using ProtoScript.Runners; using System; using System.Collections.Generic; @@ -623,6 +624,7 @@ private bool HasVariableDefined(string var) } #endregion + } public class CompilationServices diff --git a/src/DynamoCore/Models/CustomNodeWorkspaceModel.cs b/src/DynamoCore/Models/CustomNodeWorkspaceModel.cs index 5fa23c34b41..d9bda39eeec 100644 --- a/src/DynamoCore/Models/CustomNodeWorkspaceModel.cs +++ b/src/DynamoCore/Models/CustomNodeWorkspaceModel.cs @@ -33,22 +33,20 @@ private set public CustomNodeWorkspaceModel( WorkspaceInfo info, - NodeFactory factory, - ElementResolver elementResolver) + NodeFactory factory) : this( factory, Enumerable.Empty(), Enumerable.Empty(), - info, - elementResolver) { } + info) { } public CustomNodeWorkspaceModel( NodeFactory factory, IEnumerable e, IEnumerable n, WorkspaceInfo info, - ElementResolver elementResolver) - : base(e, n, info, factory, elementResolver) + ElementResolver elementResolver = null) + : base(e, n, info, factory) { HasUnsavedChanges = false; @@ -56,9 +54,16 @@ private set Category = info.Category; Description = info.Description; + if (elementResolver != null) + { + ElementResolver.CopyResolutionMap(elementResolver); + } + PropertyChanged += OnPropertyChanged; } + #endregion + private void OnPropertyChanged(object sender, PropertyChangedEventArgs args) { if (args.PropertyName == "Name") @@ -71,7 +76,6 @@ private void OnPropertyChanged(object sender, PropertyChangedEventArgs args) } } - #endregion /// /// All CustomNodeDefinitions which this Custom Node depends on. diff --git a/src/DynamoCore/Models/DynamoModel.cs b/src/DynamoCore/Models/DynamoModel.cs index 46758069cd6..7568ce9fb8b 100644 --- a/src/DynamoCore/Models/DynamoModel.cs +++ b/src/DynamoCore/Models/DynamoModel.cs @@ -902,8 +902,7 @@ private bool OpenFile(WorkspaceInfo workspaceInfo, XmlDocument xmlDoc, out Works nodeGraph.Notes, workspaceInfo, DebugSettings.VerboseLogging, - IsTestMode, - nodeGraph.ElementResolver + IsTestMode ); RegisterHomeWorkspace(newWorkspace); diff --git a/src/DynamoCore/Models/HomeWorkspaceModel.cs b/src/DynamoCore/Models/HomeWorkspaceModel.cs index 107036d59f8..0191cfba076 100644 --- a/src/DynamoCore/Models/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Models/HomeWorkspaceModel.cs @@ -39,7 +39,7 @@ public class HomeWorkspaceModel : WorkspaceModel Enumerable.Empty(), new WorkspaceInfo(){FileName = fileName, Name = "Home"}, verboseLogging, - isTestMode, new ElementResolver()) { } + isTestMode) { } public HomeWorkspaceModel( EngineController engine, @@ -50,9 +50,8 @@ public class HomeWorkspaceModel : WorkspaceModel IEnumerable n, WorkspaceInfo info, bool verboseLogging, - bool isTestMode, - ElementResolver elementResolver) - : base(e, n, info, factory, elementResolver) + bool isTestMode) + : base(e, n, info, factory) { RunSettings = new RunSettings(info.RunType, info.RunPeriod); diff --git a/src/DynamoCore/Models/WorkspaceModel.cs b/src/DynamoCore/Models/WorkspaceModel.cs index 35a4bd4da69..77de1530fc7 100644 --- a/src/DynamoCore/Models/WorkspaceModel.cs +++ b/src/DynamoCore/Models/WorkspaceModel.cs @@ -423,6 +423,7 @@ public UndoRedoRecorder UndoRecorder get { return undoRecorder; } } + public ElementResolver ElementResolver { get; protected set; } /// /// A unique identifier for the workspace. /// @@ -431,7 +432,6 @@ public Guid Guid get { return guid; } } - public ElementResolver ElementResolver { get; private set; } #endregion @@ -441,8 +441,7 @@ public Guid Guid IEnumerable e, IEnumerable n, WorkspaceInfo info, - NodeFactory factory, - ElementResolver elementResolver) + NodeFactory factory) { guid = Guid.NewGuid(); @@ -463,11 +462,20 @@ public Guid Guid undoRecorder = new UndoRedoRecorder(this); NodeFactory = factory; - ElementResolver = elementResolver; + // Update ElementResolver from nodeGraph.Nodes (where node is CBN) + ElementResolver = new ElementResolver(); foreach (var node in nodes) + { RegisterNode(node); + var cbn = node as CodeBlockNodeModel; + if (cbn != null && cbn.ElementResolver != null) + { + ElementResolver.CopyResolutionMap(cbn.ElementResolver); + } + } + foreach (var connector in Connectors) RegisterConnector(connector); } @@ -651,6 +659,7 @@ public virtual bool Save(ProtoCore.Core core) internal void ResetWorkspace() { + ElementResolver = new ElementResolver(); ResetWorkspaceCore(); } diff --git a/src/DynamoCore/Nodes/CodeBlockNode.cs b/src/DynamoCore/Nodes/CodeBlockNode.cs index 566ea49523b..57bd3e9b74d 100644 --- a/src/DynamoCore/Nodes/CodeBlockNode.cs +++ b/src/DynamoCore/Nodes/CodeBlockNode.cs @@ -2,13 +2,11 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Xml; - -using Dynamo.Core; using Dynamo.DSEngine; - using ProtoCore.AST.AssociativeAST; using Dynamo.Models; using Dynamo.Utilities; @@ -43,6 +41,8 @@ public bool ShouldFocus internal set { shouldFocus = value; } } + public ElementResolver ElementResolver { get; private set; } + private struct Formatting { public const double INITIAL_MARGIN = 0; @@ -56,13 +56,7 @@ public CodeBlockNodeModel(LibraryServices libraryServices) ArgumentLacing = LacingStrategy.Disabled; this.libraryServices = libraryServices; this.libraryServices.LibraryLoaded += LibraryServicesOnLibraryLoaded; - } - - public CodeBlockNodeModel(string userCode, LibraryServices libraryServices) - : this(libraryServices) - { - code = userCode; - ProcessCodeDirect(); + this.ElementResolver = new ElementResolver(); } public CodeBlockNodeModel(string userCode, double xPos, double yPos, LibraryServices libraryServices) @@ -75,6 +69,7 @@ public CodeBlockNodeModel(string userCode, Guid guid, double xPos, double yPos, Y = yPos; this.libraryServices = libraryServices; this.libraryServices.LibraryLoaded += LibraryServicesOnLibraryLoaded; + this.ElementResolver = new ElementResolver(); code = userCode; GUID = guid; ShouldFocus = false; @@ -90,7 +85,7 @@ public override void Dispose() private void LibraryServicesOnLibraryLoaded(object sender, LibraryServices.LibraryLoadedEventArgs libraryLoadedEventArgs) { - ProcessCodeDirect(); + //ProcessCodeDirect(); } /// @@ -183,7 +178,7 @@ public string Code private set { code = value; } } - public void SetCodeContent(string newCode) + public void SetCodeContent(string newCode, ElementResolver workspaceElementResolver) { if (code != null && code.Equals(newCode)) return; @@ -201,7 +196,7 @@ public void SetCodeContent(string newCode) SaveAndDeleteConnectors(inportConnections, outportConnections); code = newCode; - ProcessCode(ref errorMessage, ref warningMessage); + ProcessCode(ref errorMessage, ref warningMessage, workspaceElementResolver); //Recreate connectors that can be reused LoadAndCreateConnectors(inportConnections, outportConnections); @@ -237,6 +232,7 @@ protected override bool UpdateValueCore(UpdateValueParams updateValueParams) { string name = updateValueParams.PropertyName; string value = updateValueParams.PropertyValue; + ElementResolver workspaceElementResolver = updateValueParams.ElementResolver; if (name != "Code") return base.UpdateValueCore(updateValueParams); @@ -254,7 +250,7 @@ protected override bool UpdateValueCore(UpdateValueParams updateValueParams) else { if (!value.Equals(Code)) - SetCodeContent(value); + SetCodeContent(value, workspaceElementResolver); } return true; } @@ -265,6 +261,7 @@ protected override void SerializeCore(XmlElement element, SaveContext context) var helper = new XmlElementHelper(element); helper.SetAttribute("CodeText", code); helper.SetAttribute("ShouldFocus", shouldFocus); + } protected override void DeserializeCore(XmlElement nodeElement, SaveContext context) @@ -273,6 +270,11 @@ protected override void DeserializeCore(XmlElement nodeElement, SaveContext cont var helper = new XmlElementHelper(nodeElement); shouldFocus = helper.ReadBoolean("ShouldFocus"); code = helper.ReadString("CodeText"); + + // Lookup namespace resolution map if available and initialize new instance of ElementResolver + var resolutionMap = CodeBlockUtils.DeserializeElementResolver(nodeElement); + ElementResolver = new ElementResolver(resolutionMap); + ProcessCodeDirect(); } @@ -377,7 +379,8 @@ internal void ProcessCodeDirect() OnNodeModified(); } - private void ProcessCode(ref string errorMessage, ref string warningMessage) + private void ProcessCode(ref string errorMessage, ref string warningMessage, + ElementResolver workspaceElementResolver = null) { code = CodeBlockUtils.FormatUserText(code); codeStatements.Clear(); @@ -387,7 +390,11 @@ private void ProcessCode(ref string errorMessage, ref string warningMessage) try { - var parseParam = new ParseParam(GUID, code); + // During loading of CBN from file, the elementResolver from the workspace is unavailable + // in which case, a local copy of the ER obtained from the CBN is used + var resolver = workspaceElementResolver ?? this.ElementResolver; + var parseParam = new ParseParam(GUID, code, resolver); + if (CompilerUtils.PreCompileCodeBlock(libraryServices.LibraryManagementCore, ref parseParam)) { if (parseParam.ParsedNodes != null) diff --git a/src/DynamoCore/Nodes/Custom Nodes/Function.cs b/src/DynamoCore/Nodes/Custom Nodes/Function.cs index 06025e80161..7927b81045e 100644 --- a/src/DynamoCore/Nodes/Custom Nodes/Function.cs +++ b/src/DynamoCore/Nodes/Custom Nodes/Function.cs @@ -203,6 +203,8 @@ public class Symbol : NodeModel { private string inputSymbol = String.Empty; private string nickName = String.Empty; + private ElementResolver elementResolver; + private ElementResolver workspaceElementResolver; public Symbol() { @@ -213,6 +215,8 @@ public Symbol() ArgumentLacing = LacingStrategy.Disabled; InputSymbol = String.Empty; + + elementResolver = new ElementResolver(); } public string InputSymbol @@ -319,6 +323,9 @@ protected override void DeserializeCore(XmlElement nodeElement, SaveContext cont } ArgumentLacing = LacingStrategy.Disabled; + + var resolutionMap = CodeBlockUtils.DeserializeElementResolver(nodeElement); + elementResolver = new ElementResolver(resolutionMap); } private bool TryParseInputSymbol(string inputSymbol, @@ -329,11 +336,12 @@ protected override void DeserializeCore(XmlElement nodeElement, SaveContext cont defaultValue = null; var parseString = InputSymbol; - - // if it has default value, then append ';' parseString += ";"; - var parseParam = new ParseParam(this.GUID, parseString); + // During loading of symbol node from file, the elementResolver from the workspace is unavailable + // in which case, a local copy of the ER obtained from the symbol node is used + var resolver = workspaceElementResolver ?? elementResolver; + var parseParam = new ParseParam(this.GUID, parseString, resolver); if (EngineController.CompilationServices.PreCompileCodeBlock(ref parseParam) && parseParam.ParsedNodes != null && @@ -359,6 +367,7 @@ protected override bool UpdateValueCore(UpdateValueParams updateValueParams) { string name = updateValueParams.PropertyName; string value = updateValueParams.PropertyValue; + workspaceElementResolver = updateValueParams.ElementResolver; if (name == "InputSymbol") { diff --git a/src/DynamoCore/Utilities/CodeBlockUtils.cs b/src/DynamoCore/Utilities/CodeBlockUtils.cs index 68585fb29bc..da18779db29 100644 --- a/src/DynamoCore/Utilities/CodeBlockUtils.cs +++ b/src/DynamoCore/Utilities/CodeBlockUtils.cs @@ -1,4 +1,6 @@ -using Dynamo.UI; +using System.Diagnostics; +using System.Xml; +using Dynamo.UI; using Dynamo.Models; using Dynamo.Nodes; @@ -10,7 +12,7 @@ namespace Dynamo.Utilities { - public class CodeBlockUtils + public static class CodeBlockUtils { /// /// Call this method to turn all "\r\n" and "\r" @@ -228,6 +230,31 @@ public static string FormatUserText(string inputCode) return inputCode; } + public static IDictionary> DeserializeElementResolver( + XmlElement nodeElement) + { + var xmlDoc = nodeElement.OwnerDocument; + Debug.Assert(xmlDoc != null); + + var nodes = xmlDoc.GetElementsByTagName("NamespaceResolutionMap"); + + var resolutionMap = new Dictionary>(); + if (nodes.Count > 0) + { + foreach (XmlNode child in nodes[0].ChildNodes) + { + if (child.Attributes != null) + { + XmlAttribute pName = child.Attributes["partialName"]; + XmlAttribute rName = child.Attributes["resolvedName"]; + XmlAttribute aName = child.Attributes["assemblyName"]; + var kvp = new KeyValuePair(rName.Value, aName.Value); + resolutionMap.Add(pName.Value, kvp); + } + } + } + return resolutionMap; + } } /// diff --git a/src/DynamoCoreWpf/Views/CodeBlocks/CodeBlockEditor.xaml.cs b/src/DynamoCoreWpf/Views/CodeBlocks/CodeBlockEditor.xaml.cs index 496fb365bfb..bd259b8706e 100644 --- a/src/DynamoCoreWpf/Views/CodeBlocks/CodeBlockEditor.xaml.cs +++ b/src/DynamoCoreWpf/Views/CodeBlocks/CodeBlockEditor.xaml.cs @@ -18,7 +18,7 @@ using System.Windows.Input; using System.Windows.Media; using System.Xml; -using DynCmd = Dynamo.Models.DynamoModel; +using DynCmd = Dynamo.Models.DynamoModel; namespace Dynamo.UI.Controls { @@ -83,7 +83,8 @@ private IEnumerable GetCompletionData(string code, string strin var engineController = dynamoViewModel.EngineController; - return engineController.CodeCompletionServices.GetCompletionsOnType(code, stringToComplete). + return engineController.CodeCompletionServices.GetCompletionsOnType( + code, stringToComplete, dynamoViewModel.HomeSpace.ElementResolver). Select(x => new CodeBlockCompletionData(x)); } @@ -91,15 +92,16 @@ internal IEnumerable SearchCompletions(string stringToComplete, { var engineController = dynamoViewModel.EngineController; - return engineController.CodeCompletionServices.SearchCompletions(stringToComplete, guid). - Select(x => new CodeBlockCompletionData(x)); + return engineController.CodeCompletionServices.SearchCompletions(stringToComplete, guid, + dynamoViewModel.CurrentSpace.ElementResolver).Select(x => new CodeBlockCompletionData(x)); } internal IEnumerable GetFunctionSignatures(string code, string functionName, string functionPrefix) { var engineController = dynamoViewModel.EngineController; - return engineController.CodeCompletionServices.GetFunctionSignatures(code, functionName, functionPrefix). + return engineController.CodeCompletionServices.GetFunctionSignatures( + code, functionName, functionPrefix, dynamoViewModel.HomeSpace.ElementResolver). Select(x => new CodeBlockInsightItem(x)); } @@ -276,6 +278,7 @@ private void OnTextAreaTextEntered(object sender, TextCompositionEventArgs e) } else if (completionWindow == null && (char.IsLetterOrDigit(e.Text[0]) || e.Text[0] == '_')) { + // Begin completion while typing only if the previous character already typed in // is a white space or non-alphanumeric character if (startPos > 1 && char.IsLetterOrDigit(InternalEditor.Document.GetCharAt(startPos - 2))) diff --git a/src/Engine/ProtoCore/CodeGen.cs b/src/Engine/ProtoCore/CodeGen.cs index 06422e85759..e4aef54c98d 100644 --- a/src/Engine/ProtoCore/CodeGen.cs +++ b/src/Engine/ProtoCore/CodeGen.cs @@ -2734,7 +2734,10 @@ protected void BuildRealDependencyForIdentList(AssociativeGraph.GraphNode graphN } } - protected void EmitIdentifierListNode(Node node, ref ProtoCore.Type inferedType, bool isBooleanOp = false, ProtoCore.AssociativeGraph.GraphNode graphNode = null, ProtoCore.CompilerDefinitions.Associative.SubCompilePass subPass = ProtoCore.CompilerDefinitions.Associative.SubCompilePass.kNone, ProtoCore.AST.Node bnode = null) + protected void EmitIdentifierListNode(Node node, ref ProtoCore.Type inferedType, bool isBooleanOp = false, + ProtoCore.AssociativeGraph.GraphNode graphNode = null, + ProtoCore.CompilerDefinitions.Associative.SubCompilePass subPass = ProtoCore.CompilerDefinitions.Associative.SubCompilePass.kNone, + ProtoCore.AST.Node bnode = null) { if (subPass == ProtoCore.CompilerDefinitions.Associative.SubCompilePass.kUnboundIdentifier) { diff --git a/src/Engine/ProtoCore/CompilerUtils.cs b/src/Engine/ProtoCore/CompilerUtils.cs index 5454027a3b3..c62c4b6f6e8 100644 --- a/src/Engine/ProtoCore/CompilerUtils.cs +++ b/src/Engine/ProtoCore/CompilerUtils.cs @@ -1,11 +1,11 @@ using ProtoCore.AST; using ProtoCore.AST.AssociativeAST; using ProtoCore.DSASM; +using ProtoCore.Namespace; using ProtoCore.Properties; using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace ProtoCore.Utils { @@ -93,16 +93,17 @@ private bool Equals(VariableLine variableLine) public class ParseParam { - private List temporaries = null; + private List temporaries; private Dictionary unboundIdentifiers; - private List parsedNodes = null; - private List errors = null; - private List warnings = null; - - public ParseParam(System.Guid postfixGuid, System.String code) + private List parsedNodes; + private List errors; + private List warnings; + + public ParseParam(System.Guid postfixGuid, System.String code, ElementResolver elementResolver) { this.PostfixGuid = postfixGuid; this.OriginalCode = code; + this.ElementResolver = elementResolver; } public void AppendTemporaryVariable(string variable) @@ -157,6 +158,7 @@ public void AppendWarnings(IEnumerable warning public System.Guid PostfixGuid { get; private set; } public System.String OriginalCode { get; private set; } public System.String ProcessedCode { get; internal set; } + public ElementResolver ElementResolver { get; private set; } public IEnumerable Temporaries { @@ -254,8 +256,9 @@ public static bool TryLoadAssemblyIntoCore(Core core, string assemblyPath) /// stores list of AST nodes, errors and warnings /// Evaluates and stores list of unbound identifiers /// - /// - /// + /// container for compilation related parameters + /// classname resolver + /// true if code compilation succeeds, false otherwise public static bool PreCompileCodeBlock(Core core, ref ParseParam parseParams) { string postfixGuid = parseParams.PostfixGuid.ToString().Replace("-", "_"); @@ -280,23 +283,13 @@ public static bool PreCompileCodeBlock(Core core, ref ParseParam parseParams) private static bool CompileCodeBlockAST(Core core, ParseParam parseParams) { - Dictionary> unboundIdentifiers = new Dictionary>(); - IEnumerable warnings = null; + var unboundIdentifiers = new Dictionary>(); ProtoCore.BuildStatus buildStatus = null; try { int blockId = ProtoCore.DSASM.Constants.kInvalidIndex; - CodeBlockNode codeblock = new CodeBlockNode(); - List nodes = new List(); - foreach (var i in parseParams.ParsedNodes) - { - AssociativeNode assocNode = i as AssociativeNode; - - if (assocNode != null) - nodes.Add(NodeUtils.Clone(assocNode)); - } - codeblock.Body.AddRange(nodes); + bool parsingPreloadFlag = core.IsParsingPreloadedAssembly; bool parsingCbnFlag = core.IsParsingPreloadedAssembly; @@ -304,6 +297,21 @@ private static bool CompileCodeBlockAST(Core core, ParseParam parseParams) core.IsParsingCodeBlockNode = true; core.ResetForPrecompilation(); + + var astNodes = parseParams.ParsedNodes; + + // Lookup namespace resolution map in elementResolver to rewrite + // partial classnames with their fully qualified names in ASTs + // before passing them for pre-compilation. If partial class is not found in map, + // update Resolution map in elementResolver with fully resolved name from compiler. + ElementRewriter.RewriteElementNames(core.ClassTable, parseParams.ElementResolver, astNodes); + + // Clone a disposable copy of AST nodes for PreCompile() as Codegen mutates AST's + // while performing SSA transforms and we want to keep the original AST's + var codeblock = new CodeBlockNode(); + var nodes = astNodes.OfType().Select(assocNode => NodeUtils.Clone(assocNode)).ToList(); + codeblock.Body.AddRange(nodes); + buildStatus = PreCompile(string.Empty, core, codeblock, out blockId); core.IsParsingCodeBlockNode = parsingCbnFlag; @@ -316,7 +324,7 @@ private static bool CompileCodeBlockAST(Core core, ParseParam parseParams) { return false; } - warnings = buildStatus.Warnings; + IEnumerable warnings = buildStatus.Warnings; // Get the unboundIdentifiers from the warnings GetInputLines(parseParams.ParsedNodes, warnings, unboundIdentifiers); diff --git a/src/Engine/ProtoCore/Namespace/ElementResolver.cs b/src/Engine/ProtoCore/Namespace/ElementResolver.cs index 41e7f4f656f..cc48850070e 100644 --- a/src/Engine/ProtoCore/Namespace/ElementResolver.cs +++ b/src/Engine/ProtoCore/Namespace/ElementResolver.cs @@ -1,4 +1,6 @@ + using System.Collections.Generic; +using System.Linq; namespace ProtoCore.Namespace { @@ -30,6 +32,19 @@ public ElementResolver(IDictionary> namespa resolutionMap = namespaceLookupMap; } + public void CopyResolutionMap(ElementResolver otherResolver) + { + foreach (var e in otherResolver.ResolutionMap.Where(e => !resolutionMap.ContainsKey(e.Key))) + { + resolutionMap.Add(e); + } + } + + /// + /// Looks up resolved name in resolution map given the partial name + /// + /// + /// returns null if partial name is not found in resolution map public string LookupResolvedName(string partialName) { KeyValuePair resolvedName; @@ -39,6 +54,16 @@ public string LookupResolvedName(string partialName) return resolvedName.Key; } + public string LookupShortName(string resolvedName) + { + var nameList = (from keyValuePair in ResolutionMap + where keyValuePair.Value.Key == resolvedName + select keyValuePair.Key).ToList(); + + // return the shortest partial name (key) + return nameList.Any() ? nameList.OrderBy(x => x.Length).First() : string.Empty; + } + public string LookupAssemblyName(string partialName) { KeyValuePair resolvedName; diff --git a/src/Engine/ProtoCore/Namespace/ElementRewriter.cs b/src/Engine/ProtoCore/Namespace/ElementRewriter.cs new file mode 100644 index 00000000000..dc857244044 --- /dev/null +++ b/src/Engine/ProtoCore/Namespace/ElementRewriter.cs @@ -0,0 +1,211 @@ +using System.Diagnostics; +using ProtoCore.AST; +using ProtoCore.AST.AssociativeAST; +using ProtoCore.DSASM; +using ProtoCore.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProtoCore.Namespace +{ + + public static class ElementRewriter + { + /// + /// Lookup namespace resolution map to substitute + /// partial classnames with their fully qualified names in ASTs. + /// If partial class is not found in map, + /// update ResolutionMap with fully resolved name from compiler. + /// + /// + /// + /// parent AST node + public static void RewriteElementNames(ClassTable classTable, + ElementResolver elementResolver, IEnumerable astNodes) + { + foreach (var node in astNodes) + { + var astNode = node as AssociativeNode; + if (astNode == null) + continue; + + LookupResolvedNameAndRewriteAst(classTable, elementResolver, ref astNode); + } + } + + internal static void LookupResolvedNameAndRewriteAst(ClassTable classTable, ElementResolver elementResolver, + ref AssociativeNode astNode) + { + Debug.Assert(elementResolver != null); + + // Get partial class identifier/identifier lists + var classIdentifiers = GetClassIdentifiers(astNode); + + var resolvedNames = new Queue(); + foreach (var identifier in classIdentifiers) + { + var partialName = CoreUtils.GetIdentifierExceptMethodName(identifier); + var resolvedName = elementResolver.LookupResolvedName(partialName); + if (string.IsNullOrEmpty(resolvedName)) + { + // If namespace resolution map does not contain entry for partial name, + // back up on compiler to resolve the namespace from partial name + var matchingClasses = CoreUtils.GetResolvedClassName(classTable, identifier); + + if (matchingClasses.Length == 1) + { + resolvedName = matchingClasses[0]; + var assemblyName = CoreUtils.GetAssemblyFromClassName(classTable, resolvedName); + + elementResolver.AddToResolutionMap(partialName, resolvedName, assemblyName); + } + else + { + // Class name could not be resolved - Possible namespace conflict + // This will be reported subsequently in the pre-compilation stage if there's a conflict + // Enter an empty resolved name to the list and continue + resolvedNames.Enqueue(string.Empty); + continue; + } + } + resolvedNames.Enqueue(resolvedName); + } + + RewriteAstWithResolvedName(ref astNode, resolvedNames); + } + + internal static IEnumerable GetClassIdentifiers(AssociativeNode astNode) + { + var classIdentifiers = new List(); + var resolvedNames = new Queue(); + DfsTraverse(ref astNode, classIdentifiers, resolvedNames); + return classIdentifiers; + } + + /// + /// Replace partial identifier with fully resolved identifier list in original AST + /// This is the same AST that is also passed to the VM for execution + /// + /// + /// fully qualified class identifier list + private static void RewriteAstWithResolvedName(ref AssociativeNode astNode, Queue resolvedNames) + { + DfsTraverse(ref astNode, null, resolvedNames); + } + + #region private utility methods + + private static void DfsTraverse(ref AssociativeNode astNode, ICollection classIdentifiers, + Queue resolvedNames) + { + if (astNode is BinaryExpressionNode) + { + var bnode = astNode as BinaryExpressionNode; + AssociativeNode leftNode = bnode.LeftNode; + AssociativeNode rightNode = bnode.RightNode; + DfsTraverse(ref leftNode, classIdentifiers, resolvedNames); + DfsTraverse(ref rightNode, classIdentifiers, resolvedNames); + + bnode.LeftNode = leftNode; + bnode.RightNode = rightNode; + } + else if (astNode is FunctionCallNode) + { + var fCall = astNode as FunctionCallNode; + for (int n = 0; n < fCall.FormalArguments.Count; ++n) + { + AssociativeNode argNode = fCall.FormalArguments[n]; + DfsTraverse(ref argNode, classIdentifiers, resolvedNames); + fCall.FormalArguments[n] = argNode; + } + } + else if (astNode is ExprListNode) + { + var exprList = astNode as ExprListNode; + for (int n = 0; n < exprList.list.Count; ++n) + { + AssociativeNode exprNode = exprList.list[n]; + DfsTraverse(ref exprNode, classIdentifiers, resolvedNames); + exprList.list[n] = exprNode; + } + } + else if (astNode is InlineConditionalNode) + { + var inlineNode = astNode as InlineConditionalNode; + AssociativeNode condition = inlineNode.ConditionExpression; + AssociativeNode trueBody = inlineNode.TrueExpression; + AssociativeNode falseBody = inlineNode.FalseExpression; + + DfsTraverse(ref condition, classIdentifiers, resolvedNames); + DfsTraverse(ref trueBody, classIdentifiers, resolvedNames); + DfsTraverse(ref falseBody, classIdentifiers, resolvedNames); + + inlineNode.ConditionExpression = condition; + inlineNode.FalseExpression = falseBody; + inlineNode.TrueExpression = trueBody; + } + else if (astNode is IdentifierListNode) + { + var identListNode = astNode as IdentifierListNode; + var rightNode = identListNode.RightNode; + + if (rightNode is FunctionCallNode) + { + DfsTraverse(ref rightNode, classIdentifiers, resolvedNames); + } + if (resolvedNames.Any()) + { + astNode = RewriteIdentifierListNode(identListNode, resolvedNames); + } + else + { + classIdentifiers.Add(identListNode); + } + } + + } + + private static IdentifierListNode RewriteIdentifierListNode(IdentifierListNode identListNode, Queue resolvedNames) + { + var resolvedName = resolvedNames.Dequeue(); + + // if resolved name is null or empty, return the identifier list node as is + if (string.IsNullOrEmpty(resolvedName)) + return identListNode; + + string[] strIdentList = resolvedName.Split('.'); + Validity.Assert(strIdentList.Length >= 2); + + var newIdentList = new IdentifierListNode + { + LeftNode = new IdentifierNode(strIdentList[0]), + RightNode = new IdentifierNode(strIdentList[1]), + Optr = Operator.dot + }; + for (var n = 2; n < strIdentList.Length; ++n) + { + var subIdentList = new IdentifierListNode + { + LeftNode = newIdentList, + RightNode = new IdentifierNode(strIdentList[n]), + Optr = Operator.dot + }; + newIdentList = subIdentList; + } + + // The last ident list for the functioncall or identifier rhs + var lastIdentList = new IdentifierListNode + { + LeftNode = newIdentList, + RightNode = identListNode.RightNode, + Optr = Operator.dot + }; + + return lastIdentList; + } + + #endregion + } +} diff --git a/src/Engine/ProtoCore/ProtoCore.csproj b/src/Engine/ProtoCore/ProtoCore.csproj index 302ebcd49f8..9801a798dc3 100644 --- a/src/Engine/ProtoCore/ProtoCore.csproj +++ b/src/Engine/ProtoCore/ProtoCore.csproj @@ -136,6 +136,7 @@ + diff --git a/src/Engine/ProtoCore/Utils/CoreUtils.cs b/src/Engine/ProtoCore/Utils/CoreUtils.cs index 8530b48ed55..8872b01d7e9 100644 --- a/src/Engine/ProtoCore/Utils/CoreUtils.cs +++ b/src/Engine/ProtoCore/Utils/CoreUtils.cs @@ -2,6 +2,7 @@ using ProtoCore.DSASM; using System.Collections.Generic; using ProtoCore.AST.AssociativeAST; +using System; namespace ProtoCore.Utils { @@ -734,15 +735,41 @@ public static string GetIdentifierStringUntilFirstParenthesis(ProtoCore.AST.Asso return identListString; } + /// + /// Retrieves the string format of the identifier list from left to right, leaving out any symbols after the last identifier. + /// Given: A.B() + /// Return: "A" + /// Given: A.B.C()[0] + /// Return: "A.B" + /// Given: A.B().C + /// Return: "A" + /// Given: A.B[0].C + /// Return: "A.B[0].C" + /// + /// + /// + public static string GetIdentifierExceptMethodName(ProtoCore.AST.AssociativeAST.IdentifierListNode identList) + { + Validity.Assert(null != identList); + string identListString = identList.ToString(); + int removeIndex = identListString.IndexOf('('); + if (removeIndex > 0) + { + identListString = identListString.Remove(removeIndex); + identListString = identListString.Remove(identListString.LastIndexOf('.')); + } + return identListString; + } + /// /// Traverses the identifierlist argument until class name resolution succeeds or fails. /// /// /// /// - public static string[] GetResolvedClassName(ProtoCore.DSASM.ClassTable classTable, ProtoCore.AST.AssociativeAST.IdentifierListNode identList) + public static string[] GetResolvedClassName(ClassTable classTable, IdentifierListNode identList) { - string[] classNames = classTable.GetAllMatchingClasses(ProtoCore.Utils.CoreUtils.GetIdentifierStringUntilFirstParenthesis(identList)); + string[] classNames = classTable.GetAllMatchingClasses(GetIdentifierStringUntilFirstParenthesis(identList)); // Failed to find the first time // Attempt to remove identifiers in the identifierlist until we find a class or not @@ -753,11 +780,11 @@ public static string[] GetResolvedClassName(ProtoCore.DSASM.ClassTable classTabl if (leftNode is IdentifierListNode) { identList = leftNode as IdentifierListNode; - classNames = classTable.GetAllMatchingClasses(ProtoCore.Utils.CoreUtils.GetIdentifierStringUntilFirstParenthesis(identList)); + classNames = classTable.GetAllMatchingClasses(GetIdentifierStringUntilFirstParenthesis(identList)); } if (leftNode is IdentifierNode) { - IdentifierNode identNode = leftNode as IdentifierNode; + var identNode = leftNode as IdentifierNode; classNames = classTable.GetAllMatchingClasses(identNode.Name); break; } @@ -769,6 +796,24 @@ public static string[] GetResolvedClassName(ProtoCore.DSASM.ClassTable classTabl return classNames; } + /// + /// Given a partial class name, get assembly to which the class belongs + /// + /// class table in Core + /// class name + /// assembly to which the class belongs + public static string GetAssemblyFromClassName(ClassTable classTable, string className) + { + //throw new NotImplementedException(); + var ci = classTable.IndexOf(className); + + if (ci == ProtoCore.DSASM.Constants.kInvalidIndex) + return string.Empty; + + var classNode = classTable.ClassNodes[ci]; + return classNode.ExternLib; + } + /// /// Parses designscript code and outputs ProtoAST /// diff --git a/test/DynamoCoreTests/CodeBlockNodeTests.cs b/test/DynamoCoreTests/CodeBlockNodeTests.cs index 7c4bf3f5c12..0d07a9ba28d 100644 --- a/test/DynamoCoreTests/CodeBlockNodeTests.cs +++ b/test/DynamoCoreTests/CodeBlockNodeTests.cs @@ -8,12 +8,14 @@ using Dynamo.Utilities; using ProtoCore.DSASM; using Dynamo.Models; +using ProtoCore.Namespace; using DynCmd = Dynamo.Models.DynamoModel; using ProtoCore.Mirror; using Dynamo.DSEngine; using ProtoCore.Utils; using Dynamo.DSEngine.CodeCompletion; using Dynamo.UI; +using System.Xml; namespace Dynamo.Tests { @@ -809,6 +811,33 @@ public void TypedIdentifier_AssignedToDifferentType_ThrowsWarning2() #endregion + #region Codeblock Namespace Resolution Tests + + [Test] + public void Resolve_NamespaceConflict_LoadLibrary() + { + string code = "Point.ByCoordinates(10,0,0);"; + + var cbn = CreateCodeBlockNode(); + + UpdateCodeBlockNodeContent(cbn, code); + Assert.AreEqual(1, cbn.OutPortData.Count); + + // FFITarget introduces conflicts with Point class in + // FFITarget.Dummy.Point, FFITarget.Dynamo.Point + const string libraryPath = "FFITarget.dll"; + + CompilerUtils.TryLoadAssemblyIntoCore( + ViewModel.Model.LibraryServices.LibraryManagementCore, libraryPath); + + code = "Point.ByCoordinates(0,0,0);"; + UpdateCodeBlockNodeContent(cbn, code); + Assert.AreEqual(0, ViewModel.Model.LibraryServices.LibraryManagementCore.BuildStatus.Warnings.Count()); + } + + #endregion + + private CodeBlockNodeModel CreateCodeBlockNode() { var cbn = new CodeBlockNodeModel(ViewModel.Model.LibraryServices); @@ -827,6 +856,7 @@ private void UpdateCodeBlockNodeContent(CodeBlockNodeModel cbn, string value) } } + public class CodeBlockCompletionTests { private ProtoCore.Core libraryServicesCore = null; diff --git a/test/DynamoCoreTests/CoreTests.cs b/test/DynamoCoreTests/CoreTests.cs index 6b4eb3c32f8..326bab244f1 100644 --- a/test/DynamoCoreTests/CoreTests.cs +++ b/test/DynamoCoreTests/CoreTests.cs @@ -169,7 +169,8 @@ public void ValidateConnectionsDoesNotClearError() Assert.AreEqual(ElementState.Active, codeBlockNode.State); // Entering an invalid value will cause it to be erroneous. - codeBlockNode.SetCodeContent("--"); // Invalid numeric value. + var elementResolver = model.CurrentWorkspace.ElementResolver; + codeBlockNode.SetCodeContent("--", elementResolver); // Invalid numeric value. Assert.AreEqual(ElementState.Error, codeBlockNode.State); Assert.IsNotEmpty(codeBlockNode.ToolTipText); // Error tooltip text. @@ -189,7 +190,7 @@ public void ValidateConnectionsDoesNotClearError() Assert.IsNotEmpty(codeBlockNode.ToolTipText); // Error tooltip text. // Update to valid numeric value, should cause the node to be active. - codeBlockNode.SetCodeContent("1234;"); + codeBlockNode.SetCodeContent("1234;", elementResolver); Assert.AreEqual(ElementState.Active, codeBlockNode.State); Assert.IsEmpty(codeBlockNode.ToolTipText); // Error tooltip is gone. } diff --git a/test/DynamoCoreUITests/VisualizationManagerUITests.cs b/test/DynamoCoreUITests/VisualizationManagerUITests.cs index 60122abd064..807fa05cd15 100644 --- a/test/DynamoCoreUITests/VisualizationManagerUITests.cs +++ b/test/DynamoCoreUITests/VisualizationManagerUITests.cs @@ -14,6 +14,7 @@ using NUnit.Framework; using Dynamo.Selection; +using ProtoCore.Namespace; namespace DynamoCoreUITests { @@ -398,7 +399,8 @@ public void CanDrawNodeLabels() var cbn = model.CurrentWorkspace.Nodes.FirstOrDefault(x => x.GUID.ToString() == "fdec3b9b-56ae-4d01-85c2-47b8425e3130") as CodeBlockNodeModel; Assert.IsNotNull(cbn); - cbn.SetCodeContent("Point.ByCoordinates(a<1>,a<1>,a<1>);"); + var elementResolver = model.CurrentWorkspace.ElementResolver; + cbn.SetCodeContent("Point.ByCoordinates(a<1>,a<1>,a<1>);", elementResolver); var ws = ViewModel.Model.CurrentWorkspace as HomeWorkspaceModel; ws.RunSettings.RunType = RunType.Automatic; @@ -411,7 +413,7 @@ public void CanDrawNodeLabels() cbn.DisplayLabels = true; Assert.AreEqual(BackgroundPreview.Text.Count(), 4); - cbn.SetCodeContent("Point.ByCoordinates(a<1>,a<2>,a<3>);"); + cbn.SetCodeContent("Point.ByCoordinates(a<1>,a<2>,a<3>);", elementResolver); //change the lacing to cross product //ensure that the labels update to match diff --git a/test/Engine/FFITarget/ElementResolverTarget.cs b/test/Engine/FFITarget/ElementResolverTarget.cs new file mode 100644 index 00000000000..54024b375da --- /dev/null +++ b/test/Engine/FFITarget/ElementResolverTarget.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FFITarget +{ + public class ElementResolverTarget + { + } +} diff --git a/test/Engine/FFITarget/FFITarget.csproj b/test/Engine/FFITarget/FFITarget.csproj index f8b7138c5f7..5027babca33 100644 --- a/test/Engine/FFITarget/FFITarget.csproj +++ b/test/Engine/FFITarget/FFITarget.csproj @@ -51,6 +51,7 @@ + Code diff --git a/test/Engine/ProtoTest/FFITests/ElementRewriterTests.cs b/test/Engine/ProtoTest/FFITests/ElementRewriterTests.cs new file mode 100644 index 00000000000..2b704d3b779 --- /dev/null +++ b/test/Engine/ProtoTest/FFITests/ElementRewriterTests.cs @@ -0,0 +1,67 @@ +using System.Linq; +using NUnit.Framework; +using ProtoCore.AST.AssociativeAST; +using ProtoCore.Namespace; +using ProtoCore.Utils; + +namespace ProtoTest.FFITests +{ + [TestFixture] + class ElementRewriterTests : ProtoTestBase + { + [Test] + public void LookupResolvedName_FromElementResolver_RewriteAst() + { + var astNodes = CoreUtils.BuildASTList(core, "p = Point.ByCoordinates(0,0,0);"); + var astNode = astNodes[0]; + + var elementResolver = new ElementResolver(); + elementResolver.AddToResolutionMap("Point", "Autodesk.DS.Geometry.Point", "Protogeometry.dll"); + + ElementRewriter.LookupResolvedNameAndRewriteAst(core.ClassTable, elementResolver, ref astNode); + + Assert.AreEqual("p = Autodesk.DS.Geometry.Point.ByCoordinates(0, 0, 0);\n", astNode.ToString()); + } + + [Test] + public void LookupResolvedName_FromCompiler_RewriteAst() + { + + string code = @"import (""FFITarget.dll"");"; + var mirror = thisTest.RunScriptSource(code); + + var testCore = thisTest.GetTestCore(); + var astNodes = CoreUtils.BuildASTList(testCore, "d = ElementResolverTarget.ElementResolverTarget();"); + var astNode = astNodes[0]; + + var elementResolver = new ElementResolver(); + + ElementRewriter.LookupResolvedNameAndRewriteAst(testCore.ClassTable, elementResolver, ref astNode); + + Assert.AreEqual("d = FFITarget.ElementResolverTarget.ElementResolverTarget();\n", astNode.ToString()); + + // Add verification for contents of element resolver resolution map + Assert.AreEqual(1, elementResolver.ResolutionMap.Count); + + var assembly = elementResolver.LookupAssemblyName("ElementResolverTarget"); + var resolvedName = elementResolver.LookupResolvedName("ElementResolverTarget"); + + Assert.AreEqual("FFITarget.dll", assembly); + Assert.AreEqual("FFITarget.ElementResolverTarget", resolvedName); + } + + // Add tests for ElementRewriter.GetClassIdentifiers() api + [Test] + public void GetClassIdentifiers_FromAst() + { + var astNodes = CoreUtils.BuildASTList(core, "d = Point.ByCoordinates(0, 0, pt.X + 5);"); + var astNode = astNodes[0]; + + var identifiers = ElementRewriter.GetClassIdentifiers(astNode); + var identifierListNodes = identifiers as IdentifierListNode[] ?? identifiers.ToArray(); + Assert.AreEqual(2, identifierListNodes.Count()); + Assert.AreEqual("pt.X", identifierListNodes.ElementAt(0).ToString()); + Assert.AreEqual("Point.ByCoordinates(0, 0, (pt.X) + 5)", identifierListNodes.ElementAt(1).ToString()); + } + } +} diff --git a/test/Engine/ProtoTest/GraphCompiler/NewFrontEndTests.cs b/test/Engine/ProtoTest/GraphCompiler/NewFrontEndTests.cs index a59a6236f41..b567f551b1d 100644 --- a/test/Engine/ProtoTest/GraphCompiler/NewFrontEndTests.cs +++ b/test/Engine/ProtoTest/GraphCompiler/NewFrontEndTests.cs @@ -4,6 +4,7 @@ using System.Text; using NUnit.Framework; using ProtoCore.AST.AssociativeAST; +using ProtoCore.Namespace; using ProtoCore.Utils; using ProtoTestFx.TD; @@ -20,7 +21,9 @@ public void ReproMAGN3603() b = (1 + 2) * 3; c = 1 + 2 * 3;"; - ParseParam parseParam = new ParseParam(Guid.NewGuid(), code); + ElementResolver elementResolver = new ElementResolver(); + ParseParam parseParam = new ParseParam(Guid.NewGuid(), code, elementResolver); + Assert.IsTrue(CompilerUtils.PreCompileCodeBlock(thisTest.CreateTestCore(), ref parseParam)); Assert.IsTrue(parseParam.ParsedNodes != null && parseParam.ParsedNodes.Count() > 0); diff --git a/test/Engine/ProtoTest/ProtoTest.csproj b/test/Engine/ProtoTest/ProtoTest.csproj index dba357c9468..1ac55809fa9 100644 --- a/test/Engine/ProtoTest/ProtoTest.csproj +++ b/test/Engine/ProtoTest/ProtoTest.csproj @@ -130,6 +130,7 @@ + diff --git a/test/Engine/ProtoTest/UtilsTests/CoreUtilsTest.cs b/test/Engine/ProtoTest/UtilsTests/CoreUtilsTest.cs index 59ef3430caf..676ce72a853 100644 --- a/test/Engine/ProtoTest/UtilsTests/CoreUtilsTest.cs +++ b/test/Engine/ProtoTest/UtilsTests/CoreUtilsTest.cs @@ -1,13 +1,7 @@ -using System; using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using ProtoCore.AST.AssociativeAST; -using ProtoCore.DSASM; -using ProtoCore.DSASM.Mirror; using ProtoCore.Utils; -using ProtoFFI; -using ProtoTestFx.TD; namespace ProtoTest.UtilsTests { [TestFixture] @@ -15,11 +9,11 @@ class CoreUtilsTest : ProtoTestBase { private bool Test_GetIdentifierStringUntilFirstParenthesis(string input, string expected) { - List astList = ProtoCore.Utils.CoreUtils.BuildASTList(core, input); + List astList = CoreUtils.BuildASTList(core, input); IdentifierListNode identList = (astList[0] as BinaryExpressionNode).RightNode as IdentifierListNode; // Verify expected string - string identListString = ProtoCore.Utils.CoreUtils.GetIdentifierStringUntilFirstParenthesis(identList); + string identListString = CoreUtils.GetIdentifierStringUntilFirstParenthesis(identList); return identListString.Equals(expected); } @@ -43,7 +37,7 @@ public void Test_GetIdentifierStringUntilFirstParenthesis_02() Assert.IsTrue(Test_GetIdentifierStringUntilFirstParenthesis(input, expected)); } - [Test] + [Test] public void Test_GetIdentifierStringUntilFirstParenthesis_03() { // Given: A.B().C() @@ -53,7 +47,7 @@ public void Test_GetIdentifierStringUntilFirstParenthesis_03() Assert.IsTrue(Test_GetIdentifierStringUntilFirstParenthesis(input, expected)); } - [Test] + [Test] public void Test_GetIdentifierStringUntilFirstParenthesis_04() { // Given: A.B[0].C @@ -62,5 +56,55 @@ public void Test_GetIdentifierStringUntilFirstParenthesis_04() string expected = "A.B[0].C"; Assert.IsTrue(Test_GetIdentifierStringUntilFirstParenthesis(input, expected)); } + + private bool Test_GetIdentifierExceptMethodName(string input, string expected) + { + var astList = CoreUtils.BuildASTList(core, input); + var identList = (astList[0] as BinaryExpressionNode).RightNode as IdentifierListNode; + + // Verify expected string + string identListString = CoreUtils.GetIdentifierExceptMethodName(identList); + return identListString.Equals(expected); + } + + [Test] + public void Test_GetIdentifierExceptMethodName_01() + { + // Given: A.B() + // Return: "A" + string input = "p = A.B();"; + string expected = "A"; + Assert.IsTrue(Test_GetIdentifierExceptMethodName(input, expected)); + } + + [Test] + public void Test_GetIdentifierExceptMethodName_02() + { + // Given: A.B.C()[0] + // Return: "A.B" + string input = "p = A.B.C()[0];"; + string expected = "A.B"; + Assert.IsTrue(Test_GetIdentifierExceptMethodName(input, expected)); + } + + [Test] + public void Test_GetIdentifierExceptMethodName_03() + { + // Given: A.B().C + // Return: "A" + string input = "p = A.B().C;"; + string expected = "A"; + Assert.IsTrue(Test_GetIdentifierExceptMethodName(input, expected)); + } + + [Test] + public void Test_GetIdentifierExceptMethodName_04() + { + // Given: A.B[0].C + // Return: "A.B[0].C" + string input = "p = A.B[0].C;"; + string expected = "A.B[0].C"; + Assert.IsTrue(Test_GetIdentifierExceptMethodName(input, expected)); + } } }