diff --git a/Toy b/Toy index 8b04939..168369d 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit 8b049394308f153064d3e6dd735746e517255900 +Subproject commit 168369d8970c6497d3ccd15363dd46f04ea4e7f4 diff --git a/assets/scripts/root.toy b/assets/scripts/root.toy index b9f323d..02eb4d6 100644 --- a/assets/scripts/root.toy +++ b/assets/scripts/root.toy @@ -21,8 +21,7 @@ fn onInit(node: opaque) { makeChild(node, "assets/scripts/child.toy", false); //actually, grab that first node and free it - var o: opaque = getNode(node, 0); - freeNode(o); + freeChildNode(node, 0); //must be done from the parent node, so it's pointer can be nullified } fn onStep(node: opaque) { diff --git a/core/common.c b/core/common.c index ddef6fc..443f2b2 100644 --- a/core/common.c +++ b/core/common.c @@ -1,2 +1,10 @@ #include "common.h" +STATIC_ASSERT(sizeof(char) == 1); +STATIC_ASSERT(sizeof(short) == 2); +STATIC_ASSERT(sizeof(int) == 4); +STATIC_ASSERT(sizeof(float) == 4); +STATIC_ASSERT(sizeof(unsigned char) == 1); +STATIC_ASSERT(sizeof(unsigned short) == 2); +STATIC_ASSERT(sizeof(unsigned int) == 4); + diff --git a/core/common.h b/core/common.h index e1f9df0..7376fb4 100644 --- a/core/common.h +++ b/core/common.h @@ -11,3 +11,7 @@ #define CORE_API #endif +#include + +//test variable sizes based on platform +#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed") diff --git a/core/engine.c b/core/engine.c index 3f6e9bf..3293720 100644 --- a/core/engine.c +++ b/core/engine.c @@ -61,7 +61,6 @@ void freeEngine() { callEngineNode(engine.rootNode, &engine.interpreter, "onFree"); freeEngineNode(engine.rootNode); - FREE(EngineNode, engine.rootNode); engine.rootNode = NULL; } diff --git a/core/engine_node.c b/core/engine_node.c index a6716ad..574bffc 100644 --- a/core/engine_node.c +++ b/core/engine_node.c @@ -2,12 +2,25 @@ #include "memory.h" +STATIC_ASSERT(sizeof(EngineNode) == 32); +STATIC_ASSERT(sizeof(EngineNodeCallback) == 8); +STATIC_ASSERT(sizeof(LiteralDictionary*) == 8); +STATIC_ASSERT(sizeof(EngineNode*) == 8); +STATIC_ASSERT(sizeof(int) == 4); + +static void freeMemory(void* ptr) { + //free this node type's memory + FREE(EngineNode, ptr); +} + void initEngineNode(EngineNode* node, Interpreter* interpreter, void* tb, size_t size) { //init + node->freeMemory = freeMemory; + node->functions = ALLOCATE(LiteralDictionary, 1); node->children = NULL; node->capacity = 0; node->count = 0; - node->functions = ALLOCATE(LiteralDictionary, 1); + initLiteralDictionary(node->functions); //run bytecode @@ -37,7 +50,7 @@ void pushEngineNode(EngineNode* node, EngineNode* child) { int oldCapacity = node->capacity; node->capacity = GROW_CAPACITY(oldCapacity); - node->children = GROW_ARRAY(EngineNode, node->children, oldCapacity, node->capacity); + node->children = GROW_ARRAY(EngineNode*, node->children, oldCapacity, node->capacity); } //prune tombstones (experimental) @@ -49,41 +62,35 @@ void pushEngineNode(EngineNode* node, EngineNode* child) { } //move down - if (node->children[i].functions != NULL) { + if (node->children[i] != NULL) { node->children[counter++] = node->children[i]; } } - //zero the rest - while (counter < node->capacity) { - node->children[counter].children = NULL; - node->children[counter].capacity = 0; - node->children[counter].count = 0; - node->children[counter].functions = NULL; - counter++; - } - //assign - node->children[node->count++] = *child; + node->children[node->count++] = child; } void freeEngineNode(EngineNode* node) { - //free and tombstone this node - for (int i = 0; i < node->capacity; i++) { - freeEngineNode(&node->children[i]); + if (node == NULL) { + return; //NO-OP } - FREE_ARRAY(EngineNode, node->children, node->capacity); + //free and tombstone this node + for (int i = 0; i < node->count; i++) { + freeEngineNode(node->children[i]); + } + + //free the pointer array to the children + FREE_ARRAY(EngineNode*, node->children, node->capacity); if (node->functions != NULL) { freeLiteralDictionary(node->functions); FREE(LiteralDictionary, node->functions); } - node->children = NULL; - node->capacity = -1; - node->count = -1; - node->functions = NULL; + //free this node's memory + node->freeMemory(node); } static void callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key) { @@ -110,8 +117,8 @@ static void callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Li //recurse to the (non-tombstone) children for (int i = 0; i < node->count; i++) { - if (node->children[i].functions != NULL) { - callEngineNodeLiteral(&node->children[i], interpreter, key); + if (node->children[i] != NULL) { + callEngineNodeLiteral(node->children[i], interpreter, key); } } } diff --git a/core/engine_node.h b/core/engine_node.h index b995c4d..8c526c4 100644 --- a/core/engine_node.h +++ b/core/engine_node.h @@ -8,16 +8,20 @@ //forward declare typedef struct _engineNode EngineNode; +typedef void (*EngineNodeCallback)(void*); + //the node object, which forms a tree typedef struct _engineNode { - //use Toy's memory model - EngineNode* children; - int capacity; - int count; //includes tombstones - //TODO: add "liveCount" + //function for releasing memory + EngineNodeCallback freeMemory; //toy functions, stored in a dict for flexibility LiteralDictionary* functions; + + //use Toy's memory model + EngineNode** children; + int capacity; + int count; //includes tombstones } EngineNode; CORE_API void initEngineNode(EngineNode* node, Interpreter* interpreter, void* tb, size_t size); //run bytecode, then grab all top-level function literals diff --git a/core/lib_engine.c b/core/lib_engine.c index d411f3c..bc2d362 100644 --- a/core/lib_engine.c +++ b/core/lib_engine.c @@ -231,6 +231,13 @@ static int nativeInitNode(Interpreter* interpreter, LiteralArray* arguments) { freeLiteral(nodeIdn); + //check argument types + if (!IS_OPAQUE(node)) { + interpreter->errorOutput("Incorrect argument type passed to initNode\n"); + freeLiteral(node); + return -1; + } + EngineNode* engineNode = AS_OPAQUE(node); //init the new node @@ -259,6 +266,13 @@ static int nativeFreeNode(Interpreter* interpreter, LiteralArray* arguments) { freeLiteral(nodeIdn); + //check argument types + if (!IS_OPAQUE(node)) { + interpreter->errorOutput("Incorrect argument type passed to freeNode\n"); + freeLiteral(node); + return -1; + } + EngineNode* engineNode = AS_OPAQUE(node); //free the node @@ -271,6 +285,60 @@ static int nativeFreeNode(Interpreter* interpreter, LiteralArray* arguments) { return 0; } +static int nativeFreeChildNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments passed to freeChildNode\n"); + return -1; + } + + Literal index = popLiteralArray(arguments); + Literal node = popLiteralArray(arguments); + + Literal nodeIdn = node; //annoying + + if (!parseIdentifierToValue(interpreter, &node)) { + interpreter->errorOutput("Failed to parse parent node identifier to value\n"); + freeLiteral(node); + return -1; + } + + freeLiteral(nodeIdn); + + //check argument types + if (!IS_OPAQUE(node) || !IS_INTEGER(index)) { + interpreter->errorOutput("Incorrect argument type passed to freeChildNode\n"); + freeLiteral(node); + return -1; + } + + EngineNode* parentNode = AS_OPAQUE(node); + int idx = AS_INTEGER(index); + + //check bounds + if (idx < 0 || idx >= parentNode->count) { + interpreter->errorOutput("Node index out of bounds in freeChildNode\n"); + freeLiteral(node); + freeLiteral(index); + return -1; + } + + //get the child node + EngineNode* childNode = parentNode->children[idx]; + + //free the node + if (childNode != NULL) { + callEngineNode(childNode, &engine.interpreter, "onFree"); + freeEngineNode(childNode); + } + + parentNode->children[idx] = NULL; + + //cleanup + freeLiteral(node); + freeLiteral(index); + return 0; +} + static int nativePushNode(Interpreter* interpreter, LiteralArray* arguments) { //checks if (arguments->count != 2) { @@ -360,7 +428,7 @@ static int nativeGetNode(Interpreter* interpreter, LiteralArray* arguments) { return -1; } - EngineNode* childNode = &parentNode->children[intIndex]; + EngineNode* childNode = parentNode->children[intIndex]; Literal child = TO_OPAQUE_LITERAL(childNode, -1); pushLiteralArray(&interpreter->stack, child); @@ -386,7 +454,8 @@ int hookEngine(Interpreter* interpreter, Literal identifier, Literal alias) { {"loadRootNode", nativeLoadRootNode}, {"loadNode", nativeLoadNode}, {"initNode", nativeInitNode}, - {"freeNode", nativeFreeNode}, + // {"freeNode", nativeFreeNode}, + {"freeChildNode", nativeFreeChildNode}, {"pushNode", nativePushNode}, {"getNode", nativeGetNode}, {NULL, NULL} diff --git a/test/scripts/child_engine_node.toy b/test/scripts/child_engine_node.toy index 46ef7b3..e84172c 100644 --- a/test/scripts/child_engine_node.toy +++ b/test/scripts/child_engine_node.toy @@ -1,14 +1,14 @@ var tally: int = 0; -fn onInit() { +fn onInit(node) { print "child init called"; } -fn onStep() { +fn onStep(node) { print "child step called"; print ++tally; } -fn onFree() { +fn onFree(node) { print "child free called"; } \ No newline at end of file diff --git a/test/scripts/parent_engine_node.toy b/test/scripts/parent_engine_node.toy index b78ceef..3c8eb59 100644 --- a/test/scripts/parent_engine_node.toy +++ b/test/scripts/parent_engine_node.toy @@ -1,11 +1,11 @@ -fn onInit() { +fn onInit(node) { print "init called"; } -fn onStep() { +fn onStep(node) { print "step called"; } -fn onFree() { +fn onFree(node) { print "free called"; } \ No newline at end of file diff --git a/test/test_engine_node.c b/test/test_engine_node.c index 7ad9020..2ba0763 100644 --- a/test/test_engine_node.c +++ b/test/test_engine_node.c @@ -6,6 +6,7 @@ #include "interpreter.h" #include "repl_tools.h" #include "console_colors.h" +#include "memory.h" #include #include @@ -29,15 +30,15 @@ int main() { unsigned char* tb = compileString(source, &size); //create and test the engine node - EngineNode node; + EngineNode* node = ALLOCATE(EngineNode, 1); - initEngineNode(&node, &interpreter, tb, size); + initEngineNode(node, &interpreter, tb, size); - callEngineNode(&node, &interpreter, "onInit"); - callEngineNode(&node, &interpreter, "onStep"); - callEngineNode(&node, &interpreter, "onFree"); + callEngineNode(node, &interpreter, "onInit"); + callEngineNode(node, &interpreter, "onStep"); + callEngineNode(node, &interpreter, "onFree"); - freeEngineNode(&node); + freeEngineNode(node); //free free((void*)source); @@ -56,35 +57,35 @@ int main() { unsigned char* tb = compileString(source, &size); //create and test the engine node - EngineNode node; + EngineNode* node = ALLOCATE(EngineNode, 1); - initEngineNode(&node, &interpreter, tb, size); + initEngineNode(node, &interpreter, tb, size); resetInterpreter(&interpreter); for (int i = 0; i < 10; i++) { char* source = readFile("./scripts/child_engine_node.toy", &size); unsigned char* tb = compileString(source, &size); - EngineNode child; - initEngineNode(&child, &interpreter, tb, size); + EngineNode* child = ALLOCATE(EngineNode, 1); + initEngineNode(child, &interpreter, tb, size); resetInterpreter(&interpreter); - pushEngineNode(&node, &child); + pushEngineNode(node, child); free((void*)source); } //test the calls - callEngineNode(&node, &interpreter, "onInit"); + callEngineNode(node, &interpreter, "onInit"); for (int i = 0; i < 10; i++) { - callEngineNode(&node, &interpreter, "onStep"); + callEngineNode(node, &interpreter, "onStep"); } - callEngineNode(&node, &interpreter, "onFree"); + callEngineNode(node, &interpreter, "onFree"); //free - freeEngineNode(&node); + freeEngineNode(node); free((void*)source); freeInterpreter(&interpreter); }