/// <summary> /// Update the cached ASTs in the subtree given the modified ASTs /// </summary> /// <param name="st"></param> /// <param name="modifiedASTList"></param> private void UpdateCachedASTList(Subtree st, List<AssociativeNode> modifiedASTList) { // Disable removed nodes from the cache Subtree oldSubTree; bool cachedTreeExists = currentSubTreeList.TryGetValue(st.GUID, out oldSubTree); if (cachedTreeExists && oldSubTree.AstNodes != null) { List<AssociativeNode> removedNodes = null; if (st.IsInput) { removedNodes = GetInactiveASTList(oldSubTree.AstNodes, st.AstNodes); (modifiedASTList[0] as BinaryExpressionNode).OriginalAstID = (removedNodes[0] as BinaryExpressionNode).OriginalAstID; } else if (!st.ForceExecution) { removedNodes = GetInactiveASTList(oldSubTree.AstNodes, st.AstNodes); // We only need the removed binary ASTs // Function definitions are handled in ChangeSetData.RemovedFunctionDefNodesFromModification csData.RemovedBinaryNodesFromModification.AddRange(removedNodes.Where(n => n is BinaryExpressionNode)); } foreach (var removedAST in csData.RemovedBinaryNodesFromModification) { core.BuildStatus.ClearWarningsForAst(removedAST.ID); runtimeCore.RuntimeStatus.ClearWarningsForAst(removedAST.ID); } } // Cache the modifed functions //var modifiedFunctions = st.AstNodes.Where(n => n is FunctionDefinitionNode); var modifiedFunctions = modifiedASTList.Where(n => n is FunctionDefinitionNode); csData.ModifiedFunctions.AddRange(modifiedFunctions); // Handle cached subtree if (!cachedTreeExists) { // Cache the subtree if it does not exist yet // This scenario is possible if a subtree was deleted and the same subtree was added again as a modified subtree currentSubTreeList.Add(st.GUID, st); } else { if (null == oldSubTree.AstNodes) { // The ast list for this subtree is null // This is due to the liverunner being passed an empty astlist, such as a codeblock with no content // Populate this subtree with the current ast contents oldSubTree.AstNodes = modifiedASTList; currentSubTreeList[st.GUID] = oldSubTree; } else { if (st.ForceExecution) { // Get the cached AST and append it to the changeSet csData.ForceExecuteASTList.AddRange(GetUnmodifiedASTList(oldSubTree.AstNodes, st.AstNodes)); } // Update the cached AST to reflect the change List<AssociativeNode> newCachedASTList = new List<AssociativeNode>(); // Get all the unomodified ASTs and append them to the cached ast list newCachedASTList.AddRange(GetUnmodifiedASTList(oldSubTree.AstNodes, st.AstNodes)); // Append all the modified ASTs to the cached ast list newCachedASTList.AddRange(modifiedASTList); // ================================================================================ // Get a list of functions that were removed // This is the list of functions that exist in oldSubTree.AstNodes and no longer exist in st.AstNodes // This will passed to the changeset applier to handle removed functions in the VM // ================================================================================ IEnumerable<AssociativeNode> removedFunctions = oldSubTree.AstNodes.Where(f => f is FunctionDefinitionNode && !st.AstNodes.Contains(f)); csData.RemovedFunctionDefNodesFromModification.AddRange(removedFunctions); st.AstNodes.Clear(); st.AstNodes.AddRange(newCachedASTList); currentSubTreeList[st.GUID] = st; } } }
/// <summary> /// Gets the only the modified nodes from the subtree by checking of the previous cached instance /// </summary> /// <param name="subtree"></param> /// <returns></returns> private List<AssociativeNode> GetModifiedNodes(Subtree subtree, out List<AssociativeNode> modifiedInputAST) { modifiedInputAST = new List<AssociativeNode>(); Subtree st; if (!currentSubTreeList.TryGetValue(subtree.GUID, out st) || st.AstNodes == null) { // If the subtree was not cached, it means the cache was delted // This means the current subtree is all modified return subtree.AstNodes; } // We want to process only modified statements // If the AST is identical to an existing AST in the same GUID, it means it was not modified List<AssociativeNode> modifiedASTList = new List<AssociativeNode>(); foreach (AssociativeNode node in subtree.AstNodes) { // Check if node exists in the prev AST list bool nodeFound = false; foreach (AssociativeNode prevNode in st.AstNodes) { if (prevNode.Equals(node)) { nodeFound = true; break; } } if (!nodeFound) { // At this point, the ast was determined to have been modified // It can then be handled normally regardless of its ForceExecution state subtree.ForceExecution = false; if (st.IsInput) { // An input node is not re-compiled and executed // It is handled by the ChangeSetApply by re-executing the modified node with the updated changes modifiedInputAST.Add(node); } else { modifiedASTList.Add(node); BinaryExpressionNode bnode = node as BinaryExpressionNode; if (null != bnode) { if (bnode.RightNode is LanguageBlockNode) { csData.ModifiedNestedLangBlock.Add(bnode); } else if (bnode.LeftNode is IdentifierNode) { string lhsName = (bnode.LeftNode as IdentifierNode).Name; Validity.Assert(null != lhsName && string.Empty != lhsName); if (CoreUtils.IsSSATemp(lhsName)) { // If the lhs of this binary expression is an SSA temp, and it existed in the lhs of any cached nodes, // this means that it was a modified variable within the previous expression. // Inherit its expression ID foreach (AssociativeNode prevNode in st.AstNodes) { BinaryExpressionNode prevBinaryNode = prevNode as BinaryExpressionNode; if (null != prevBinaryNode) { IdentifierNode prevIdent = prevBinaryNode.LeftNode as IdentifierNode; if (null != prevIdent) { if (prevIdent.Equals(bnode.LeftNode as IdentifierNode)) { bnode.InheritID(prevBinaryNode.ID); bnode.ExpressionUID = prevBinaryNode.ExpressionUID; } } } } } else { // Handle re-defined lhs expressions HandleRedefinedLHS(bnode, st.AstNodes); } } } } } } return modifiedASTList; }
private IEnumerable<AssociativeNode> GetDeltaAstListDeleted(IEnumerable<Subtree> deletedSubTrees) { var deltaAstList = new List<AssociativeNode>(); csData.DeletedBinaryExprASTNodes = new List<AssociativeNode>(); csData.DeletedFunctionDefASTNodes = new List<AssociativeNode>(); if (deletedSubTrees != null) { foreach (var st in deletedSubTrees) { if (st.AstNodes != null && st.AstNodes.Count > 0) { csData.DeletedBinaryExprASTNodes.AddRange(st.AstNodes); } else { // Handle the case where only the GUID of the deleted subtree was provided // Get the cached subtree that is now being deleted Subtree removeSubTree = new Subtree(); if (currentSubTreeList.TryGetValue(st.GUID, out removeSubTree)) { if (removeSubTree.AstNodes != null) { csData.DeletedBinaryExprASTNodes.AddRange(removeSubTree.AstNodes); } } } // Cache removed function definitions Subtree oldSubTree; if (currentSubTreeList.TryGetValue(st.GUID, out oldSubTree)) { if (oldSubTree.AstNodes != null) { csData.DeletedFunctionDefASTNodes.AddRange(oldSubTree.AstNodes.Where(n => n is FunctionDefinitionNode)); } currentSubTreeList.Remove(st.GUID); } // Build the nullify ASTs var nullNodes = BuildNullAssignments(csData.DeletedBinaryExprASTNodes); deltaAstList.AddRange(nullNodes); foreach (AssociativeNode node in deltaAstList) { var bnode = node as BinaryExpressionNode; if (bnode != null) { bnode.guid = st.GUID; } } core.BuildStatus.ClearWarningsForGraph(st.GUID); runtimeCore.RuntimeStatus.ClearWarningsForGraph(st.GUID); } } return deltaAstList; }
[Test] public void RegressMAGN5353() { // This test case tries to verify that when a FFI object is deleted, // the corresponding _Dispose() should be invoked. // // It is for defect http://adsk-oss.myjetbrains.com/youtrack/issue/MAGN-5353 var added = new List<Subtree>(); var guid1 = Guid.NewGuid(); var code1 = @"import(""FFITarget.dll""); x = DisposeTracer(); DisposeTracer.DisposeCount = 0;"; added.Add(ProtoTestFx.TD.TestFrameWork.CreateSubTreeFromCode(guid1, code1)); var guid2 = Guid.NewGuid(); var code2 = "y = DisposeTracer.DisposeCount;"; added.Add(ProtoTestFx.TD.TestFrameWork.CreateSubTreeFromCode(guid2, code2)); // Verify that UpateCount is only called once var syncData = new GraphSyncData(null, added, null); liveRunner.UpdateGraph(syncData); AssertValue("y", 0); // Modify CBN2 Subtree subtree = new Subtree(new List<AssociativeNode>{}, guid1); List<Subtree> deleted = new List<Subtree>(); deleted.Add(subtree); var guid3 = Guid.NewGuid(); var code3 = "__GC();"; syncData = new GraphSyncData(deleted, new List<Subtree> { ProtoTestFx.TD.TestFrameWork.CreateSubTreeFromCode(guid3, code3)}, null); liveRunner.UpdateGraph(syncData); AssertValue("y", 1);
private bool VerifyGraphSyncData(IEnumerable<NodeModel> nodes) { GraphSyncData graphSyncdata = syncDataManager.GetSyncData(); syncDataManager.ResetStates(); var reExecuteNodesIds = new HashSet<Guid>( nodes.Where(n => n.NeedsForceExecution) .Select(n => n.GUID)); if (reExecuteNodesIds.Any()) { for (int i = 0; i < graphSyncdata.ModifiedSubtrees.Count; ++i) { var st = graphSyncdata.ModifiedSubtrees[i]; if (reExecuteNodesIds.Contains(st.GUID)) { Subtree newSt = new Subtree(st.AstNodes, st.GUID); newSt.ForceExecution = true; graphSyncdata.ModifiedSubtrees[i] = newSt; } } } if (graphSyncdata.AddedSubtrees.Any() || graphSyncdata.ModifiedSubtrees.Any() || graphSyncdata.DeletedSubtrees.Any()) { lock (graphSyncDataQueue) { graphSyncDataQueue.Enqueue(graphSyncdata); } return true; } return false; }
private bool VerifyGraphSyncData() { GraphSyncData data = syncDataManager.GetSyncData(); syncDataManager.ResetStates(); var reExecuteNodesIds = controller.DynamoViewModel.Model.HomeSpace.Nodes .Where(n => n.ForceReExecuteOfNode) .Select(n => n.GUID); if (reExecuteNodesIds.Any() && data.ModifiedSubtrees != null) { for (int i = 0; i < data.ModifiedSubtrees.Count; ++i) { var st = data.ModifiedSubtrees[i]; if (reExecuteNodesIds.Contains(st.GUID)) { Subtree newSt = new Subtree(st.AstNodes, st.GUID); newSt.ForceExecution = true; data.ModifiedSubtrees[i] = newSt; } } } if ((data.AddedSubtrees != null && data.AddedSubtrees.Count > 0) || (data.ModifiedSubtrees != null && data.ModifiedSubtrees.Count > 0) || (data.DeletedSubtrees != null && data.DeletedSubtrees.Count > 0)) { lock (graphSyncDataQueue) { graphSyncDataQueue.Enqueue(data); } return true; } return false; }
/// <summary> /// Gets the only the modified nodes from the subtree by checking of the previous cached instance /// </summary> /// <param name="subtree"></param> /// <returns></returns> private List<AssociativeNode> GetModifiedNodes(Subtree subtree) { Subtree st; if (!currentSubTreeList.TryGetValue(subtree.GUID, out st) || st.AstNodes == null) { // If the subtree was not cached, it means the cache was delted // This means the current subtree is all modified return subtree.AstNodes; } // We want to process only modified statements // If the AST is identical to an existing AST in the same GUID, it means it was not modified List<AssociativeNode> modifiedASTList = new List<AssociativeNode>(); foreach (AssociativeNode node in subtree.AstNodes) { // Check if node exists in the prev AST list bool nodeFound = false; if (!subtree.ForceExecution) { foreach (AssociativeNode prevNode in st.AstNodes) { if (prevNode.Equals(node)) { nodeFound = true; break; } } } if (!nodeFound) { // node is modifed as it does not match any existing modifiedASTList.Add(node); BinaryExpressionNode bnode = node as BinaryExpressionNode; if (null != bnode && bnode.LeftNode is IdentifierNode) { string lhsName = (bnode.LeftNode as IdentifierNode).Name; Validity.Assert(null != lhsName && string.Empty != lhsName); if (CoreUtils.IsSSATemp(lhsName)) { // If the lhs of this binary expression is an SSA temp, and it existed in the lhs of any cached nodes, // this means that it was a modified variable within the previous expression. // Inherit its expression ID foreach (AssociativeNode prevNode in st.AstNodes) { BinaryExpressionNode prevBinaryNode = prevNode as BinaryExpressionNode; if (null != prevBinaryNode) { IdentifierNode prevIdent = prevBinaryNode.LeftNode as IdentifierNode; if (null != prevIdent) { if (prevIdent.Equals(bnode.LeftNode as IdentifierNode)) { bnode.InheritID(prevBinaryNode.ID); bnode.exprUID = prevBinaryNode.exprUID; } } } } } else { // Handle re-defined lhs expressions HandleRedefinedLHS(bnode, st.AstNodes); } } } } return modifiedASTList; }