bytecode dump
This commit is contained in:
@@ -231,7 +231,7 @@ static int run_test_suite(size_t heap_size)
|
||||
}
|
||||
|
||||
/* Run an immediate script string */
|
||||
static int run_eval(const char *script)
|
||||
static int run_eval(const char *script, int print_bytecode)
|
||||
{
|
||||
if (!find_cell_shop()) return 1;
|
||||
|
||||
@@ -253,13 +253,33 @@ static int run_eval(const char *script)
|
||||
JS_AddIntrinsicRegExp(ctx);
|
||||
JS_AddIntrinsicJSON(ctx);
|
||||
|
||||
JSValue v = JS_Eval(ctx, script, strlen(script), "<eval>", 0);
|
||||
int result = 0;
|
||||
if (JS_IsException(v)) {
|
||||
uncaught_exception(ctx, v);
|
||||
result = 1;
|
||||
|
||||
if (print_bytecode) {
|
||||
/* Compile only, then dump and optionally execute */
|
||||
JSValue func = JS_Eval(ctx, script, strlen(script), "<eval>", JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
if (JS_IsException(func)) {
|
||||
uncaught_exception(ctx, func);
|
||||
result = 1;
|
||||
} else {
|
||||
JS_DumpFunctionBytecode(ctx, func);
|
||||
/* Now execute it */
|
||||
JSValue v = JS_EvalFunction(ctx, func);
|
||||
if (JS_IsException(v)) {
|
||||
uncaught_exception(ctx, v);
|
||||
result = 1;
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
JSValue v = JS_Eval(ctx, script, strlen(script), "<eval>", 0);
|
||||
if (JS_IsException(v)) {
|
||||
uncaught_exception(ctx, v);
|
||||
result = 1;
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
}
|
||||
}
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
@@ -283,8 +303,12 @@ int cell_init(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Check for -e or --eval flag to run immediate script */
|
||||
/* Also check for -p flag to print bytecode */
|
||||
if (argc >= 3 && (strcmp(argv[1], "-e") == 0 || strcmp(argv[1], "--eval") == 0)) {
|
||||
return run_eval(argv[2]);
|
||||
return run_eval(argv[2], 0);
|
||||
}
|
||||
if (argc >= 3 && (strcmp(argv[1], "-p") == 0 || strcmp(argv[1], "--print-bytecode") == 0)) {
|
||||
return run_eval(argv[2], 1);
|
||||
}
|
||||
|
||||
int script_start = 1;
|
||||
|
||||
270
source/quickjs.c
270
source/quickjs.c
@@ -22723,6 +22723,276 @@ static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *ar
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Bytecode dump function (always available, for debugging)
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Opcode names for bytecode dump */
|
||||
static const char *dump_opcode_names[] = {
|
||||
#define FMT(f)
|
||||
#define DEF(id, size, n_pop, n_push, f) #id,
|
||||
#define def(id, size, n_pop, n_push, f)
|
||||
#include "quickjs-opcode.h"
|
||||
#undef def
|
||||
#undef DEF
|
||||
#undef FMT
|
||||
};
|
||||
|
||||
static void dump_bytecode_opcodes (JSContext *ctx, JSFunctionBytecode *b) {
|
||||
const uint8_t *tab = b->byte_code_buf;
|
||||
int len = b->byte_code_len;
|
||||
const JSValue *cpool = b->cpool;
|
||||
uint32_t cpool_count = b->cpool_count;
|
||||
const JSVarDef *vars = b->vardefs ? b->vardefs + b->arg_count : NULL;
|
||||
int var_count = b->var_count;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < len) {
|
||||
int op = tab[pos];
|
||||
if (op >= OP_COUNT) {
|
||||
printf (" %5d: <invalid opcode 0x%02x>\n", pos, op);
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
const JSOpCode *oi = &short_opcode_info (op);
|
||||
int size = oi->size;
|
||||
if (pos + size > len) {
|
||||
printf (" %5d: <truncated opcode 0x%02x>\n", pos, op);
|
||||
break;
|
||||
}
|
||||
|
||||
printf (" %5d: %s", pos, dump_opcode_names[op]);
|
||||
pos++;
|
||||
|
||||
switch (oi->fmt) {
|
||||
case OP_FMT_none_int:
|
||||
printf (" %d", op - OP_push_0);
|
||||
break;
|
||||
case OP_FMT_npopx:
|
||||
printf (" %d", op - OP_call0);
|
||||
break;
|
||||
case OP_FMT_u8:
|
||||
printf (" %u", get_u8 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_i8:
|
||||
printf (" %d", get_i8 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_u16:
|
||||
case OP_FMT_npop:
|
||||
printf (" %u", get_u16 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_npop_u16:
|
||||
printf (" %u,%u", get_u16 (tab + pos), get_u16 (tab + pos + 2));
|
||||
break;
|
||||
case OP_FMT_i16:
|
||||
printf (" %d", get_i16 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_i32:
|
||||
printf (" %d", get_i32 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_u32:
|
||||
printf (" %u", get_u32 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_label8:
|
||||
printf (" ->%d", pos + get_i8 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_label16:
|
||||
printf (" ->%d", pos + get_i16 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_label:
|
||||
printf (" ->%u", pos + get_u32 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_label_u16:
|
||||
printf (" ->%u,%u", pos + get_u32 (tab + pos), get_u16 (tab + pos + 4));
|
||||
break;
|
||||
case OP_FMT_const8: {
|
||||
uint32_t idx = get_u8 (tab + pos);
|
||||
printf (" [%u]", idx);
|
||||
if (idx < cpool_count) {
|
||||
printf (": ");
|
||||
JS_PrintValue (ctx, js_dump_value_write, stdout, cpool[idx], NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_const: {
|
||||
uint32_t idx = get_u32 (tab + pos);
|
||||
printf (" [%u]", idx);
|
||||
if (idx < cpool_count) {
|
||||
printf (": ");
|
||||
JS_PrintValue (ctx, js_dump_value_write, stdout, cpool[idx], NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_key: {
|
||||
uint32_t idx = get_u32 (tab + pos);
|
||||
printf (" [%u]", idx);
|
||||
if (idx < cpool_count) {
|
||||
printf (": ");
|
||||
JS_PrintValue (ctx, js_dump_value_write, stdout, cpool[idx], NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_key_u8: {
|
||||
uint32_t idx = get_u32 (tab + pos);
|
||||
printf (" [%u],%d", idx, get_u8 (tab + pos + 4));
|
||||
if (idx < cpool_count) {
|
||||
printf (": ");
|
||||
JS_PrintValue (ctx, js_dump_value_write, stdout, cpool[idx], NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_key_u16: {
|
||||
uint32_t idx = get_u32 (tab + pos);
|
||||
printf (" [%u],%d", idx, get_u16 (tab + pos + 4));
|
||||
if (idx < cpool_count) {
|
||||
printf (": ");
|
||||
JS_PrintValue (ctx, js_dump_value_write, stdout, cpool[idx], NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_none_loc:
|
||||
printf (" loc%d", (op - OP_get_loc0) % 4);
|
||||
break;
|
||||
case OP_FMT_loc8: {
|
||||
int idx = get_u8 (tab + pos);
|
||||
printf (" loc%d", idx);
|
||||
if (vars && idx < var_count) {
|
||||
char buf[KEY_GET_STR_BUF_SIZE];
|
||||
printf (": %s", JS_KeyGetStr (ctx, buf, sizeof(buf), vars[idx].var_name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_loc: {
|
||||
int idx = get_u16 (tab + pos);
|
||||
printf (" loc%d", idx);
|
||||
if (vars && idx < var_count) {
|
||||
char buf[KEY_GET_STR_BUF_SIZE];
|
||||
printf (": %s", JS_KeyGetStr (ctx, buf, sizeof(buf), vars[idx].var_name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_FMT_none_arg:
|
||||
printf (" arg%d", (op - OP_get_arg0) % 4);
|
||||
break;
|
||||
case OP_FMT_arg:
|
||||
printf (" arg%d", get_u16 (tab + pos));
|
||||
break;
|
||||
case OP_FMT_none_var_ref:
|
||||
printf (" var_ref%d", (op - OP_get_var_ref0) % 4);
|
||||
break;
|
||||
case OP_FMT_var_ref:
|
||||
printf (" var_ref%d", get_u16 (tab + pos));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf ("\n");
|
||||
pos += size - 1; /* -1 because we already incremented pos after reading opcode */
|
||||
}
|
||||
}
|
||||
|
||||
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val) {
|
||||
JSFunctionBytecode *b = NULL;
|
||||
|
||||
if (!JS_IsPtr (func_val)) {
|
||||
printf ("JS_DumpFunctionBytecode: not a pointer value\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the object header to check type */
|
||||
void *ptr = JS_VALUE_GET_PTR (func_val);
|
||||
objhdr_t hdr = *(objhdr_t *)ptr;
|
||||
uint8_t type = objhdr_type (hdr);
|
||||
|
||||
if (type == OBJ_FUNCTION) {
|
||||
/* It's a JSFunction - extract bytecode */
|
||||
JSFunction *fn = (JSFunction *)ptr;
|
||||
if (fn->kind != JS_FUNC_KIND_BYTECODE) {
|
||||
printf ("JS_DumpFunctionBytecode: not a bytecode function (kind=%d)\n", fn->kind);
|
||||
return;
|
||||
}
|
||||
b = fn->u.func.function_bytecode;
|
||||
} else if (type == OBJ_CODE) {
|
||||
/* It's raw bytecode from js_create_function */
|
||||
b = (JSFunctionBytecode *)ptr;
|
||||
} else {
|
||||
printf ("JS_DumpFunctionBytecode: not a function or bytecode (type=%d)\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!b) {
|
||||
printf ("JS_DumpFunctionBytecode: no bytecode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[KEY_GET_STR_BUF_SIZE];
|
||||
|
||||
printf ("=== Bytecode Dump ===\n");
|
||||
|
||||
/* Function name */
|
||||
const char *fname = JS_KeyGetStr (ctx, buf, sizeof(buf), b->func_name);
|
||||
printf ("Function: %s\n", fname ? fname : "<anonymous>");
|
||||
|
||||
/* Debug info */
|
||||
if (b->has_debug && !JS_IsNull (b->debug.filename)) {
|
||||
printf ("File: %s\n", JS_KeyGetStr (ctx, buf, sizeof(buf), b->debug.filename));
|
||||
}
|
||||
|
||||
/* Basic stats */
|
||||
printf ("Args: %d, Vars: %d, Stack: %d\n", b->arg_count, b->var_count, b->stack_size);
|
||||
printf ("Bytecode length: %d bytes\n", b->byte_code_len);
|
||||
|
||||
/* Arguments */
|
||||
if (b->arg_count > 0 && b->vardefs) {
|
||||
printf ("\nArguments:\n");
|
||||
for (int i = 0; i < b->arg_count; i++) {
|
||||
printf (" %d: %s\n", i, JS_KeyGetStr (ctx, buf, sizeof(buf), b->vardefs[i].var_name));
|
||||
}
|
||||
}
|
||||
|
||||
/* Local variables */
|
||||
if (b->var_count > 0 && b->vardefs) {
|
||||
printf ("\nLocal variables:\n");
|
||||
for (int i = 0; i < b->var_count; i++) {
|
||||
JSVarDef *vd = &b->vardefs[b->arg_count + i];
|
||||
const char *kind = vd->is_const ? "const" : vd->is_lexical ? "let" : "var";
|
||||
printf (" %d: %s %s", i, kind, JS_KeyGetStr (ctx, buf, sizeof(buf), vd->var_name));
|
||||
if (vd->scope_level) printf (" [scope:%d]", vd->scope_level);
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Closure variables */
|
||||
if (b->closure_var_count > 0) {
|
||||
printf ("\nClosure variables:\n");
|
||||
for (int i = 0; i < b->closure_var_count; i++) {
|
||||
JSClosureVar *cv = &b->closure_var[i];
|
||||
printf (" %d: %s (%s:%s%d)\n", i,
|
||||
JS_KeyGetStr (ctx, buf, sizeof(buf), cv->var_name),
|
||||
cv->is_local ? "local" : "parent",
|
||||
cv->is_arg ? "arg" : "loc",
|
||||
cv->var_idx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Constant pool */
|
||||
if (b->cpool_count > 0) {
|
||||
printf ("\nConstant pool (%d entries):\n", b->cpool_count);
|
||||
for (uint32_t i = 0; i < b->cpool_count; i++) {
|
||||
printf (" [%u]: ", i);
|
||||
JS_PrintValue (ctx, js_dump_value_write, stdout, b->cpool[i], NULL);
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Bytecode instructions */
|
||||
printf ("\nBytecode:\n");
|
||||
dump_bytecode_opcodes (ctx, b);
|
||||
|
||||
printf ("=== End Bytecode Dump ===\n");
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* array function and sub-functions
|
||||
* ----------------------------------------------------------------------------
|
||||
|
||||
@@ -783,6 +783,9 @@ JSValue JS_ReadObject (JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
||||
reading a script or module with JS_ReadObject() */
|
||||
JSValue JS_EvalFunction (JSContext *ctx, JSValue fun_obj);
|
||||
|
||||
/* Dump bytecode of a compiled function (for debugging) */
|
||||
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
|
||||
|
||||
/* C function definition */
|
||||
typedef enum JSCFunctionEnum {
|
||||
JS_CFUNC_generic,
|
||||
|
||||
Reference in New Issue
Block a user