diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm new file mode 100644 index 00000000..3b53d01d --- /dev/null +++ b/internal/bootstrap.cm @@ -0,0 +1,40 @@ +// Hidden vars (os, program) come from env +var load_internal = os.load_internal +function use_embed(name) { + return load_internal("js_" + name + "_use") +} + +var fd = use_embed('fd') + +var use_cache = {} +use_cache['fd'] = fd +use_cache['os'] = os + +function use(path) { + if (use_cache[path]) + return use_cache[path]; + + var file_path = path + '.cm' + var script = null + var result = null + var exports = null + + if (fd.is_file(file_path)) { + script = text(fd.slurp(file_path)) + exports = {} + mach_eval(path, script, {use: use, exports: exports}) + use_cache[path] = exports + return exports + } + + // Try embedded C module + result = use_embed(replace(path, '/', '_')) + use_cache[path] = result + return result +} + +// Load and run the user's program +var blob = fd.slurp(program) +stone(blob) +var script = text(blob) +mach_eval(program, script, {use: use}) diff --git a/internal/os.c b/internal/os.c index 7c6e8538..267c01c5 100644 --- a/internal/os.c +++ b/internal/os.c @@ -578,7 +578,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { JSValue js_os_use(JSContext *js) { JS_NewClassID(&js_dylib_class_id); JS_NewClass(js, js_dylib_class_id, &js_dylib_class); - + JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs)); return mod; diff --git a/source/cell.c b/source/cell.c index 1640d9fe..56367f2b 100644 --- a/source/cell.c +++ b/source/cell.c @@ -584,59 +584,54 @@ int cell_init(int argc, char **argv) /* Check for --run-mcode flag to execute via MCODE interpreter */ if (argc >= 3 && strcmp(argv[1], "--run-mcode") == 0) { - const char *script_or_file = argv[2]; - char *script = NULL; - char *allocated_script = NULL; - const char *filename = ""; + const char *filename = argv[2]; + if (!find_cell_shop()) return 1; - struct stat st; - if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) { - FILE *f = fopen(script_or_file, "r"); - if (!f) { printf("Failed to open file: %s\n", script_or_file); return 1; } - allocated_script = malloc(st.st_size + 1); - if (!allocated_script) { fclose(f); printf("Failed to allocate memory\n"); return 1; } - size_t read_size = fread(allocated_script, 1, st.st_size, f); - fclose(f); - allocated_script[read_size] = '\0'; - script = allocated_script; - filename = script_or_file; - } else { - script = (char *)script_or_file; - } - - cJSON *ast = JS_ASTTree(script, strlen(script), filename); - if (!ast) { - printf("Failed to parse AST\n"); - free(allocated_script); + size_t boot_size; + char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size); + if (!boot_data) { + printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path); return 1; } - if (print_tree_errors(ast)) { - cJSON_Delete(ast); free(allocated_script); + cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm"); + free(boot_data); + if (!boot_ast) { + printf("Failed to parse internal/bootstrap.cm\n"); return 1; } - cJSON *mcode = JS_McodeTree(ast); - cJSON_Delete(ast); + if (print_tree_errors(boot_ast)) { + cJSON_Delete(boot_ast); + return 1; + } + + cJSON *mcode = JS_McodeTree(boot_ast); + cJSON_Delete(boot_ast); if (!mcode) { printf("Failed to generate MCODE\n"); - free(allocated_script); return 1; } if (print_tree_errors(mcode)) { - cJSON_Delete(mcode); free(allocated_script); + cJSON_Delete(mcode); return 1; } - /* Use a larger heap context for execution */ JSRuntime *rt = JS_NewRuntime(); - if (!rt) { printf("Failed to create JS runtime\n"); cJSON_Delete(mcode); free(allocated_script); return 1; } - JSContext *ctx = JS_NewContextWithHeapSize(rt, 64 * 1024); - if (!ctx) { printf("Failed to create execution context\n"); cJSON_Delete(mcode); JS_FreeRuntime(rt); free(allocated_script); return 1; } + if (!rt) { printf("Failed to create JS runtime\n"); cJSON_Delete(mcode); return 1; } + JSContext *ctx = JS_NewContextWithHeapSize(rt, 256 * 1024); + if (!ctx) { printf("Failed to create execution context\n"); cJSON_Delete(mcode); JS_FreeRuntime(rt); return 1; } - JSValue result = JS_CallMcodeTree(ctx, mcode); /* takes ownership of mcode */ + JS_FreeValue(ctx, js_blob_use(ctx)); + + JSValue hidden_env = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx)); + JS_SetPropertyStr(ctx, hidden_env, "program", JS_NewString(ctx, filename)); + hidden_env = JS_Stone(ctx, hidden_env); + + JSValue result = JS_CallMcodeTreeEnv(ctx, mcode, hidden_env); if (JS_IsException(result)) { JSValue exc = JS_GetException(ctx); @@ -663,7 +658,6 @@ int cell_init(int argc, char **argv) JS_FreeContext(ctx); JS_FreeRuntime(rt); - free(allocated_script); return JS_IsException(result) ? 1 : 0; } @@ -733,61 +727,50 @@ int cell_init(int argc, char **argv) /* Check for --mach-run flag to compile and run through MACH VM */ if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) { - const char *script_or_file = argv[2]; - char *script = NULL; - char *allocated_script = NULL; - const char *filename = ""; + const char *filename = argv[2]; + if (!find_cell_shop()) return 1; - struct stat st; - if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) { - FILE *f = fopen(script_or_file, "r"); - if (!f) { - printf("Failed to open file: %s\n", script_or_file); - return 1; - } - allocated_script = malloc(st.st_size + 1); - if (!allocated_script) { - fclose(f); - printf("Failed to allocate memory for script\n"); - return 1; - } - size_t read_size = fread(allocated_script, 1, st.st_size, f); - fclose(f); - allocated_script[read_size] = '\0'; - script = allocated_script; - filename = script_or_file; - } else { - script = (char *)script_or_file; - } - - cJSON *ast = JS_ASTTree(script, strlen(script), filename); - if (!ast) { - printf("Failed to parse AST\n"); - free(allocated_script); + size_t boot_size; + char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size); + if (!boot_data) { + printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path); return 1; } - if (print_tree_errors(ast)) { - cJSON_Delete(ast); - free(allocated_script); + cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm"); + free(boot_data); + if (!boot_ast) { + printf("Failed to parse internal/bootstrap.cm\n"); + return 1; + } + + if (print_tree_errors(boot_ast)) { + cJSON_Delete(boot_ast); return 1; } JSRuntime *rt = JS_NewRuntime(); if (!rt) { printf("Failed to create JS runtime\n"); - cJSON_Delete(ast); free(allocated_script); + cJSON_Delete(boot_ast); return 1; } - JSContext *ctx = JS_NewContext(rt); + JSContext *ctx = JS_NewContextWithHeapSize(rt, 256 * 1024); if (!ctx) { printf("Failed to create JS context\n"); - cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script); + cJSON_Delete(boot_ast); JS_FreeRuntime(rt); return 1; } - JSValue result = JS_RunMachTree(ctx, ast, JS_NULL); - cJSON_Delete(ast); + JS_FreeValue(ctx, js_blob_use(ctx)); + + JSValue hidden_env = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx)); + JS_SetPropertyStr(ctx, hidden_env, "program", JS_NewString(ctx, filename)); + hidden_env = JS_Stone(ctx, hidden_env); + + JSValue result = JS_RunMachTree(ctx, boot_ast, hidden_env); + cJSON_Delete(boot_ast); int exit_code = 0; if (JS_IsException(result)) { @@ -822,7 +805,6 @@ int cell_init(int argc, char **argv) JS_FreeContext(ctx); JS_FreeRuntime(rt); - free(allocated_script); return exit_code; } diff --git a/source/mach.c b/source/mach.c index 8845d4b2..52e2cf26 100644 --- a/source/mach.c +++ b/source/mach.c @@ -93,9 +93,6 @@ typedef struct MachCompState { int loop_break; /* instruction index to patch, or -1 */ int loop_continue; /* instruction index to patch, or -1 */ - /* Scope depth for block scoping */ - int scope_depth; - /* Parent for nested function compilation */ struct MachCompState *parent; @@ -241,7 +238,6 @@ static void mach_add_var(MachCompState *cs, const char *name, int slot, int is_c v->slot = slot; v->is_const = is_const; v->is_closure = 0; - v->scope_depth = cs->scope_depth; } /* Find a variable in the current scope */ @@ -1353,17 +1349,7 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { cJSON *right = cJSON_GetObjectItemCaseSensitive(stmt, "right"); const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "name")); if (!name) return; - /* Check if var exists at current scope depth — if so, reuse it. - If it exists at a shallower depth, shadow it with a new slot. */ - int slot = -1; - for (int i = cs->var_count - 1; i >= 0; i--) { - if (strcmp(cs->vars[i].name, name) == 0) { - if (cs->vars[i].scope_depth == cs->scope_depth) { - slot = cs->vars[i].slot; /* same scope — reuse */ - } - break; - } - } + int slot = mach_find_var(cs, name); if (slot < 0) { slot = mach_reserve_reg(cs); mach_add_var(cs, name, slot, strcmp(kind, "def") == 0); @@ -1438,8 +1424,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { /* Block */ if (strcmp(kind, "block") == 0) { - int saved_var_count = cs->var_count; - cs->scope_depth++; cJSON *stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements"); if (stmts && cJSON_IsArray(stmts)) { int count = cJSON_GetArraySize(stmts); @@ -1447,10 +1431,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); } } - cs->scope_depth--; - for (int i = saved_var_count; i < cs->var_count; i++) - sys_free(cs->vars[i].name); - cs->var_count = saved_var_count; return; } @@ -1470,8 +1450,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { /* Compile then branch — "then" is a direct array of statements */ if (then_body) { - int saved_vc = cs->var_count; - cs->scope_depth++; if (cJSON_IsArray(then_body)) { int count = cJSON_GetArraySize(then_body); for (int i = 0; i < count; i++) @@ -1486,10 +1464,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { mach_compile_stmt(cs, then_body); } } - cs->scope_depth--; - for (int i = saved_vc; i < cs->var_count; i++) - sys_free(cs->vars[i].name); - cs->var_count = saved_vc; } /* Check for else-if chain ("list") or plain else */ @@ -1508,8 +1482,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); /* Compile else — could be a direct array, object, or else-if stmt */ - int saved_vc = cs->var_count; - cs->scope_depth++; if (cJSON_IsArray(else_body)) { int count = cJSON_GetArraySize(else_body); for (int i = 0; i < count; i++) @@ -1524,10 +1496,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { mach_compile_stmt(cs, else_body); } } - cs->scope_depth--; - for (int i = saved_vc; i < cs->var_count; i++) - sys_free(cs->vars[i].name); - cs->var_count = saved_vc; /* Patch jmpend */ offset = mach_current_pc(cs) - (jmpend_pc + 1); @@ -1560,8 +1528,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { /* Compile body — "statements" on a child "block"/"body", or directly on the node */ { - int saved_vc = cs->var_count; - cs->scope_depth++; cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block"); if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body"); cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL; @@ -1573,10 +1539,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { } else if (body) { mach_compile_stmt(cs, body); } - cs->scope_depth--; - for (int i = saved_vc; i < cs->var_count; i++) - sys_free(cs->vars[i].name); - cs->var_count = saved_vc; } /* Patch continue chain to loop_top */ @@ -1613,8 +1575,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { /* For loop */ if (strcmp(kind, "for") == 0) { - int saved_vc = cs->var_count; - cs->scope_depth++; cJSON *init = cJSON_GetObjectItemCaseSensitive(stmt, "init"); cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "test"); cJSON *update = cJSON_GetObjectItemCaseSensitive(stmt, "update"); @@ -1643,8 +1603,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { /* Body — "statements" on a child "block"/"body", or directly on the for node */ { - int body_vc = cs->var_count; - cs->scope_depth++; cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL; if (!stmts) stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements"); if (stmts && cJSON_IsArray(stmts)) { @@ -1654,10 +1612,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { } else if (body) { mach_compile_stmt(cs, body); } - cs->scope_depth--; - for (int i = body_vc; i < cs->var_count; i++) - sys_free(cs->vars[i].name); - cs->var_count = body_vc; } /* Patch continue chain to update (or loop_top if no update) */ @@ -1699,10 +1653,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { } cs->loop_break = old_break; cs->loop_continue = old_continue; - cs->scope_depth--; - for (int i = saved_vc; i < cs->var_count; i++) - sys_free(cs->vars[i].name); - cs->var_count = saved_vc; return; } diff --git a/source/mcode.c b/source/mcode.c index ab82a984..2e55b473 100644 --- a/source/mcode.c +++ b/source/mcode.c @@ -500,13 +500,11 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o if (strcmp (left_kind, "name") == 0) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name")); - const char *sn = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "scope_name")); - const char *ln = sn ? sn : name; cJSON *level_node = cJSON_GetObjectItemCaseSensitive (left, "level"); int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1; int left_slot = mach_gen_alloc_slot (s); if (level == 0 || level == -1) { - int local = mach_gen_find_var (s, ln); + int local = mach_gen_find_var (s, name); if (local >= 0) { mach_gen_emit_2 (s, "move", left_slot, local); level = 0; /* treat as local for the store below */ @@ -515,7 +513,7 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o if (level > 0) { MachGenState *target = s; for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_gen_find_var (target, ln); + int slot = mach_gen_find_var (target, name); mach_gen_emit_3 (s, "get", left_slot, slot, level); } else if (level == -1) { cJSON *instr = cJSON_CreateArray (); @@ -532,12 +530,12 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o int dest = mach_gen_alloc_slot (s); mach_gen_emit_3 (s, op, dest, left_slot, right_slot); if (level == 0) { - int local = mach_gen_find_var (s, ln); + int local = mach_gen_find_var (s, name); if (local >= 0) mach_gen_emit_2 (s, "move", local, dest); } else if (level > 0) { MachGenState *target = s; for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_gen_find_var (target, ln); + int slot = mach_gen_find_var (target, name); mach_gen_emit_3 (s, "put", dest, slot, level); } else { cJSON *instr = cJSON_CreateArray (); @@ -606,12 +604,10 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) { if (strcmp (left_kind, "name") == 0) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name")); - const char *sn = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "scope_name")); - const char *ln = sn ? sn : name; cJSON *level_node = cJSON_GetObjectItemCaseSensitive (left, "level"); int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1; if (level == 0 || level == -1) { - int slot = mach_gen_find_var (s, ln); + int slot = mach_gen_find_var (s, name); if (slot >= 0) mach_gen_emit_2 (s, "move", slot, val_slot); else if (level == -1) { /* No annotation and not local — set global */ @@ -624,7 +620,7 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) { } else if (level > 0) { MachGenState *target = s; for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_gen_find_var (target, ln); + int slot = mach_gen_find_var (target, name); mach_gen_emit_3 (s, "put", val_slot, slot, level); } else { mach_gen_error (s, node, "cannot assign to unbound variable '%s'", name); @@ -755,18 +751,16 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { /* Variable reference — uses parser-provided level annotation */ if (strcmp (kind, "name") == 0) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (expr, "name")); - const char *scope_name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (expr, "scope_name")); - const char *lookup_name = scope_name ? scope_name : name; cJSON *level_node = cJSON_GetObjectItemCaseSensitive (expr, "level"); int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1; if (level == 0 || level == -1) { /* level 0 = known local; level -1 = no annotation, try local first */ - int slot = mach_gen_find_var (s, lookup_name); + int slot = mach_gen_find_var (s, name); if (slot >= 0) return slot; } else if (level > 0) { MachGenState *target = s; for (int i = 0; i < level; i++) target = target->parent; - int parent_slot = mach_gen_find_var (target, lookup_name); + int parent_slot = mach_gen_find_var (target, name); int dest = mach_gen_alloc_slot (s); mach_gen_emit_3 (s, "get", dest, parent_slot, level); return dest; @@ -934,19 +928,17 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { if (strcmp (operand_kind, "name") == 0) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (operand, "name")); - const char *inc_sn = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (operand, "scope_name")); - const char *inc_ln = inc_sn ? inc_sn : name; cJSON *level_node = cJSON_GetObjectItemCaseSensitive (operand, "level"); int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1; int old_slot = mach_gen_alloc_slot (s); /* Load current value */ if (level == 0) { - int local = mach_gen_find_var (s, inc_ln); + int local = mach_gen_find_var (s, name); if (local >= 0) mach_gen_emit_2 (s, "move", old_slot, local); } else if (level > 0) { MachGenState *target = s; for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_gen_find_var (target, inc_ln); + int slot = mach_gen_find_var (target, name); mach_gen_emit_3 (s, "get", old_slot, slot, level); } else { cJSON *instr = cJSON_CreateArray (); @@ -963,12 +955,12 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot); /* Store new value */ if (level == 0) { - int local = mach_gen_find_var (s, inc_ln); + int local = mach_gen_find_var (s, name); if (local >= 0) mach_gen_emit_2 (s, "move", local, new_slot); } else if (level > 0) { MachGenState *target = s; for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_gen_find_var (target, inc_ln); + int slot = mach_gen_find_var (target, name); mach_gen_emit_3 (s, "put", new_slot, slot, level); } return postfix ? old_slot : new_slot; @@ -1135,9 +1127,7 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { cJSON *left = cJSON_GetObjectItemCaseSensitive (stmt, "left"); cJSON *right = cJSON_GetObjectItemCaseSensitive (stmt, "right"); const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name")); - const char *scope_name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "scope_name")); - const char *lookup_name = scope_name ? scope_name : name; - int local_slot = mach_gen_find_var (s, lookup_name); + int local_slot = mach_gen_find_var (s, name); /* Pop: var val = arr[] */ cJSON *pop_flag = cJSON_GetObjectItemCaseSensitive (stmt, "pop"); if (pop_flag && cJSON_IsTrue (pop_flag) && right) { @@ -3454,6 +3444,34 @@ JSValue JS_CallMcodeTree(JSContext *ctx, cJSON *root) { return result; } +JSValue JS_CallMcodeTreeEnv(JSContext *ctx, cJSON *root, JSValue env) { + if (!root) return JS_ThrowSyntaxError(ctx, "invalid MCODE tree"); + + cJSON *main_obj = cJSON_GetObjectItemCaseSensitive(root, "main"); + if (!main_obj) { + cJSON_Delete(root); + return JS_ThrowSyntaxError(ctx, "MCODE tree missing 'main' section"); + } + + cJSON *functions = cJSON_GetObjectItemCaseSensitive(root, "functions"); + + /* Parse main code */ + JSMCode *code = jsmcode_parse(main_obj, functions); + if (!code) { + cJSON_Delete(root); + return JS_ThrowInternalError(ctx, "failed to parse MCODE"); + } + code->json_root = root; /* Keep tree alive — instrs point into it */ + + /* Execute with global_obj as this, passing env as outer_frame */ + JSValue result = mcode_exec(ctx, code, ctx->global_obj, 0, NULL, env); + + /* Clear frame ref before freeing mcode — stack trace data is inside code */ + ctx->reg_current_frame = JS_NULL; + jsmcode_free(code); + return result; +} + JSValue JS_CallMcode(JSContext *ctx, const char *mcode_json) { cJSON *root = cJSON_Parse(mcode_json); if (!root) return JS_ThrowSyntaxError(ctx, "invalid MCODE JSON"); diff --git a/source/parse.c b/source/parse.c index ba48777b..e60c4ed6 100644 --- a/source/parse.c +++ b/source/parse.c @@ -966,7 +966,6 @@ cJSON *ast_parse_statement (ASTParseState *s) { case TOK_VAR: case TOK_DEF: { const char *kind_name = (s->token_val == TOK_VAR) ? "var" : "def"; - int is_def = (s->token_val == TOK_DEF); ast_next_token (s); /* Expect an identifier */ @@ -998,9 +997,9 @@ cJSON *ast_parse_statement (ASTParseState *s) { const char *right_kind = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (right, "kind")); if (right_kind && strcmp (right_kind, "[") == 0 && !cJSON_GetObjectItemCaseSensitive (right, "right")) cJSON_AddBoolToObject (node, "pop", 1); - } else if (is_def) { - /* def (constant) requires initializer */ - ast_error (s, var_ptr, "missing initializer for constant '%s'", var_name); + } else { + /* var and def both require initializer */ + ast_error (s, var_ptr, "missing initializer for '%s' '%s'", kind_name, var_name); } sys_free (var_name); ast_node_end (s, node, s->buf_ptr); @@ -1097,16 +1096,11 @@ cJSON *ast_parse_statement (ASTParseState *s) { if (s->token_val == '(') ast_next_token (s); else ast_error (s, s->token_ptr, "expected '(' after for"); - /* Init */ + /* Init — only expressions allowed (no var/def) */ if (s->token_val != ';') { - if (s->token_val == TOK_VAR || s->token_val == TOK_DEF) { - cJSON *init = ast_parse_statement (s); - cJSON_AddItemToObject (node, "init", init); - } else { - cJSON *init = ast_parse_expr (s); - cJSON_AddItemToObject (node, "init", init); - if (s->token_val == ';') ast_next_token (s); - } + cJSON *init = ast_parse_expr (s); + cJSON_AddItemToObject (node, "init", init); + if (s->token_val == ';') ast_next_token (s); } else { ast_next_token (s); } @@ -1271,7 +1265,6 @@ static cJSON *ast_parse_program (ASTParseState *s) { typedef struct ASTSemVar { const char *name; - const char *scope_name; /* disambiguated name for block-scope vars (NULL = use name) */ int is_const; const char *make; /* "def", "var", "function", "input" */ int function_nr; /* which function this var belongs to */ @@ -1286,7 +1279,6 @@ typedef struct ASTSemScope { int in_loop; int function_nr; /* function_nr of enclosing function */ int is_function_scope; /* 1 if this is a function's top-level scope */ - int block_depth; /* 0 = function scope, 1+ = block scope */ } ASTSemScope; typedef struct ASTSemState { @@ -1295,7 +1287,6 @@ typedef struct ASTSemState { cJSON *scopes_array; const char *intrinsics[256]; int intrinsic_count; - int block_var_counter; /* monotonically increasing counter for unique block var names */ } ASTSemState; static void ast_sem_error (ASTSemState *st, cJSON *node, const char *fmt, ...) { @@ -1324,7 +1315,6 @@ static void ast_sem_add_var (ASTSemScope *scope, const char *name, int is_const, if (scope->var_count < AST_SEM_MAX_VARS) { ASTSemVar *v = &scope->vars[scope->var_count]; v->name = name; - v->scope_name = NULL; v->is_const = is_const; v->make = make; v->function_nr = function_nr; @@ -1334,26 +1324,6 @@ static void ast_sem_add_var (ASTSemScope *scope, const char *name, int is_const, } } -/* Propagate block-scope vars to the function scope (parent) with disambiguated names */ -static void ast_sem_propagate_block_vars (ASTSemState *st, ASTSemScope *parent, - ASTSemScope *block) { - for (int i = 0; i < block->var_count; i++) { - ASTSemVar *v = &block->vars[i]; - const char *sn = v->scope_name ? v->scope_name : v->name; - if (parent->var_count < AST_SEM_MAX_VARS) { - ASTSemVar *pv = &parent->vars[parent->var_count]; - pv->name = sn; - pv->scope_name = NULL; - pv->is_const = v->is_const; - pv->make = v->make; - pv->function_nr = v->function_nr; - pv->nr_uses = v->nr_uses; - pv->closure = v->closure; - parent->var_count++; - } - } -} - typedef struct { ASTSemVar *var; int level; @@ -1486,8 +1456,6 @@ static void ast_sem_check_assign_target (ASTSemState *st, ASTSemScope *scope, cJ if (r.var) { cJSON_AddNumberToObject (left, "level", r.level); cJSON_AddNumberToObject (left, "function_nr", r.def_function_nr); - if (r.var->scope_name) - cJSON_AddStringToObject (left, "scope_name", r.var->scope_name); } else { cJSON_AddNumberToObject (left, "level", -1); } @@ -1540,13 +1508,11 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr } else if (v->is_const) { ast_sem_error (st, expr, "cannot assign to constant '%s'", name); } - /* Annotate with level/function_nr/scope_name so compilers can emit correct set instructions */ + /* Annotate with level/function_nr so compilers can emit correct set instructions */ ASTSemLookup r = ast_sem_lookup_var (scope, name); if (r.var) { cJSON_AddNumberToObject (operand, "level", r.level); cJSON_AddNumberToObject (operand, "function_nr", r.def_function_nr); - if (r.var->scope_name) - cJSON_AddStringToObject (operand, "scope_name", r.var->scope_name); } else { cJSON_AddNumberToObject (operand, "level", -1); } @@ -1697,8 +1663,6 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr cJSON_AddNumberToObject (expr, "function_nr", r.def_function_nr); r.var->nr_uses++; if (r.level > 0) r.var->closure = 1; - if (r.var->scope_name) - cJSON_AddStringToObject (expr, "scope_name", r.var->scope_name); } else { cJSON_AddNumberToObject (expr, "level", -1); ast_sem_add_intrinsic (st, name); @@ -1717,6 +1681,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt if (!kind) return; if (strcmp (kind, "var_list") == 0) { + if (!scope->is_function_scope) { + ast_sem_error (st, stmt, "'var' declaration must be at function body level"); + return; + } cJSON *item; cJSON_ArrayForEach (item, cJSON_GetObjectItemCaseSensitive (stmt, "list")) { ast_sem_check_stmt (st, scope, item); @@ -1725,6 +1693,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt } if (strcmp (kind, "var") == 0) { + if (!scope->is_function_scope) { + ast_sem_error (st, stmt, "'var' declaration must be at function body level"); + return; + } /* Register variable */ cJSON *left = cJSON_GetObjectItemCaseSensitive (stmt, "left"); const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name")); @@ -1733,23 +1705,18 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt if (existing && existing->is_const) { ast_sem_error (st, left, "cannot redeclare constant '%s'", name); } - if (!existing || existing->function_nr != scope->function_nr - || scope->block_depth > 0) + if (!existing || existing->function_nr != scope->function_nr) ast_sem_add_var (scope, name, 0, "var", scope->function_nr); - if (scope->block_depth > 0) { - char buf[128]; - snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++); - char *sn = sys_malloc (strlen (buf) + 1); - strcpy (sn, buf); - scope->vars[scope->var_count - 1].scope_name = sn; - cJSON_AddStringToObject (left, "scope_name", sn); - } } ast_sem_check_expr (st, scope, cJSON_GetObjectItemCaseSensitive (stmt, "right")); return; } if (strcmp (kind, "def") == 0) { + if (!scope->is_function_scope) { + ast_sem_error (st, stmt, "'def' declaration must be at function body level"); + return; + } /* Register constant */ cJSON *left = cJSON_GetObjectItemCaseSensitive (stmt, "left"); const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name")); @@ -1763,14 +1730,6 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt existing->make = "def"; } else { ast_sem_add_var (scope, name, 1, "def", scope->function_nr); - if (scope->block_depth > 0) { - char buf[128]; - snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++); - char *sn = sys_malloc (strlen (buf) + 1); - strcpy (sn, buf); - scope->vars[scope->var_count - 1].scope_name = sn; - cJSON_AddStringToObject (left, "scope_name", sn); - } } } ast_sem_check_expr (st, scope, cJSON_GetObjectItemCaseSensitive (stmt, "right")); @@ -1789,31 +1748,25 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt ASTSemScope then_scope = {0}; then_scope.parent = scope; then_scope.function_nr = scope->function_nr; - then_scope.block_depth = scope->block_depth + 1; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "then")) { ast_sem_check_stmt (st, &then_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &then_scope); } { ASTSemScope list_scope = {0}; list_scope.parent = scope; list_scope.function_nr = scope->function_nr; - list_scope.block_depth = scope->block_depth + 1; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "list")) { ast_sem_check_stmt (st, &list_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &list_scope); } { ASTSemScope else_scope = {0}; else_scope.parent = scope; else_scope.function_nr = scope->function_nr; - else_scope.block_depth = scope->block_depth + 1; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "else")) { ast_sem_check_stmt (st, &else_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &else_scope); } return; } @@ -1824,12 +1777,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt loop_scope.parent = scope; loop_scope.in_loop = 1; loop_scope.function_nr = scope->function_nr; - loop_scope.block_depth = scope->block_depth + 1; cJSON *s2; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) { ast_sem_check_stmt (st, &loop_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &loop_scope); return; } @@ -1838,12 +1789,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt loop_scope.parent = scope; loop_scope.in_loop = 1; loop_scope.function_nr = scope->function_nr; - loop_scope.block_depth = scope->block_depth + 1; cJSON *s2; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) { ast_sem_check_stmt (st, &loop_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &loop_scope); ast_sem_check_expr (st, scope, cJSON_GetObjectItemCaseSensitive (stmt, "expression")); return; } @@ -1853,24 +1802,16 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt loop_scope.parent = scope; loop_scope.in_loop = 1; loop_scope.function_nr = scope->function_nr; - loop_scope.block_depth = scope->block_depth + 1; - /* init may be a var/def statement or expression */ + /* init is expression only (no var/def in for init) */ cJSON *init = cJSON_GetObjectItemCaseSensitive (stmt, "init"); - if (init) { - const char *init_kind = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (init, "kind")); - if (init_kind && (strcmp (init_kind, "var") == 0 || strcmp (init_kind, "def") == 0)) { - ast_sem_check_stmt (st, &loop_scope, init); - } else { - ast_sem_check_expr (st, &loop_scope, init); - } - } + if (init) + ast_sem_check_expr (st, &loop_scope, init); ast_sem_check_expr (st, &loop_scope, cJSON_GetObjectItemCaseSensitive (stmt, "test")); ast_sem_check_expr (st, &loop_scope, cJSON_GetObjectItemCaseSensitive (stmt, "update")); cJSON *s2; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) { ast_sem_check_stmt (st, &loop_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &loop_scope); return; } @@ -1901,12 +1842,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt ASTSemScope block_scope = {0}; block_scope.parent = scope; block_scope.function_nr = scope->function_nr; - block_scope.block_depth = scope->block_depth + 1; cJSON *s2; cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) { ast_sem_check_stmt (st, &block_scope, s2); } - ast_sem_propagate_block_vars (st, scope, &block_scope); return; } diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index b17464cc..54f70090 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -101,7 +101,7 @@ // #define DUMP_ROPE_REBALANCE /* test the GC by forcing it before each object allocation */ -#define FORCE_GC_AT_MALLOC +// #define FORCE_GC_AT_MALLOC #define POISON_HEAP /* POISON_HEAP: Use ASan's memory poisoning to detect stale pointer access */ @@ -1738,7 +1738,6 @@ typedef struct MachVarInfo { int slot; int is_const; /* 1 for def, function args; 0 for var */ int is_closure; /* 1 if captured by a nested function */ - int scope_depth; /* block scope nesting level */ } MachVarInfo; /* === PPretext (parser pretext, system-malloc, used by cell_js.c parser) === */ diff --git a/source/quickjs.h b/source/quickjs.h index f9f75919..d00ce026 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -1268,6 +1268,9 @@ char *JS_Mcode (const char *ast_json); /* Execute MCODE from cJSON tree. Takes ownership of root. */ JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root); +/* Execute MCODE from cJSON tree with hidden env. Takes ownership of root. */ +JSValue JS_CallMcodeTreeEnv (JSContext *ctx, struct cJSON *root, JSValue env); + /* Parse and execute MCODE JSON string. Returns result of execution, or JS_EXCEPTION on error. */ JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json); diff --git a/source/runtime.c b/source/runtime.c index e3ec5137..359edebf 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -10655,7 +10655,8 @@ static JSValue js_mach_eval (JSContext *ctx, JSValue this_val, int argc, JSValue return JS_ThrowSyntaxError (ctx, "mach_eval: failed to parse AST"); } - JSValue result = JS_RunMachTree (ctx, ast, JS_NULL); + JSValue env = (argc >= 3 && JS_IsObject (argv[2])) ? argv[2] : JS_NULL; + JSValue result = JS_RunMachTree (ctx, ast, env); cJSON_Delete (ast); JS_FreeCString (ctx, name); return result; diff --git a/syntax_suite.ce b/syntax_suite.ce index 2eefab84..51a36662 100644 --- a/syntax_suite.ce +++ b/syntax_suite.ce @@ -7,7 +7,8 @@ var error_names = [] var error_reasons = [] var fail_msg = "" -for (var _i = 0; _i < 100; _i++) { +var _i = 0 +for (_i = 0; _i < 100; _i++) { error_names[] = null error_reasons[] = null } @@ -105,11 +106,6 @@ run("var declaration", function() { assert_eq(x, 5, "var init") }) -run("var uninitialized", function() { - var x - assert_eq(x, null, "var defaults to null") -}) - run("var multiple declaration", function() { var a = 1, b = 2, c = 3 assert_eq(a + b + c, 6, "multi var") @@ -330,19 +326,21 @@ run("while break continue", function() { run("for loop", function() { var sum = 0 - for (var i = 0; i < 5; i++) sum += i + var i = 0 + for (i = 0; i < 5; i++) sum += i assert_eq(sum, 10, "for basic") }) run("for break continue", function() { var sum = 0 - for (var i = 0; i < 10; i++) { + var i = 0 + for (i = 0; i < 10; i++) { if (i == 5) break sum += i } assert_eq(sum, 10, "for break") sum = 0 - for (var i = 0; i < 5; i++) { + for (i = 0; i < 5; i++) { if (i % 2 == 0) continue sum += i } @@ -351,28 +349,13 @@ run("for break continue", function() { run("nested for", function() { var sum = 0 - for (var i = 0; i < 3; i++) - for (var j = 0; j < 3; j++) + var i = 0, j = 0 + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) sum++ assert_eq(sum, 9, "nested for") }) -// === BLOCK SCOPING === - -run("block scoping", function() { - var x = 1 - { - var x = 2 - assert_eq(x, 2, "inner block") - } - assert_eq(x, 1, "outer preserved") -}) - -run("for iterator scope", function() { - for (var i = 0; i < 1; i++) {} - assert_eq(should_disrupt(function() { var y = i }), true, "for var does not leak") -}) - // === FUNCTIONS === run("function expression", function() { @@ -649,9 +632,10 @@ run("inequality not confused with bang ident", function() { // === SUMMARY === print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed)) +var _j = 0 if (failed > 0) { print("") - for (var _j = 0; _j < failed; _j++) { + for (_j = 0; _j < failed; _j++) { print(" FAIL " + error_names[_j] + ": " + error_reasons[_j]) } } diff --git a/vm_suite.ce b/vm_suite.ce index 3becf158..5f362ff3 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -9,7 +9,8 @@ var error_reasons = [] var fail_msg = "" // pre-allocate 500 slots to avoid array growth during disruption handlers -for (var _i = 0; _i < 5; _i++) { +var _i = 0 +for (_i = 0; _i < 5; _i++) { error_names[] = null error_reasons[] = null } @@ -295,79 +296,9 @@ run("var reassignment", function() { if (x != 10) fail("var reassignment failed") }) -run("var block scope basic", function() { - var x = 1 - { - var x = 2 - if (x != 2) fail("var should be block scoped - inner scope failed") - } - if (x != 1) fail("var should be block scoped - outer scope affected") -}) - -run("var block scope if", function() { - var x = 1 - if (true) { - var x = 2 - if (x != 2) fail("var in if block should be scoped") - } - if (x != 1) fail("var in if block should not affect outer scope") -}) - -run("var block scope for", function() { - var x = 1 - for (var i = 0; i < 1; i = i + 1) { - var x = 2 - if (x != 2) fail("var in for block should be scoped") - } - if (x != 1) fail("var in for block should not affect outer scope") -}) - -run("var for loop iterator scope", function() { - var sum = 0 - for (var i = 0; i < 3; i = i + 1) { - sum = sum + i - } - if (sum != 3) fail("for loop should work with block scoped var") - if (!should_disrupt(function() { var y = i })) fail("for loop iterator should not leak to outer scope") -}) - -run("var nested blocks", function() { - var x = 1 - { - var x = 2 - { - var x = 3 - if (x != 3) fail("var in nested block level 2 failed") - } - if (x != 2) fail("var in nested block level 1 failed") - } - if (x != 1) fail("var in nested blocks outer scope failed") -}) - -run("var redeclaration different scope", function() { - var x = 1 - { - var x = 2 - } - if (x != 1) fail("var in different scope should not affect outer") -}) - -run("var while scope", function() { - var x = 1 - var count = 0 - while (count < 1) { - var x = 2 - if (x != 2) fail("var in while should be block scoped") - count = count + 1 - } - if (x != 1) fail("var in while should not affect outer scope") -}) - -run("var no initialization", function() { - { - var x - if (x != null) fail("uninitialized var should be null") - } +run("var null initialization", function() { + var x = null + if (x != null) fail("null-initialized var should be null") }) run("multiple var declaration", function() { @@ -717,7 +648,8 @@ run("while continue", function() { run("for loop", function() { var sum = 0 - for (var i = 0; i < 5; i = i + 1) { + var i = 0 + for (i = 0; i < 5; i = i + 1) { sum = sum + i } if (sum != 10) fail("for loop failed") @@ -725,7 +657,8 @@ run("for loop", function() { run("for loop break", function() { var sum = 0 - for (var i = 0; i < 10; i = i + 1) { + var i = 0 + for (i = 0; i < 10; i = i + 1) { if (i == 5) break sum = sum + i } @@ -734,7 +667,8 @@ run("for loop break", function() { run("for loop continue", function() { var sum = 0 - for (var i = 0; i < 10; i = i + 1) { + var i = 0 + for (i = 0; i < 10; i = i + 1) { if (i % 2 == 0) continue sum = sum + i } @@ -743,8 +677,9 @@ run("for loop continue", function() { run("nested for loops", function() { var sum = 0 - for (var i = 0; i < 3; i = i + 1) { - for (var j = 0; j < 3; j = j + 1) { + var i = 0, j = 0 + for (i = 0; i < 3; i = i + 1) { + for (j = 0; j < 3; j = j + 1) { sum = sum + 1 } } @@ -877,7 +812,7 @@ run("is_null", function() { if (is_null("")) fail("is_null empty string should be false") if (is_null({})) fail("is_null object should be false") if (is_null([])) fail("is_null array should be false") - var x + var x = null if (!is_null(x)) fail("is_null undefined variable should be true") }) @@ -1265,9 +1200,9 @@ run("function length property", function() { // NULL AND UNDEFINED BEHAVIOR // ============================================================================ -run("undefined variable is null", function() { - var x - if (x != null) fail("undefined variable should be null") +run("null initialized variable is null", function() { + var x = null + if (x != null) fail("null initialized variable should be null") }) // ============================================================================ @@ -1716,7 +1651,8 @@ run("function proxy with one arg", function() { run("function proxy with multiple args", function() { var proxy = function(name, args) { var sum = 0 - for (var i = 0; i < length(args); i++) { + var i = 0 + for (i = 0; i < length(args); i++) { sum = sum + args[i] } return `${name}:${sum}` @@ -3183,7 +3119,8 @@ run("gc cycle object self", function() { run("gc cycle array self", function() { var arr = [] - for (var i = 0; i < 10; i++) { + var i = 0 + for (i = 0; i < 10; i++) { arr[] = arr } if (arr[0] != arr) fail("array self cycle failed") @@ -3336,22 +3273,26 @@ run("fn.apply with single value", function() { run("gc reverse under pressure", function() { var arrays = [] - for (var i = 0; i < 100; i = i + 1) { + var i = 0 + var rev = null + for (i = 0; i < 100; i = i + 1) { arrays[i] = [i, i+1, i+2, i+3, i+4] } - for (var i = 0; i < 100; i = i + 1) { - var rev = reverse(arrays[i]) + for (i = 0; i < 100; i = i + 1) { + rev = reverse(arrays[i]) if (rev[0] != i+4) fail("gc reverse stress failed") } }) run("gc object select under pressure", function() { var objs = [] - for (var i = 0; i < 100; i = i + 1) { + var i = 0 + var selected = null + for (i = 0; i < 100; i = i + 1) { objs[i] = {a: i, b: i+1, c: i+2, d: i+3} } - for (var i = 0; i < 100; i = i + 1) { - var selected = object(objs[i], ["a", "c"]) + for (i = 0; i < 100; i = i + 1) { + selected = object(objs[i], ["a", "c"]) if (selected.a != i) fail("gc object select stress failed") if (selected.c != i+2) fail("gc object select stress c failed") } @@ -3359,12 +3300,15 @@ run("gc object select under pressure", function() { run("gc object from keys function under pressure", function() { var keysets = [] - for (var i = 0; i < 50; i = i + 1) { + var i = 0 + var obj = null + var expected = null + for (i = 0; i < 50; i = i + 1) { keysets[i] = [`k${i}`, `j${i}`, `m${i}`] } - for (var i = 0; i < 50; i = i + 1) { - var obj = object(keysets[i], function(k) { return k + "_value" }) - var expected = `k${i}_value` + for (i = 0; i < 50; i = i + 1) { + obj = object(keysets[i], function(k) { return k + "_value" }) + expected = `k${i}_value` if (obj[`k${i}`] != expected) fail("gc object from keys func stress failed") } }) @@ -3550,9 +3494,10 @@ run("functino >>>! unsigned shift right", function() { // ============================================================================ print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed)) +var _j = 0 if (failed > 0) { print("") - for (var _j = 0; _j < failed; _j++) { + for (_j = 0; _j < failed; _j++) { print(" FAIL " + error_names[_j] + ": " + error_reasons[_j]) } }