diff --git a/source/cell.c b/source/cell.c index 72ee4b49..fb889c4a 100644 --- a/source/cell.c +++ b/source/cell.c @@ -465,6 +465,74 @@ int cell_init(int argc, char **argv) return mach_json ? 0 : 1; } + /* Check for --vmcode flag to dump linked register VM bytecode */ + if (argc >= 3 && strcmp(argv[1], "--vmcode") == 0) { + const char *script_or_file = argv[2]; + char *script = NULL; + char *allocated_script = NULL; + const char *filename = ""; + + 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; + } + + JSRuntime *rt = JS_NewRuntime(); + if (!rt) { + printf("Failed to create JS runtime\n"); + free(allocated_script); + return 1; + } + JSContext *ctx = JS_NewContext(rt); + if (!ctx) { + printf("Failed to create JS context\n"); + JS_FreeRuntime(rt); + free(allocated_script); + return 1; + } + + char *ast_json = JS_AST(ctx, script, strlen(script), filename); + if (!ast_json) { + printf("Failed to parse AST\n"); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + free(allocated_script); + return 1; + } + + char *mach_json = JS_Mach(ctx, ast_json); + free(ast_json); + + if (mach_json) { + JS_DumpRegisterMach(ctx, mach_json, JS_NULL); + free(mach_json); + } else { + printf("Failed to generate machine code\n"); + } + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + free(allocated_script); + return mach_json ? 0 : 1; + } + /* Check for --mach-run flag to generate and run machine code through register VM */ if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) { const char *script_or_file = argv[2]; diff --git a/source/quickjs.c b/source/quickjs.c index fe87a2eb..1fde7207 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -541,6 +541,78 @@ typedef enum MachOpcode { MACH_OP_COUNT } MachOpcode; +static const char *mach_opcode_names[MACH_OP_COUNT] = { + [MACH_OP_NOP] = "nop", + [MACH_OP_ACCESS] = "access", + [MACH_OP_NULL] = "null", + [MACH_OP_TRUE] = "true", + [MACH_OP_FALSE] = "false", + [MACH_OP_MOVE] = "move", + [MACH_OP_ADD] = "add", + [MACH_OP_SUB] = "sub", + [MACH_OP_MUL] = "mul", + [MACH_OP_DIV] = "div", + [MACH_OP_MOD] = "mod", + [MACH_OP_POW] = "pow", + [MACH_OP_NEG] = "neg", + [MACH_OP_INC] = "inc", + [MACH_OP_DEC] = "dec", + [MACH_OP_EQ] = "eq", + [MACH_OP_NE] = "ne", + [MACH_OP_LT] = "lt", + [MACH_OP_LE] = "le", + [MACH_OP_GT] = "gt", + [MACH_OP_GE] = "ge", + [MACH_OP_STRICT_EQ] = "strict_eq", + [MACH_OP_STRICT_NE] = "strict_ne", + [MACH_OP_NOT] = "not", + [MACH_OP_BITNOT] = "bitnot", + [MACH_OP_BITAND] = "bitand", + [MACH_OP_BITOR] = "bitor", + [MACH_OP_BITXOR] = "bitxor", + [MACH_OP_SHL] = "shl", + [MACH_OP_SHR] = "shr", + [MACH_OP_USHR] = "ushr", + [MACH_OP_LOAD_PROP] = "load_prop", + [MACH_OP_STORE_PROP] = "store_prop", + [MACH_OP_LOAD_IDX] = "load_idx", + [MACH_OP_STORE_IDX] = "store_idx", + [MACH_OP_DELETE_PROP] = "delete_prop", + [MACH_OP_DELETE_IDX] = "delete_idx", + [MACH_OP_GET] = "get", + [MACH_OP_PUT] = "put", + [MACH_OP_CAP_NAME] = "cap_name", + [MACH_OP_GET_ENV_SLOT] = "get_env_slot", + [MACH_OP_SET_ENV_SLOT] = "set_env_slot", + [MACH_OP_GET_GLOBAL_SLOT] = "get_global_slot", + [MACH_OP_SET_GLOBAL_SLOT] = "set_global_slot", + [MACH_OP_SET_VAR] = "set_var", + [MACH_OP_JUMP] = "jump", + [MACH_OP_JUMP_TRUE] = "jump_true", + [MACH_OP_JUMP_FALSE] = "jump_false", + [MACH_OP_JUMP_NOT_NULL] = "jump_not_null", + [MACH_OP_FRAME] = "frame", + [MACH_OP_SET_THIS] = "set_this", + [MACH_OP_ARG] = "arg", + [MACH_OP_INVOKE] = "invoke", + [MACH_OP_GOFRAME] = "goframe", + [MACH_OP_GOINVOKE] = "goinvoke", + [MACH_OP_MKFUNC] = "mkfunc", + [MACH_OP_MKFUNC_ARROW] = "mkfunc_arrow", + [MACH_OP_MKRECORD] = "mkrecord", + [MACH_OP_MKARRAY] = "mkarray", + [MACH_OP_MKNEW] = "mknew", + [MACH_OP_RETURN] = "return", + [MACH_OP_RETURN_UNDEF] = "return_undef", + [MACH_OP_THROW] = "throw", + [MACH_OP_TRY_BEGIN] = "try_begin", + [MACH_OP_TRY_END] = "try_end", + [MACH_OP_CATCH_BEGIN] = "catch_begin", + [MACH_OP_CATCH_END] = "catch_end", + [MACH_OP_FINALLY_BEGIN] = "finally_begin", + [MACH_OP_FINALLY_END] = "finally_end", +}; + /* Compiled instruction for register VM. Each instruction has opcode + up to 3 operands. Operands can be register indices, constant pool indices, or literal values. */ @@ -32184,6 +32256,332 @@ done: Register VM Public API ============================================================ */ +/* Print a single constant pool value for dump output */ +static void dump_cpool_value(JSContext *ctx, JSValue val) { + uint32_t tag = JS_VALUE_GET_TAG(val); + + if (JS_IsPtr(val)) { + void *ptr = JS_VALUE_GET_PTR(val); + objhdr_t hdr = *(objhdr_t *)ptr; + uint8_t mist_type = objhdr_type(hdr); + if (mist_type == OBJ_TEXT) { + const char *str = JS_ToCString(ctx, val); + if (str) { + printf("\"%s\"", str); + JS_FreeCString(ctx, str); + } else { + printf(""); + } + return; + } + printf("", mist_type); + return; + } + + switch (tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + printf("%s", JS_VALUE_GET_BOOL(val) ? "true" : "false"); + break; + case JS_TAG_NULL: + printf("null"); + break; + case JS_TAG_SHORT_FLOAT: + printf("%g", JS_VALUE_GET_FLOAT64(val)); + break; + case JS_TAG_STRING_IMM: { + const char *str = JS_ToCString(ctx, val); + if (str) { + printf("\"%s\"", str); + JS_FreeCString(ctx, str); + } else { + printf(""); + } + break; + } + default: + printf("", tag); + break; + } +} + +/* Internal helper to dump JSCodeRegister */ +static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent) { + char pad[64]; + int pad_len = indent * 2; + if (pad_len > 60) pad_len = 60; + memset(pad, ' ', pad_len); + pad[pad_len] = '\0'; + + /* Function header */ + const char *name = ""; + if (!JS_IsNull(code->name)) { + const char *n = JS_ToCString(ctx, code->name); + if (n) name = n; + } + printf("%sFunction: %s\n", pad, name); + printf("%s Arity: %d, Slots: %d, Close: %d\n", pad, + code->arity, code->nr_slots, code->nr_close_slots); + if (!JS_IsNull(code->name)) { + JS_FreeCString(ctx, name); + } + + /* Constant pool */ + if (code->cpool_count > 0) { + printf("%s\n%sConstant Pool (%d entries):\n", pad, pad, code->cpool_count); + for (uint32_t i = 0; i < code->cpool_count; i++) { + printf("%s [%d]: ", pad, i); + dump_cpool_value(ctx, code->cpool[i]); + printf("\n"); + } + } + + /* Instructions */ + printf("%s\n%sInstructions (%d):\n", pad, pad, code->instr_count); + for (uint32_t i = 0; i < code->instr_count; i++) { + MachInstr *instr = &code->instructions[i]; + uint8_t op = instr->opcode; + const char *op_name = (op < MACH_OP_COUNT) ? mach_opcode_names[op] : "???"; + + printf("%s %3d: %-14s ", pad, i, op_name); + + switch (op) { + /* No operands */ + case MACH_OP_NOP: + case MACH_OP_RETURN_UNDEF: + case MACH_OP_TRY_END: + case MACH_OP_CATCH_END: + case MACH_OP_FINALLY_BEGIN: + case MACH_OP_FINALLY_END: + break; + + /* dest only */ + case MACH_OP_NULL: + case MACH_OP_TRUE: + case MACH_OP_FALSE: + printf("r%d", instr->a); + break; + + /* dest, cpool_idx */ + case MACH_OP_ACCESS: + printf("r%d, #%d", instr->a, instr->b); + if (instr->b >= 0 && (uint32_t)instr->b < code->cpool_count) { + printf(" ; "); + dump_cpool_value(ctx, code->cpool[instr->b]); + } + break; + + /* dest, src */ + case MACH_OP_MOVE: + case MACH_OP_NEG: + case MACH_OP_INC: + case MACH_OP_DEC: + case MACH_OP_NOT: + case MACH_OP_BITNOT: + printf("r%d, r%d", instr->a, instr->b); + break; + + /* dest, src1, src2 */ + case MACH_OP_ADD: + case MACH_OP_SUB: + case MACH_OP_MUL: + case MACH_OP_DIV: + case MACH_OP_MOD: + case MACH_OP_POW: + case MACH_OP_EQ: + case MACH_OP_NE: + case MACH_OP_LT: + case MACH_OP_LE: + case MACH_OP_GT: + case MACH_OP_GE: + case MACH_OP_STRICT_EQ: + case MACH_OP_STRICT_NE: + case MACH_OP_BITAND: + case MACH_OP_BITOR: + case MACH_OP_BITXOR: + case MACH_OP_SHL: + case MACH_OP_SHR: + case MACH_OP_USHR: + printf("r%d, r%d, r%d", instr->a, instr->b, instr->c); + break; + + /* dest, obj, cpool_idx (property) */ + case MACH_OP_LOAD_PROP: + printf("r%d, r%d, #%d", instr->a, instr->b, instr->c); + if (instr->c >= 0 && (uint32_t)instr->c < code->cpool_count) { + printf(" ; "); + dump_cpool_value(ctx, code->cpool[instr->c]); + } + break; + + /* obj, val, cpool_idx (property) */ + case MACH_OP_STORE_PROP: + printf("r%d, r%d, #%d", instr->a, instr->b, instr->c); + if (instr->c >= 0 && (uint32_t)instr->c < code->cpool_count) { + printf(" ; "); + dump_cpool_value(ctx, code->cpool[instr->c]); + } + break; + + /* dest, obj, idx */ + case MACH_OP_LOAD_IDX: + case MACH_OP_DELETE_IDX: + printf("r%d, r%d, r%d", instr->a, instr->b, instr->c); + break; + + /* obj, idx, val */ + case MACH_OP_STORE_IDX: + printf("r%d, r%d, r%d", instr->a, instr->b, instr->c); + break; + + /* dest, obj, cpool_idx */ + case MACH_OP_DELETE_PROP: + printf("r%d, r%d, #%d", instr->a, instr->b, instr->c); + break; + + /* dest, slot, depth */ + case MACH_OP_GET: + printf("r%d, slot=%d, depth=%d", instr->a, instr->b, instr->c); + break; + + /* src, slot, depth */ + case MACH_OP_PUT: + printf("r%d, slot=%d, depth=%d", instr->a, instr->b, instr->c); + break; + + /* dest, cpool_idx (name) */ + case MACH_OP_CAP_NAME: + printf("r%d, #%d", instr->a, instr->b); + if (instr->b >= 0 && (uint32_t)instr->b < code->cpool_count) { + printf(" ; "); + dump_cpool_value(ctx, code->cpool[instr->b]); + } + break; + + /* dest, slot_idx */ + case MACH_OP_GET_ENV_SLOT: + case MACH_OP_GET_GLOBAL_SLOT: + printf("r%d, slot=%d", instr->a, instr->b); + break; + + /* src, slot_idx */ + case MACH_OP_SET_ENV_SLOT: + case MACH_OP_SET_GLOBAL_SLOT: + printf("r%d, slot=%d", instr->a, instr->b); + break; + + /* cpool_idx, src */ + case MACH_OP_SET_VAR: + printf("#%d, r%d", instr->a, instr->b); + if (instr->a >= 0 && (uint32_t)instr->a < code->cpool_count) { + printf(" ; "); + dump_cpool_value(ctx, code->cpool[instr->a]); + } + break; + + /* label (resolved to instruction index) */ + case MACH_OP_JUMP: + case MACH_OP_TRY_BEGIN: + printf("@%d", instr->a); + break; + + /* cond, label (resolved to instruction index) */ + case MACH_OP_JUMP_TRUE: + case MACH_OP_JUMP_FALSE: + case MACH_OP_JUMP_NOT_NULL: + printf("r%d, @%d", instr->a, instr->b); + break; + + /* frame_reg, func_reg, argc */ + case MACH_OP_FRAME: + case MACH_OP_GOFRAME: + printf("r%d, r%d, %d", instr->a, instr->b, instr->c); + break; + + /* frame_reg, this_reg */ + case MACH_OP_SET_THIS: + printf("r%d, r%d", instr->a, instr->b); + break; + + /* frame_reg, arg_idx, val_reg */ + case MACH_OP_ARG: + printf("r%d, [%d], r%d", instr->a, instr->b, instr->c); + break; + + /* frame_reg, ret_reg */ + case MACH_OP_INVOKE: + printf("r%d, r%d", instr->a, instr->b); + break; + + /* frame_reg */ + case MACH_OP_GOINVOKE: + printf("r%d", instr->a); + break; + + /* dest, func_id */ + case MACH_OP_MKFUNC: + case MACH_OP_MKFUNC_ARROW: + printf("r%d, func#%d", instr->a, instr->b); + break; + + /* dest, argc */ + case MACH_OP_MKRECORD: + case MACH_OP_MKARRAY: + printf("r%d, %d", instr->a, instr->b); + break; + + /* dest, func_reg, argc */ + case MACH_OP_MKNEW: + printf("r%d, r%d, %d", instr->a, instr->b, instr->c); + break; + + /* src */ + case MACH_OP_RETURN: + case MACH_OP_THROW: + printf("r%d", instr->a); + break; + + /* dest */ + case MACH_OP_CATCH_BEGIN: + printf("r%d", instr->a); + break; + + default: + printf("a=%d, b=%d, c=%d", instr->a, instr->b, instr->c); + break; + } + printf("\n"); + } + + /* Nested functions */ + if (code->func_count > 0) { + printf("%s\n%sNested Functions (%d):\n", pad, pad, code->func_count); + for (uint32_t i = 0; i < code->func_count; i++) { + printf("%s [%d]:\n", pad, i); + if (code->functions[i]) { + dump_register_code(ctx, code->functions[i], indent + 2); + } else { + printf("%s \n", pad); + } + } + } +} + +/* Dump linked register VM bytecode */ +void JS_DumpRegisterMach(JSContext *ctx, const char *mach_json, JSValue env) { + JSCodeRegister *code = js_link_mach(ctx, mach_json, env); + if (!code) { + printf("=== Register VM Bytecode ===\nFailed to link machine code\n=== End Register VM Bytecode ===\n"); + return; + } + + printf("=== Register VM Bytecode ===\n"); + dump_register_code(ctx, code, 0); + printf("=== End Register VM Bytecode ===\n"); +} + /* Link and execute register-based machine code */ JSValue JS_IntegrateRegister(JSContext *ctx, const char *mach_json, JSValue env) { /* Link the machine code */ diff --git a/source/quickjs.h b/source/quickjs.h index 0bf2f0e0..80b621e0 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -1232,6 +1232,10 @@ char *JS_Mach (JSContext *ctx, const char *ast_json); Returns result of execution, or JS_EXCEPTION on error. */ JSValue JS_IntegrateRegister (JSContext *ctx, const char *mach_json, JSValue env); +/* Dump linked register VM bytecode to stdout for debugging. + Links mach_json and prints the resulting bytecode with resolved labels. */ +void JS_DumpRegisterMach (JSContext *ctx, const char *mach_json, JSValue env); + /* Integrate a CellModule with an environment and execute. Returns callable function value, or JS_EXCEPTION on error. */ JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);