add function disassembler
This commit is contained in:
@@ -74,6 +74,14 @@ JSC_CCALL(js_compile_unblob,
|
||||
return JS_ReadObject(js, data, size, JS_READ_OBJ_BYTECODE);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_disassemble,
|
||||
return js_debugger_fn_bytecode(js, argv[0]);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_fn_info,
|
||||
return js_debugger_fn_info(js, argv[0]);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_js_funcs[] = {
|
||||
MIST_FUNC_DEF(os, calc_mem, 0),
|
||||
MIST_FUNC_DEF(os, mem_limit, 1),
|
||||
@@ -85,6 +93,8 @@ static const JSCFunctionListEntry js_js_funcs[] = {
|
||||
MIST_FUNC_DEF(js, eval_compile, 1),
|
||||
MIST_FUNC_DEF(js, compile_blob, 1),
|
||||
MIST_FUNC_DEF(js, compile_unblob, 1),
|
||||
MIST_FUNC_DEF(js, disassemble, 1),
|
||||
MIST_FUNC_DEF(js, fn_info, 1),
|
||||
};
|
||||
|
||||
JSValue js_js_use(JSContext *js) {
|
||||
|
||||
407
source/quickjs.c
407
source/quickjs.c
@@ -90,7 +90,7 @@
|
||||
32: dump line number table
|
||||
64: dump compute_stack_size
|
||||
*/
|
||||
//#define DUMP_BYTECODE (1)
|
||||
#define DUMP_BYTECODE (1)
|
||||
/* dump the occurence of the automatic GC */
|
||||
//#define DUMP_GC
|
||||
/* dump objects freed by the garbage collector */
|
||||
@@ -37002,14 +37002,413 @@ JSValue js_debugger_fn_info(JSContext *ctx, JSValue fn)
|
||||
goto done;
|
||||
|
||||
JSFunctionBytecode *b = f->u.func.function_bytecode;
|
||||
if (b->has_debug) {
|
||||
JS_SetPropertyStr(ctx, ret, "filename", JS_AtomToString(ctx, b->debug.filename));
|
||||
// JS_SetPropertyStr(ctx, ret, "line", JS_NewInt32(ctx,b->debug.line_num));
|
||||
char atom_buf[ATOM_GET_STR_BUF_SIZE];
|
||||
const char *str;
|
||||
int i;
|
||||
|
||||
// Function name
|
||||
if (b->func_name != JS_ATOM_NULL) {
|
||||
str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
|
||||
JS_SetPropertyStr(ctx, ret, "name", JS_NewString(ctx, str));
|
||||
}
|
||||
|
||||
// File location info
|
||||
if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
|
||||
int line_num, col_num;
|
||||
str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
|
||||
line_num = find_line_num(ctx, b, -1, &col_num);
|
||||
JS_SetPropertyStr(ctx, ret, "filename", JS_NewString(ctx, str));
|
||||
JS_SetPropertyStr(ctx, ret, "line", JS_NewInt32(ctx, line_num));
|
||||
JS_SetPropertyStr(ctx, ret, "column", JS_NewInt32(ctx, col_num));
|
||||
}
|
||||
|
||||
// Arguments
|
||||
if (b->arg_count && b->vardefs) {
|
||||
JSValue args_array = JS_NewArray(ctx);
|
||||
for (i = 0; i < b->arg_count; i++) {
|
||||
str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->vardefs[i].var_name);
|
||||
JS_SetPropertyUint32(ctx, args_array, i, JS_NewString(ctx, str));
|
||||
}
|
||||
JS_SetPropertyStr(ctx, ret, "args", args_array);
|
||||
}
|
||||
|
||||
// Local variables
|
||||
if (b->var_count && b->vardefs) {
|
||||
JSValue locals_array = JS_NewArray(ctx);
|
||||
for (i = 0; i < b->var_count; i++) {
|
||||
JSVarDef *vd = &b->vardefs[b->arg_count + i];
|
||||
JSValue local_obj = JS_NewObject(ctx);
|
||||
|
||||
str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name);
|
||||
JS_SetPropertyStr(ctx, local_obj, "name", JS_NewString(ctx, str));
|
||||
|
||||
const char *var_type =
|
||||
vd->var_kind == JS_VAR_CATCH ? "catch" :
|
||||
(vd->var_kind == JS_VAR_FUNCTION_DECL ||
|
||||
vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
|
||||
vd->is_const ? "const" :
|
||||
vd->is_lexical ? "let" : "var";
|
||||
JS_SetPropertyStr(ctx, local_obj, "type", JS_NewString(ctx, var_type));
|
||||
JS_SetPropertyStr(ctx, local_obj, "index", JS_NewInt32(ctx, i));
|
||||
|
||||
if (vd->scope_level) {
|
||||
JS_SetPropertyStr(ctx, local_obj, "scope_level", JS_NewInt32(ctx, vd->scope_level));
|
||||
JS_SetPropertyStr(ctx, local_obj, "scope_next", JS_NewInt32(ctx, vd->scope_next));
|
||||
}
|
||||
|
||||
JS_SetPropertyUint32(ctx, locals_array, i, local_obj);
|
||||
}
|
||||
JS_SetPropertyStr(ctx, ret, "locals", locals_array);
|
||||
}
|
||||
|
||||
// Closure variables
|
||||
if (b->closure_var_count) {
|
||||
JSValue closure_array = JS_NewArray(ctx);
|
||||
for (i = 0; i < b->closure_var_count; i++) {
|
||||
JSClosureVar *cv = &b->closure_var[i];
|
||||
JSValue closure_obj = JS_NewObject(ctx);
|
||||
|
||||
str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name);
|
||||
JS_SetPropertyStr(ctx, closure_obj, "name", JS_NewString(ctx, str));
|
||||
JS_SetPropertyStr(ctx, closure_obj, "is_local", JS_NewBool(ctx, cv->is_local));
|
||||
JS_SetPropertyStr(ctx, closure_obj, "is_arg", JS_NewBool(ctx, cv->is_arg));
|
||||
JS_SetPropertyStr(ctx, closure_obj, "var_idx", JS_NewInt32(ctx, cv->var_idx));
|
||||
|
||||
const char *var_type = cv->is_const ? "const" : cv->is_lexical ? "let" : "var";
|
||||
JS_SetPropertyStr(ctx, closure_obj, "type", JS_NewString(ctx, var_type));
|
||||
|
||||
JS_SetPropertyUint32(ctx, closure_array, i, closure_obj);
|
||||
}
|
||||
JS_SetPropertyStr(ctx, ret, "closure_vars", closure_array);
|
||||
}
|
||||
|
||||
// Stack size
|
||||
JS_SetPropertyStr(ctx, ret, "stack_size", JS_NewInt32(ctx, b->stack_size));
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Opcode names array for debugger
|
||||
static const char *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
|
||||
};
|
||||
|
||||
JSValue js_debugger_fn_bytecode(JSContext *ctx, JSValue fn)
|
||||
{
|
||||
JSObject *f = JS_VALUE_GET_OBJ(fn);
|
||||
if (!f || !js_class_has_bytecode(f->class_id))
|
||||
return JS_NULL;
|
||||
|
||||
JSFunctionBytecode *b = f->u.func.function_bytecode;
|
||||
JSValue ret = JS_NewArray(ctx);
|
||||
|
||||
const uint8_t *tab = b->byte_code_buf;
|
||||
int len = b->byte_code_len;
|
||||
int pos = 0;
|
||||
int idx = 0;
|
||||
BOOL use_short_opcodes = TRUE;
|
||||
char opcode_str[256];
|
||||
|
||||
while (pos < len) {
|
||||
int op = tab[pos];
|
||||
const JSOpCode *oi;
|
||||
|
||||
if (use_short_opcodes)
|
||||
oi = &short_opcode_info(op);
|
||||
else
|
||||
oi = &opcode_info[op];
|
||||
|
||||
int size = oi->size;
|
||||
if (pos + size > len) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (op >= sizeof(opcode_names)/sizeof(opcode_names[0])) {
|
||||
snprintf(opcode_str, sizeof(opcode_str), "unknown");
|
||||
} else {
|
||||
const char *opcode_name = opcode_names[op];
|
||||
snprintf(opcode_str, sizeof(opcode_str), "%s", opcode_name);
|
||||
|
||||
// Add arguments based on opcode format
|
||||
int arg_pos = pos + 1;
|
||||
switch(oi->fmt) {
|
||||
case OP_FMT_none_int:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", op - OP_push_0);
|
||||
break;
|
||||
case OP_FMT_npopx:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", op - OP_call0);
|
||||
break;
|
||||
case OP_FMT_u8:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_u8(tab + arg_pos));
|
||||
break;
|
||||
case OP_FMT_i8:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", get_i8(tab + arg_pos));
|
||||
break;
|
||||
case OP_FMT_u16:
|
||||
case OP_FMT_npop:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_u16(tab + arg_pos));
|
||||
break;
|
||||
case OP_FMT_npop_u16:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u %u", get_u16(tab + arg_pos), get_u16(tab + arg_pos + 2));
|
||||
break;
|
||||
case OP_FMT_i16:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", get_i16(tab + arg_pos));
|
||||
break;
|
||||
case OP_FMT_i32:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", get_i32(tab + arg_pos));
|
||||
break;
|
||||
case OP_FMT_u32:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_u32(tab + arg_pos));
|
||||
break;
|
||||
#if SHORT_OPCODES
|
||||
case OP_FMT_label8:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_i8(tab + arg_pos) + arg_pos);
|
||||
break;
|
||||
case OP_FMT_label16:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_i16(tab + arg_pos) + arg_pos);
|
||||
break;
|
||||
case OP_FMT_const8:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_u8(tab + arg_pos));
|
||||
break;
|
||||
#endif
|
||||
case OP_FMT_const:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_u32(tab + arg_pos));
|
||||
break;
|
||||
case OP_FMT_label:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", get_u32(tab + arg_pos) + arg_pos);
|
||||
break;
|
||||
case OP_FMT_label_u16:
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u %u", get_u32(tab + arg_pos) + arg_pos, get_u16(tab + arg_pos + 4));
|
||||
break;
|
||||
case OP_FMT_atom:
|
||||
{
|
||||
JSAtom atom = get_u32(tab + arg_pos);
|
||||
const char *atom_str = JS_AtomToCString(ctx, atom);
|
||||
if (atom_str) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %s", atom_str);
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", atom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_atom_u8:
|
||||
{
|
||||
JSAtom atom = get_u32(tab + arg_pos);
|
||||
const char *atom_str = JS_AtomToCString(ctx, atom);
|
||||
if (atom_str) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %s %d", atom_str, get_u8(tab + arg_pos + 4));
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u %d", atom, get_u8(tab + arg_pos + 4));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_atom_u16:
|
||||
{
|
||||
JSAtom atom = get_u32(tab + arg_pos);
|
||||
const char *atom_str = JS_AtomToCString(ctx, atom);
|
||||
if (atom_str) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %s %d", atom_str, get_u16(tab + arg_pos + 4));
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u %d", atom, get_u16(tab + arg_pos + 4));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_atom_label_u8:
|
||||
case OP_FMT_atom_label_u16:
|
||||
{
|
||||
JSAtom atom = get_u32(tab + arg_pos);
|
||||
int addr = get_u32(tab + arg_pos + 4);
|
||||
int extra = (oi->fmt == OP_FMT_atom_label_u8) ?
|
||||
get_u8(tab + arg_pos + 8) : get_u16(tab + arg_pos + 8);
|
||||
const char *atom_str = JS_AtomToCString(ctx, atom);
|
||||
if (atom_str) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %s %u %u", atom_str, addr + arg_pos + 4, extra);
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u %u %u", atom, addr + arg_pos + 4, extra);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_none_loc:
|
||||
{
|
||||
int idx = (op - OP_get_loc0) % 4;
|
||||
if (idx < b->var_count) {
|
||||
const char *var_name = JS_AtomToCString(ctx, b->vardefs[idx].var_name);
|
||||
if (var_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d: %s", idx, var_name);
|
||||
JS_FreeCString(ctx, var_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_loc8:
|
||||
{
|
||||
int idx = get_u8(tab + arg_pos);
|
||||
if (idx < b->var_count) {
|
||||
const char *var_name = JS_AtomToCString(ctx, b->vardefs[idx].var_name);
|
||||
if (var_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u: %s", idx, var_name);
|
||||
JS_FreeCString(ctx, var_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_loc:
|
||||
{
|
||||
int idx = get_u16(tab + arg_pos);
|
||||
if (idx < b->var_count) {
|
||||
const char *var_name = JS_AtomToCString(ctx, b->vardefs[idx].var_name);
|
||||
if (var_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u: %s", idx, var_name);
|
||||
JS_FreeCString(ctx, var_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_none_arg:
|
||||
{
|
||||
int idx = (op - OP_get_arg0) % 4;
|
||||
if (idx < b->arg_count) {
|
||||
const char *arg_name = JS_AtomToCString(ctx, b->vardefs[idx].var_name);
|
||||
if (arg_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d: %s", idx, arg_name);
|
||||
JS_FreeCString(ctx, arg_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_arg:
|
||||
{
|
||||
int idx = get_u16(tab + arg_pos);
|
||||
if (idx < b->arg_count) {
|
||||
const char *arg_name = JS_AtomToCString(ctx, b->vardefs[idx].var_name);
|
||||
if (arg_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u: %s", idx, arg_name);
|
||||
JS_FreeCString(ctx, arg_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_none_var_ref:
|
||||
{
|
||||
int idx = (op - OP_get_var_ref0) % 4;
|
||||
if (idx < b->closure_var_count) {
|
||||
const char *var_name = JS_AtomToCString(ctx, b->closure_var[idx].var_name);
|
||||
if (var_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d: %s", idx, var_name);
|
||||
JS_FreeCString(ctx, var_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %d", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_FMT_var_ref:
|
||||
{
|
||||
int idx = get_u16(tab + arg_pos);
|
||||
if (idx < b->closure_var_count) {
|
||||
const char *var_name = JS_AtomToCString(ctx, b->closure_var[idx].var_name);
|
||||
if (var_name) {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u: %s", idx, var_name);
|
||||
JS_FreeCString(ctx, var_name);
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
} else {
|
||||
snprintf(opcode_str + strlen(opcode_str), sizeof(opcode_str) - strlen(opcode_str),
|
||||
" %u", idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue js_opcode_str = JS_NewString(ctx, opcode_str);
|
||||
JS_SetPropertyUint32(ctx, ret, idx++, js_opcode_str);
|
||||
|
||||
pos += size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_debugger_local_variables(JSContext *ctx, int stack_index) {
|
||||
JSValue ret = JS_NewObject(ctx);
|
||||
|
||||
|
||||
@@ -1005,6 +1005,7 @@ JSValue js_debugger_closure_variables(JSContext *ctx, JSValue fn);
|
||||
JSValue js_debugger_local_variables(JSContext *ctx, int stack_index);
|
||||
JSValue js_debugger_build_backtrace(JSContext *ctx, const uint8_t *cur_pc);
|
||||
JSValue js_debugger_fn_info(JSContext *ctx, JSValue fn);
|
||||
JSValue js_debugger_fn_bytecode(JSContext *js, JSValue fn);
|
||||
|
||||
#undef js_unlikely
|
||||
#undef js_force_inline
|
||||
|
||||
Reference in New Issue
Block a user