From e52971e799501c03f601144a421ab045264a51dd Mon Sep 17 00:00:00 2001 From: howsohazard <143410553+howsohazard@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:16:43 -0500 Subject: [PATCH] 18479: Improves performance and memory reuse (#34) --- src/Amalgam/SeparableBoxFilterDataStore.cpp | 6 +- src/Amalgam/entity/Entity.cpp | 6 -- src/Amalgam/evaluablenode/EvaluableNode.h | 1 + .../evaluablenode/EvaluableNodeManagement.h | 52 ++++++++++++++ .../interpreter/InterpreterOpcodesBase.cpp | 10 +-- .../InterpreterOpcodesEntityAccess.cpp | 10 +-- .../InterpreterOpcodesListManipulation.cpp | 22 +++--- .../interpreter/InterpreterOpcodesLogic.cpp | 68 +++++++------------ .../interpreter/InterpreterOpcodesMath.cpp | 31 ++++----- .../InterpreterOpcodesTransformations.cpp | 38 +---------- 10 files changed, 120 insertions(+), 124 deletions(-) diff --git a/src/Amalgam/SeparableBoxFilterDataStore.cpp b/src/Amalgam/SeparableBoxFilterDataStore.cpp index 3a12eaf7..675ab66a 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.cpp +++ b/src/Amalgam/SeparableBoxFilterDataStore.cpp @@ -12,6 +12,10 @@ void SeparableBoxFilterDataStore::BuildLabel(size_t column_index, const std::vec auto &column_data = columnData[column_index]; auto label_id = column_data->stringId; + //if the label is accessible, then don't need to check every label for being private, + //can just inform entity to get on self for performance + bool is_label_accessible = !Entity::IsLabelPrivate(label_id); + auto &entities_with_number_values = parametersAndBuffers.entitiesWithValues; entities_with_number_values.clear(); @@ -25,7 +29,7 @@ void SeparableBoxFilterDataStore::BuildLabel(size_t column_index, const std::vec { EvaluableNodeImmediateValueType value_type; EvaluableNodeImmediateValue value; - value_type = entities[entity_index]->GetValueAtLabelAsImmediateValue(label_id, value); + value_type = entities[entity_index]->GetValueAtLabelAsImmediateValue(label_id, value, is_label_accessible); GetValue(entity_index, column_index) = value; column_data->InsertNextIndexValueExceptNumbers(value_type, value, entity_index, entities_with_number_values); diff --git a/src/Amalgam/entity/Entity.cpp b/src/Amalgam/entity/Entity.cpp index e6304243..416795f5 100644 --- a/src/Amalgam/entity/Entity.cpp +++ b/src/Amalgam/entity/Entity.cpp @@ -219,12 +219,6 @@ bool Entity::GetValueAtLabelAsString(StringInternPool::StringID label_sid, std:: EvaluableNodeImmediateValueType Entity::GetValueAtLabelAsImmediateValue(StringInternPool::StringID label_sid, EvaluableNodeImmediateValue &value_out, bool on_self) { - if(label_sid <= StringInternPool::EMPTY_STRING_ID) - { - value_out.number = std::numeric_limits::quiet_NaN(); - return ENIVT_NOT_EXIST; - } - if(!on_self && IsLabelPrivate(label_sid)) { value_out.number = std::numeric_limits::quiet_NaN(); diff --git a/src/Amalgam/evaluablenode/EvaluableNode.h b/src/Amalgam/evaluablenode/EvaluableNode.h index 1056528f..560af610 100644 --- a/src/Amalgam/evaluablenode/EvaluableNode.h +++ b/src/Amalgam/evaluablenode/EvaluableNode.h @@ -439,6 +439,7 @@ class EvaluableNode //fully clears node and sets it to new_type inline void ClearAndSetType(EvaluableNodeType new_type) { + ClearMetadata(); DestructValue(); InitializeType(new_type); } diff --git a/src/Amalgam/evaluablenode/EvaluableNodeManagement.h b/src/Amalgam/evaluablenode/EvaluableNodeManagement.h index a48a88ee..58ec462f 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeManagement.h +++ b/src/Amalgam/evaluablenode/EvaluableNodeManagement.h @@ -199,6 +199,58 @@ class EvaluableNodeManager }; EvaluableNode *AllocNode(EvaluableNode *original, EvaluableNodeMetadataModifier metadata_modifier = ENMM_NO_CHANGE); + //attempts to reuse candidate if it is unique and change it into the specified type + //if candidate is not unique, then it allocates and returns a new node + inline EvaluableNodeReference ReuseOrAllocNode(EvaluableNodeReference candidate, EvaluableNodeType type) + { + if(candidate.unique && candidate != nullptr) + { + //if not cyclic, can attempt to free all child nodes + //if cyclic, don't try, in case a child node points back to candidate + if(!candidate->GetNeedCycleCheck()) + { + if(candidate->IsAssociativeArray()) + { + for(auto &[_, e] : candidate->GetMappedChildNodesReference()) + { + if(e != nullptr) + FreeNodeTreeRecurse(e); + } + } + else if(!candidate->IsImmediate()) + { + for(auto &e : candidate->GetOrderedChildNodesReference()) + { + if(e != nullptr) + FreeNodeTreeRecurse(e); + } + } + } + + candidate->ClearAndSetType(type); + return candidate; + } + else + { + return EvaluableNodeReference(AllocNode(type), true); + } + } + + //like ReuseOrAllocNode, but picks whichever node is reusable and frees the other if possible + //will try candidate_1 first + inline EvaluableNodeReference ReuseOrAllocOneOfNodes( + EvaluableNodeReference candidate_1, EvaluableNodeReference candidate_2, EvaluableNodeType type) + { + if(candidate_1.unique && candidate_1 != nullptr) + { + FreeNodeTreeIfPossible(candidate_2); + return ReuseOrAllocNode(candidate_1, type); + } + + //candidate_1 wasn't unique, so try for candidate 2 + return ReuseOrAllocNode(candidate_2, type); + } + //Copies the data structure and everything underneath it, modifying labels as specified // if cycle_free is true on input, then it can perform a faster copy inline EvaluableNodeReference DeepAllocCopy(EvaluableNode *tree, EvaluableNodeMetadataModifier metadata_modifier = ENMM_NO_CHANGE) diff --git a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp index 4a2e002e..2a2d8adb 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp @@ -283,9 +283,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNPARSE(EvaluableNode *en) auto tree = InterpretNodeForImmediateUse(ocn[0]); std::string s = Parser::Unparse(tree, evaluableNodeManager, pretty, true, deterministic_order); - evaluableNodeManager->FreeNodeTreeIfPossible(tree); - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_STRING, s), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(tree, ENT_STRING); + result->SetStringValue(s); + return result; } EvaluableNodeReference Interpreter::InterpretNode_ENT_IF(EvaluableNode *en) @@ -1410,14 +1411,12 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RAND(EvaluableNode *en) //want to generate multiple values, so return a list //try to reuse param if can so don't need to allocate more memory EvaluableNodeReference retval; - bool free_param = false; if(param.unique) { retval = param; } else { - free_param = true; retval = EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_LIST), true); retval->SetOrderedChildNodes(param->GetOrderedChildNodes()); } @@ -1435,9 +1434,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RAND(EvaluableNode *en) //free unneeded nodes that weren't part of the shuffle if(param.unique && !param->GetNeedCycleCheck()) { - if(free_param) - evaluableNodeManager->FreeNodeIfPossible(param); - for(size_t i = number_to_generate; i < num_elements; i++) evaluableNodeManager->FreeNodeTree(retval_ocn[i]); } diff --git a/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp b/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp index 8587de9c..d0edd17f 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp @@ -478,11 +478,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_ENTITY_and_CALL_ENTIT EvaluableNodeReference call_stack = ConvertArgsToCallStack(args, evaluableNodeManager); node_stack.PushEvaluableNode(call_stack); - //get a write lock on the entity - EntityWriteReference called_entity = InterpretNodeIntoRelativeSourceEntityWriteReferenceFromInterpretedEvaluableNodeIDPath(ocn[0]); - if(called_entity == nullptr) - return EvaluableNodeReference::Null(); - //current pointer to write listeners std::vector *cur_write_listeners = writeListeners; //another storage container in case getting entity changes @@ -497,6 +492,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_ENTITY_and_CALL_ENTIT cur_write_listeners = &get_changes_write_listeners; } + //get a write lock on the entity + EntityWriteReference called_entity = InterpretNodeIntoRelativeSourceEntityWriteReferenceFromInterpretedEvaluableNodeIDPath(ocn[0]); + if(called_entity == nullptr) + return EvaluableNodeReference::Null(); + ExecutionCycleCount num_steps_executed = 0; size_t num_nodes_allocated = 0; EvaluableNodeReference retval = called_entity->Execute(num_steps_allowed, num_steps_executed, diff --git a/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp b/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp index f2d0a0f4..d193aa03 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp @@ -212,9 +212,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_TAIL(EvaluableNode *en) //drop the number of characters before this length size_t utf8_start_offset = StringManipulation::GetNthUTF8CharacterOffset(s, num_chars_to_drop); - evaluableNodeManager->FreeNodeTreeIfPossible(list); - - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_STRING, s.substr(utf8_start_offset, s.size() - utf8_start_offset)), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_STRING); + result->SetStringValue(s.substr(utf8_start_offset, s.size() - utf8_start_offset)); + return result; } if(DoesEvaluableNodeTypeUseNumberData(list->GetType())) @@ -224,10 +224,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_TAIL(EvaluableNode *en) if(value == 0.0) return list; - evaluableNodeManager->FreeNodeTreeIfPossible(list); - //return (value - 1.0) if nonzero - return EvaluableNodeReference(evaluableNodeManager->AllocNode(value - 1.0), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_NUMBER); + result->SetNumberValue(value - 1.0); + return result; } } @@ -300,8 +300,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LAST(EvaluableNode *en) auto [utf8_char_start_offset, utf8_char_length] = StringManipulation::GetLastUTF8CharacterOffsetAndLength(s); - evaluableNodeManager->FreeNodeTreeIfPossible(list); - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_STRING, s.substr(utf8_char_start_offset, utf8_char_length)), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_STRING); + result->SetStringValue(s.substr(utf8_char_start_offset, utf8_char_length)); + return result; } if(DoesEvaluableNodeTypeUseNumberData(list->GetType())) @@ -312,8 +313,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LAST(EvaluableNode *en) return list; //return 1 if nonzero - evaluableNodeManager->FreeNodeTreeIfPossible(list); - return EvaluableNodeReference(evaluableNodeManager->AllocNode(1.0), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_NUMBER); + result->SetNumberValue(1.0); + return result; } } diff --git a/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp b/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp index 269112e2..078aff7f 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp @@ -21,8 +21,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_AND(EvaluableNode *en) { - EvaluableNodeReference cur = EvaluableNodeReference::Null(); auto &ocn = en->GetOrderedChildNodes(); + if(ocn.size() == 0) + return EvaluableNodeReference::Null(); + + EvaluableNodeReference cur = EvaluableNodeReference::Null(); #ifdef MULTITHREAD_SUPPORT std::vector interpreted_nodes; @@ -36,10 +39,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_AND(EvaluableNode *en) cur = cn; if(!EvaluableNode::IsTrue(cur)) - { - evaluableNodeManager->FreeNodeTreeIfPossible(cur); - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true); - } + return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE); } return cur; @@ -54,49 +54,52 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_AND(EvaluableNode *en) cur = InterpretNode(cn); if(!EvaluableNode::IsTrue(cur)) - { - evaluableNodeManager->FreeNodeTreeIfPossible(cur); - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true); - } + return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE); } + return cur; } EvaluableNodeReference Interpreter::InterpretNode_ENT_OR(EvaluableNode *en) { auto &ocn = en->GetOrderedChildNodes(); - if(ocn.size() == 0) return EvaluableNodeReference::Null(); + EvaluableNodeReference cur = EvaluableNodeReference::Null(); + #ifdef MULTITHREAD_SUPPORT std::vector interpreted_nodes; if(InterpretEvaluableNodesConcurrently(en, ocn, interpreted_nodes)) { - for(auto &cur : interpreted_nodes) + for(auto &cn : interpreted_nodes) { + //free the previous node if applicable + evaluableNodeManager->FreeNodeTreeIfPossible(cur); + + cur = cn; + //if it is a valid node and it is not zero, then return it if(EvaluableNode::IsTrue(cur)) return cur; - - evaluableNodeManager->FreeNodeTreeIfPossible(cur); } - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true); + return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE); } #endif for(auto &cn : ocn) { - auto cur = InterpretNode(cn); + //free the previous node if applicable + evaluableNodeManager->FreeNodeTreeIfPossible(cur); + + cur = InterpretNode(cn); - //if it is a valid node and it is not zero, then return it if(EvaluableNode::IsTrue(cur)) return cur; - - evaluableNodeManager->FreeNodeTreeIfPossible(cur); } - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true); + + return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE); } EvaluableNodeReference Interpreter::InterpretNode_ENT_XOR(EvaluableNode *en) @@ -145,15 +148,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_NOT(EvaluableNode *en) return EvaluableNodeReference::Null(); auto cur = InterpretNodeForImmediateUse(ocn[0]); - bool is_true = EvaluableNode::IsTrue(cur); - if(cur.unique && cur != nullptr) - cur->ClearAndSetType(is_true ? ENT_FALSE : ENT_TRUE); - else - cur = EvaluableNodeReference(evaluableNodeManager->AllocNode(is_true ? ENT_FALSE : ENT_TRUE), true); - - return cur; + return evaluableNodeManager->ReuseOrAllocNode(cur, is_true ? ENT_FALSE : ENT_TRUE); } EvaluableNodeReference Interpreter::InterpretNode_ENT_EQUAL(EvaluableNode *en) @@ -181,12 +178,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_EQUAL(EvaluableNode *en) } if(!EvaluableNode::AreDeepEqual(to_match, cur)) - { - evaluableNodeManager->FreeNodeTreeIfPossible(to_match); - evaluableNodeManager->FreeNodeTreeIfPossible(cur); - - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true); - } + return evaluableNodeManager->ReuseOrAllocOneOfNodes(to_match, cur, ENT_FALSE); evaluableNodeManager->FreeNodeTreeIfPossible(cur); } @@ -213,12 +205,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_EQUAL(EvaluableNode *en) } if(!EvaluableNode::AreDeepEqual(to_match, cur)) - { - evaluableNodeManager->FreeNodeTreeIfPossible(to_match); - evaluableNodeManager->FreeNodeTreeIfPossible(cur); - - return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true); - } + return evaluableNodeManager->ReuseOrAllocOneOfNodes(to_match, cur, ENT_FALSE); evaluableNodeManager->FreeNodeTreeIfPossible(cur); } @@ -273,10 +260,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_NEQUAL(EvaluableNode *en) EvaluableNodeReference b = InterpretNodeForImmediateUse(ocn[1]); bool a_b_not_equal = (!EvaluableNode::AreDeepEqual(a, b)); - evaluableNodeManager->FreeNodeTreeIfPossible(a); - evaluableNodeManager->FreeNodeTreeIfPossible(b); - - return EvaluableNodeReference(evaluableNodeManager->AllocNode(a_b_not_equal ? ENT_TRUE : ENT_FALSE), true); + return evaluableNodeManager->ReuseOrAllocOneOfNodes(a, b, a_b_not_equal ? ENT_TRUE : ENT_FALSE); } auto node_stack = CreateInterpreterNodeStackStateSaver(); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp b/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp index 656ff377..3cd56157 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp @@ -522,16 +522,16 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LOG(EvaluableNode *en) if(ocn.size() == 0) return EvaluableNodeReference::Null(); - double value = InterpretNodeIntoNumberValue(ocn[0]); - double log_value = log(value); - + double divisor = 1.0; if(ocn.size() > 1) //base is specified, need to scale { double log_base = InterpretNodeIntoNumberValue(ocn[1]); - log_value /= log(log_base); + divisor = log(log_base); } - return EvaluableNodeReference(evaluableNodeManager->AllocNode(log_value), true); + EvaluableNode *retval = InterpretNodeIntoUniqueNumberValueEvaluableNode(ocn[0]); + retval->SetNumberValue(std::log(retval->GetNumberValueReference()) / divisor); + return EvaluableNodeReference(retval, true); } EvaluableNodeReference Interpreter::InterpretNode_ENT_SIN(EvaluableNode *en) @@ -954,10 +954,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_DOT_PRODUCT(EvaluableNode } } - evaluableNodeManager->FreeNodeTreeIfPossible(elements1); - evaluableNodeManager->FreeNodeTreeIfPossible(elements2); - - return EvaluableNodeReference(evaluableNodeManager->AllocNode(dot_product), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocOneOfNodes(elements1, elements2, ENT_NUMBER); + result->SetNumberValue(dot_product); + return result; } //builds a vector of the values in the node, using ordered or mapped child nodes as appropriate @@ -1128,10 +1127,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GENERALIZED_DISTANCE(Evalu double value = dist_params.ComputeMinkowskiDistance(location, location_types, origin, origin_types, true); //free these after computation in case they had any code being used/referenced in the distance - evaluableNodeManager->FreeNodeTreeIfPossible(location_node); - evaluableNodeManager->FreeNodeTreeIfPossible(origin_node); - - return EvaluableNodeReference(evaluableNodeManager->AllocNode(value), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocOneOfNodes(location_node, origin_node, ENT_NUMBER); + result->SetNumberValue(value); + return result; } EvaluableNodeReference Interpreter::InterpretNode_ENT_ENTROPY(EvaluableNode *en) @@ -1342,10 +1340,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_ENTROPY(EvaluableNode *en) accumulated_entropy += p_i_first_term * std::log(p_i_exponentiated * q_i); } - //clean up node_stack.PopEvaluableNode(); - evaluableNodeManager->FreeNodeTreeIfPossible(p_node); - evaluableNodeManager->FreeNodeTreeIfPossible(q_node); //negate accumulated_entropy = -accumulated_entropy; @@ -1355,5 +1350,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_ENTROPY(EvaluableNode *en) //we take the max of the result and 0 accumulated_entropy = std::max(0.0, accumulated_entropy); - return EvaluableNodeReference(evaluableNodeManager->AllocNode(accumulated_entropy), true); + EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocOneOfNodes(p_node, q_node, ENT_NUMBER); + result->SetNumberValue(accumulated_entropy); + return result; } diff --git a/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp b/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp index 5feeb58e..50b8dea1 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp @@ -1052,24 +1052,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CONTAINS_INDEX(EvaluableNo EvaluableNode **target = TraverseToDestinationFromTraversalPathList(&container.reference, index, false); EvaluableNodeType result = (target != nullptr ? ENT_TRUE : ENT_FALSE); - evaluableNodeManager->FreeNodeTreeIfPossible(container); - - //see if can reuse index node - EvaluableNodeReference retval; - if(index != nullptr && index.unique) - { - if(!index->GetNeedCycleCheck()) - evaluableNodeManager->FreeNodeChildNodes(index); - - index->ClearAndSetType(result); - retval = EvaluableNodeReference(index.reference, true); - } - else //need a new node - { - retval = EvaluableNodeReference(evaluableNodeManager->AllocNode(result), true); - } - - return retval; + return evaluableNodeManager->ReuseOrAllocOneOfNodes(index, container, result); } EvaluableNodeReference Interpreter::InterpretNode_ENT_CONTAINS_VALUE(EvaluableNode *en) @@ -1137,24 +1120,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CONTAINS_VALUE(EvaluableNo result = ENT_TRUE; } - evaluableNodeManager->FreeNodeTreeIfPossible(collection); - - //see if can reuse value node - EvaluableNodeReference retval; - if(value != nullptr && value.unique) - { - if(!value->GetNeedCycleCheck()) - evaluableNodeManager->FreeNodeChildNodes(value); - - value->ClearAndSetType(result); - retval = EvaluableNodeReference(value.reference, true); - } - else //need a new node - { - retval = EvaluableNodeReference(evaluableNodeManager->AllocNode(result), true); - } - - return retval; + return evaluableNodeManager->ReuseOrAllocOneOfNodes(value, collection, result); } EvaluableNodeReference Interpreter::InterpretNode_ENT_REMOVE(EvaluableNode *en)