diff --git a/.gdbinit b/.gdbinit index 56124a5..fe3e786 100644 --- a/.gdbinit +++ b/.gdbinit @@ -15,4 +15,9 @@ skip fzl_env_lookup skip fzl_env_contains skip fzl_env_storevalue skip fzl_env_writevalue +skip fzl_mkctable +skip fzl_freectable +skip tok_to_type +skip nest_ctx +skip fin_ctx diff --git a/CMakeLists.txt b/CMakeLists.txt index 925fd71..1cab213 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,14 @@ add_test( NAME compile_class COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/class.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/class.fzs" ) +add_test( + NAME compile_raii + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/raii.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/raii.fzs" +) +add_test( + NAME compile_borrow + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/borrow.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/borrow.fzs" +) set_tests_properties(compile_problematic_hw PROPERTIES WILL_FAIL TRUE) @@ -109,4 +117,12 @@ add_test( NAME runtime_class COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/class.fzb" ) +add_test( + NAME runtime_raii + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/raii.fzb" +) +add_test( + NAME runtime_borrow + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/borrow.fzb" +) diff --git a/exl/borrow.fzs b/exl/borrow.fzs new file mode 100644 index 0000000..15658b7 --- /dev/null +++ b/exl/borrow.fzs @@ -0,0 +1,26 @@ +class A +{ + uint some; +} + +fn doSome(A a) A +{ + // NOTE: section below won't change `a.some` value + + // if (a.some < 1) + // { + // a.some = 1; + // return a; + // } + a.some = 1; + + return a; +} + +A a = new A; +a.some = 0; +printline(a); + +a = doSome(a); +printline(a); + diff --git a/exl/raii.fzs b/exl/raii.fzs new file mode 100644 index 0000000..998d06e --- /dev/null +++ b/exl/raii.fzs @@ -0,0 +1,34 @@ +class Animal +{ + str type; + str name; + int age; + + fn copy() Animal + { + Animal cp = new Animal; + cp.type = this.type; + cp.name = this.name; + cp.age = this.age; + + return cp; + } +} + +Animal a = new Animal; +a.type = "Cow"; +a.name = "Buryatka"; +a.age = 5; + +Animal b = a.copy(); +a.name = "Gavryusha"; +a.age = 10; + +printline(a); +printline(b); + +if (a.type == b.type && a.name != b.name && a.age < b.age) + printline("Test passed"); +else + printline("Test failed"); + diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index a0d8ac3..35e26a0 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -21,9 +21,10 @@ typedef enum FZL_OPCODE_CALL = 0xF30001, // Call function FZL_OPCODE_RET = 0xF30002, // Return from function - FZL_OPCODE_ALLOC_OBJECT = 0xFA0001, // Allocate N slots - FZL_OPCODE_GET_FIELD = 0xFA0002, // Get field N of an object at dstack - FZL_OPCODE_SET_FIELD = 0xFA0003, // Set field N of an object at dstack + FZL_OPCODE_ALLOC_OBJECT = 0xFA0001, // Allocate N slots + FZL_OPCODE_GET_FIELD = 0xFA0002, // Get field N of an object at dstack + FZL_OPCODE_SET_FIELD = 0xFA0003, // Set field N of an object at dstack + FZL_OPCODE_ESCAPE_OBJECT = 0xFA0004, // Make parent envtable as owner FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in dstack FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in dstack @@ -40,6 +41,12 @@ typedef enum FZL_OPCODE_CMP_GT = 0xB00005, // Greater than FZL_OPCODE_CMP_GTEQ = 0xB00006, // Greater than or equal + FZL_OPCODE_CMP_EQ_STR = 0xB10001, // Strings equality + FZL_OPCODE_CMP_NEQ_STR = 0xB10002, // Strings non-equality + + FZL_OPCODE_CMP_EQ_OBJ = 0xB20001, // Objects equality (by pointer) + FZL_OPCODE_CMP_NEQ_OBJ = 0xB20002, // Objects non-equality (by pointer) + FZL_OPCODE_AND = 0xC00001, // A && B FZL_OPCODE_OR = 0xC00002, // A || B diff --git a/include/vm/fzl_vm_env.h b/include/vm/fzl_vm_env.h index 3bd1ecc..6c7b139 100644 --- a/include/vm/fzl_vm_env.h +++ b/include/vm/fzl_vm_env.h @@ -4,6 +4,7 @@ #include #include #include +#include typedef enum { @@ -27,6 +28,10 @@ typedef struct size_t remainder; fzl_vm_env_entry_t *table; + + fzl_vm_object_t **owned_objects; + size_t owned_objects_remainder; + size_t owned_objects_count; } fzl_vm_env_t; // Allocate env table diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 05206b8..a09a9b9 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -65,6 +65,9 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) // Convert type token to `fzl_type_t` const fzl_type_t tok_to_type(fzl_token_t *tok) { + if (tok->type == FZL_TOKTYPE_NAME) + return FZL_TYPE_OBJECT; + if (strcmp((char *)tok->value, "i8") == 0) return FZL_TYPE_INT8; if (strcmp((char *)tok->value, "i16") == 0) @@ -98,8 +101,14 @@ const fzl_type_t tok_to_type(fzl_token_t *tok) return FZL_TYPE_NULL; } -static bool is_compatible(fzl_type_t t1, fzl_type_t t2) +static bool is_compatible(fzl_type_t t1, fzl_type_t t2, char *c1, char *c2) { + if (t1 == FZL_TYPE_OBJECT || t2 == FZL_TYPE_OBJECT) + { + return t1 == FZL_TYPE_OBJECT && t2 == FZL_TYPE_OBJECT && + (strcmp(c1, c2) == 0); + } + if (t1 == t2) return true; @@ -369,6 +378,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) fzl_symtable_res_t putres = fzl_symt_put(walker->syms[walker->sym_top - 1], node->fn.name->value, fntype, walker->op_count); + if (putres != FZL_SYMTABLE_RESULT_OK) { if (putres == FZL_SYMTABLE_RESULT_EXISTS) @@ -382,7 +392,23 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) "'%s': failed to store name '%s'", node->fn.name->filename, (char *)node->fn.name->value); - return (fzl_walk_res_t){.error = true}; + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (fntype == FZL_TYPE_OBJECT) + { + fzl_symtable_res_t updres = + fzl_symt_setobjclass(walker->syms[walker->sym_top - 1], + node->fn.name->value, node->fn.type->value); + if (updres != FZL_SYMTABLE_RESULT_OK) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': failed to assign object type to function return " + "type at %ld:%ld", + node->fn.type->filename, node->fn.type->ln, + node->fn.type->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } } // NOTE: add sym_top limit check @@ -430,7 +456,12 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) for (ssize_t i = walker->sym_top - 1; i >= lvl; i--) fzl_freesymt(walker->syms[--walker->sym_top]); - current_res = (fzl_walk_res_t){.type = fntype, .error = false}; + char *retclass = NULL; + if (fntype == FZL_TYPE_OBJECT && node->fn.type) + retclass = node->fn.type->value; + + current_res = (fzl_walk_res_t){ + .type = fntype, .error = false, .classname = retclass}; break; } @@ -445,14 +476,15 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } + bool returning_object = false; if (node->ret.value) { fzl_walk_res_t retres = walk(walker, node->ret.value); if (retres.error) return retres; - if (!is_compatible(retres.type, - tok_to_type(walker->ctx->fn->fn.type))) + if (!is_compatible(retres.type, tok_to_type(walker->ctx->fn->fn.type), + retres.classname, walker->ctx->fn->fn.type->value)) { snprintf( wlast_err, sizeof(wlast_err), @@ -462,14 +494,23 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } - current_res = (fzl_walk_res_t){.type = retres.type, .error = false}; + if (retres.type == FZL_TYPE_OBJECT) + returning_object = true; + + current_res = (fzl_walk_res_t){.type = retres.type, + .error = false, + .classname = retres.classname}; } else current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; for (size_t i = 0; i < (walker->sym_top - walker->ctx->initial_env_lvl); i++) + { + if (returning_object) + push_op(walker, FZL_OPCODE_ESCAPE_OBJECT); push_op(walker, FZL_OPCODE_RM_ENVTABLE); + } push_op(walker, FZL_OPCODE_RET); break; } @@ -500,7 +541,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (right.error) return right; - if (!is_compatible(left.type, right.type)) + if (!is_compatible(left.type, right.type, left.classname, + right.classname)) { snprintf(wlast_err, sizeof(wlast_err), "'%s': incompatible types at %ld:%ld", node->token->filename, @@ -508,31 +550,72 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } - switch (node->type) + if (left.type == FZL_TYPE_STRING && right.type == FZL_TYPE_STRING) { - case FZL_NODETYPE_EQUAL: - push_op(walker, FZL_OPCODE_CMP_EQ); - break; - case FZL_NODETYPE_NEQUAL: - push_op(walker, FZL_OPCODE_CMP_NEQ); - break; - case FZL_NODETYPE_LESS: - push_op(walker, FZL_OPCODE_CMP_LT); - break; - case FZL_NODETYPE_LESS_EQUAL: - push_op(walker, FZL_OPCODE_CMP_LTEQ); - break; - case FZL_NODETYPE_GREATER: - push_op(walker, FZL_OPCODE_CMP_GT); - break; - case FZL_NODETYPE_GREATER_EQUAL: - push_op(walker, FZL_OPCODE_CMP_GTEQ); - break; - default: - break; + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + push_op(walker, FZL_OPCODE_CMP_EQ_STR); + break; + + case FZL_NODETYPE_NEQUAL: + push_op(walker, FZL_OPCODE_CMP_NEQ_STR); + break; + + default: + snprintf(wlast_err, sizeof(wlast_err), + "'%s': unsupported strings operation at %ld:%ld", + node->token->filename, node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + } + else if (left.type == FZL_TYPE_OBJECT && right.type == FZL_TYPE_OBJECT) + { + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + push_op(walker, FZL_OPCODE_CMP_EQ_OBJ); + break; + + case FZL_NODETYPE_NEQUAL: + push_op(walker, FZL_OPCODE_CMP_NEQ_OBJ); + break; + + default: + snprintf(wlast_err, sizeof(wlast_err), + "'%s': unsupported object operation at %ld:%ld", + node->token->filename, node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + } + else + { + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + push_op(walker, FZL_OPCODE_CMP_EQ); + break; + case FZL_NODETYPE_NEQUAL: + push_op(walker, FZL_OPCODE_CMP_NEQ); + break; + case FZL_NODETYPE_LESS: + push_op(walker, FZL_OPCODE_CMP_LT); + break; + case FZL_NODETYPE_LESS_EQUAL: + push_op(walker, FZL_OPCODE_CMP_LTEQ); + break; + case FZL_NODETYPE_GREATER: + push_op(walker, FZL_OPCODE_CMP_GT); + break; + case FZL_NODETYPE_GREATER_EQUAL: + push_op(walker, FZL_OPCODE_CMP_GTEQ); + break; + default: + break; + } } - current_res = (fzl_walk_res_t){.type = left.type, .error = false}; + current_res = (fzl_walk_res_t){.type = FZL_TYPE_BOOL, .error = false}; break; } @@ -547,7 +630,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (right.error) return right; - if (!is_compatible(left.type, right.type)) + if (!is_compatible(left.type, right.type, left.classname, + right.classname)) { snprintf(wlast_err, sizeof(wlast_err), "'%s': incompatible types at %ld:%ld", node->token->filename, @@ -560,7 +644,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) else push_op(walker, FZL_OPCODE_OR); - current_res = (fzl_walk_res_t){.type = left.type, .error = false}; + current_res = (fzl_walk_res_t){.type = FZL_TYPE_BOOL, .error = false}; break; } @@ -717,7 +801,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_CALL); push_word(walker, res.ip); - current_res = (fzl_walk_res_t){.type = res.type, .error = false}; + current_res = (fzl_walk_res_t){ + .type = res.type, .error = false, .classname = res.classname}; } else if (node->call.callee->type == FZL_NODETYPE_FIELD) { @@ -810,7 +895,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_CALL); push_word(walker, res.ip); - current_res = (fzl_walk_res_t){.type = res.type, .error = false}; + current_res = (fzl_walk_res_t){ + .type = res.type, .error = false, .classname = res.classname}; } } break; @@ -972,7 +1058,9 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, const_n); - current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, + .error = false, + .classname = node->var.type->value}; break; } @@ -1016,7 +1104,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } - if (!is_compatible(sym.type, rres.type)) + if (!is_compatible(sym.type, rres.type, sym.classname, + rres.classname)) { fzl_token_t *tok = node->binary_op.right->token; snprintf(wlast_err, sizeof(wlast_err), @@ -1091,7 +1180,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } - current_res = (fzl_walk_res_t){.type = rres.type, .error = false}; + current_res = (fzl_walk_res_t){ + .type = rres.type, .error = false, .classname = rres.classname}; break; } } diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 7c22d8f..5f7539a 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -110,6 +110,43 @@ fzl_vm_t *fzl_mkvm(const char *filepath) return vm; } +void print_object(fzl_vm_object_t *obj) +{ + if (!obj) + { + printf("\n"); + return; + } + + printf("obj<"); + for (size_t i = 0; i < obj->values_count; i++) + { + fzl_value_t arg = obj->values[i]; + + if (i > 0) + printf(", "); + + if (arg.type == FZL_TYPE_STRING) + printf("str(%s)", arg.as.s.value); + else if (arg.type == FZL_TYPE_CHAR) + printf("char(%c)", arg.as.c); + else if (arg.type == FZL_TYPE_INT) + printf("int(%ld)", arg.as.i); + else if (arg.type == FZL_TYPE_UINT) + printf("uint(%lu)", arg.as.u); + else if (arg.type == FZL_TYPE_BOOL) + printf("bool(%b)", arg.as.b); + else if (arg.type == FZL_TYPE_NULL) + printf(""); + else if (arg.type == FZL_TYPE_OBJECT) + print_object(arg.as.obj); + else + printf(""); + } + + printf(">\n"); +} + int fzl_vm_run(fzl_vm_t *vm) { if (!vm) @@ -125,6 +162,125 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { + case FZL_OPCODE_CMP_EQ_STR: + { + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + int result = strcmp(left.as.s.value, right.as.s.value); + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result == 0}; + break; + } + + case FZL_OPCODE_CMP_NEQ_STR: + { + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + int result = strcmp(left.as.s.value, right.as.s.value); + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result != 0}; + break; + } + + case FZL_OPCODE_CMP_EQ_OBJ: + { + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_BOOL, .as.b = &left.as.obj == &right.as.obj}; + break; + } + + case FZL_OPCODE_CMP_NEQ_OBJ: + { + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_BOOL, .as.b = &left.as.obj != &right.as.obj}; + break; + } + + case FZL_OPCODE_ESCAPE_OBJECT: + { + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - missing value in data stack"); + return -1; + } + fzl_value_t obj = vm->dstack[--vm->dtop]; + if (obj.type != FZL_TYPE_OBJECT) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot escape non-object type"); + return -1; + } + + fzl_vm_env_t *parent = vm->estack[vm->etop - 2]; + fzl_vm_env_t *current = vm->estack[vm->etop - 1]; + if (!parent || vm->etop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot escape object from global env"); + return -1; + } + + bool found = false; + for (size_t i = 0; i < current->owned_objects_count; i++) + { + if (obj.as.obj == current->owned_objects[i]) + { + found = true; + current->owned_objects[i] = NULL; + break; + } + } + + if (found) + { + if (parent->owned_objects_remainder <= parent->owned_objects_count) + { + parent->owned_objects_remainder *= 2; + parent->owned_objects = + realloc(parent->owned_objects, parent->owned_objects_remainder * + sizeof(fzl_vm_object_t *)); + } + + parent->owned_objects[parent->owned_objects_count++] = obj.as.obj; + } + vm->dstack[vm->dtop++] = obj; + + break; + } + case FZL_OPCODE_ALLOC_OBJECT: { uint64_t values_count = vm->ops[++vm->ip]; @@ -132,7 +288,17 @@ int fzl_vm_run(fzl_vm_t *vm) obj->values = malloc(values_count * sizeof(fzl_value_t)); obj->values_count = values_count; - vm->objects[vm->objects_count++] = obj; + fzl_vm_env_t *current_env = vm->estack[vm->etop - 1]; + if (current_env->owned_objects_remainder <= + current_env->owned_objects_count) + { + current_env->owned_objects_remainder *= 2; + current_env->owned_objects = realloc( + current_env->owned_objects, + current_env->owned_objects_remainder * sizeof(fzl_vm_object_t *)); + } + + current_env->owned_objects[current_env->owned_objects_count++] = obj; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = obj}; break; @@ -260,8 +426,8 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t rlt = vm->dstack[--vm->dtop]; fzl_value_t llt = vm->dstack[--vm->dtop]; + fzl_value_t rlt = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llt.as.u < rlt.as.u}; break; @@ -474,6 +640,8 @@ int fzl_vm_run(fzl_vm_t *vm) printf("%b\n", arg.as.b); else if (arg.type == FZL_TYPE_NULL) printf("\n"); + else if (arg.type == FZL_TYPE_OBJECT) + print_object(arg.as.obj); } else { diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c index 9f3f3ef..26404ad 100644 --- a/src/vm/fzl_vm_debug.c +++ b/src/vm/fzl_vm_debug.c @@ -20,6 +20,26 @@ void fzl_vm_print_ops(fzl_vm_t *vm) switch (vm->ops[i]) { + case FZL_OPCODE_CMP_EQ_STR: + printf("CMP_EQ_STR\n"); + break; + + case FZL_OPCODE_CMP_NEQ_STR: + printf("CMP_NEQ_STR\n"); + break; + + case FZL_OPCODE_CMP_EQ_OBJ: + printf("CMP_EQ_OBJ\n"); + break; + + case FZL_OPCODE_CMP_NEQ_OBJ: + printf("CMP_NEQ_OBJ\n"); + break; + + case FZL_OPCODE_ESCAPE_OBJECT: + printf("ESCAPE_OBJ\n"); + break; + case FZL_OPCODE_ALLOC_OBJECT: printf("ALLOC_OBJECT: %lu fields\n", vm->ops[++i]); break; diff --git a/src/vm/fzl_vm_env.c b/src/vm/fzl_vm_env.c index 2ca4ede..1abe7d1 100644 --- a/src/vm/fzl_vm_env.c +++ b/src/vm/fzl_vm_env.c @@ -1,3 +1,4 @@ +#include "vm/fzl_vm_object.h" #include #include #include @@ -15,6 +16,11 @@ fzl_vm_env_t *fzl_mkenv() env->size = 0; env->table = malloc(env->remainder * sizeof(fzl_vm_env_entry_t)); + env->owned_objects_remainder = 2; + env->owned_objects_count = 0; + env->owned_objects = + malloc(env->owned_objects_remainder * sizeof(fzl_vm_object_t *)); + return env; } @@ -96,5 +102,19 @@ void fzl_freeenv(fzl_vm_env_t *env) if (env->table) free(env->table); + if (env->owned_objects) + { + for (size_t i = 0; i < env->owned_objects_count; i++) + { + if (!env->owned_objects[i]) + continue; + + free(env->owned_objects[i]->values); + free(env->owned_objects[i]); + } + + free(env->owned_objects); + } + free(env); }