diff --git a/source/quickjs.c b/source/quickjs.c index 143e2c93..ee68e0f5 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -378,6 +378,112 @@ struct JSGCObjectHeader { struct list_head link; }; +enum mist_obj_type { + OBJ_ARRAY = 0, + OBJ_BLOB = 1, + OBJ_TEXT = 2, + OBJ_RECORD = 3, + OBJ_FUNCTION= 4, + OBJ_CODE = 5, + OBJ_FRAME = 6, + OBJ_FORWARD = 7 +}; + +#define OBJHDR_CAP_SHIFT 8u +#define OBJHDR_CAP_MASK (((objhdr_t)1ull << 56) - 1ull) + +#define OBJHDR_S_BIT 3u +#define OBJHDR_P_BIT 4u +#define OBJHDR_A_BIT 5u +#define OBJHDR_R_BIT 7u + +#define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit)) +#define OBJHDR_S_MASK OBJHDR_FLAG(OBJHDR_S_BIT) +#define OBJHDR_P_MASK OBJHDR_FLAG(OBJHDR_P_BIT) +#define OBJHDR_A_MASK OBJHDR_FLAG(OBJHDR_A_BIT) +#define OBJHDR_R_MASK OBJHDR_FLAG(OBJHDR_R_BIT) + +typedef uint64_t word_t; // one actor-memory word +typedef uint64_t objhdr_t; // header word +typedef uint64_t objref_t; // 56-bit word address (0 = null) + +static inline uint8_t objhdr_type(objhdr_t h) { return (uint8_t)(h & 7u); } +static inline bool objhdr_s(objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; } +static inline uint64_t objhdr_cap56(objhdr_t h) { + return (uint64_t)((h >> OBJHDR_CAP_SHIFT) & OBJHDR_CAP_MASK); +} + +static inline objhdr_t objhdr_make(uint64_t cap56, uint8_t type, bool r, bool a, bool p, bool s) { + objhdr_t h = 0; + h |= ((objhdr_t)(cap56 & OBJHDR_CAP_MASK)) << OBJHDR_CAP_SHIFT; + h |= (objhdr_t)(type & 7u); + if (s) h |= OBJHDR_S_MASK; + if (p) h |= OBJHDR_P_MASK; + if (a) h |= OBJHDR_A_MASK; + if (r) h |= OBJHDR_R_MASK; + return h; +} + +/* Array: + [hdr(type=0, cap=element_capacity)] + [len] + [elem0..elem(cap-1)] +*/ +typedef struct mist_array { + objhdr_t hdr; + word_t len; + word_t elem[]; // length 'cap' in words +} mist_array; + +/* Blob: + [hdr(type=1, cap=bit_capacity)] + [len_bits] + [bitwords...], big-endian bit numbering per word (bit0 is MSB of first data word) +*/ +typedef struct mist_blob { + objhdr_t hdr; + word_t len_bits; + word_t bits[]; // count = (cap + 63)/64 +} mist_blob; + +/* Text (type=2): + Pretext (mutable): s=0 + [hdr(cap=char_capacity, s=0)] + [len_chars] + [packed UTF32 chars...] + Text (immutable): s=1 + [hdr(cap=length, s=1)] + [hash (0 => not computed yet)] + [packed UTF32 chars...] + Packed: 2 UTF32 per word, high 32 then low 32. +*/ +typedef struct mist_text { + objhdr_t hdr; + word_t len_or_hash; + word_t packed[]; // count = (cap + 1)/2 +} mist_text; + +/* Record (type=3): + Open-address table, slots 1..cap used, slot 0 reserved. + Capacity is (2^n - 1) mask; total slots = cap+1 (a power of two). + Layout words: + [hdr(cap=mask)] + [len_fields] + [key1][val1]...[key_cap][val_cap] + Total words = 2*(cap+1) (matches spec). +*/ +typedef struct mist_slot { + word_t key; // typically objref_t to a TEXT object (must be immutable for stable hash) + word_t value; // any value word +} mist_slot; + +typedef struct mist_record { + objhdr_t hdr; + word_t len_fields; + mist_slot slot[]; // slot[0] corresponds to field #1, ..., slot[cap-1] is field #cap +} mist_record; + + #ifdef RC_TRACE typedef struct RcEvent { JSGCObjectHeader *ptr; @@ -10210,10 +10316,9 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, func = f->u.cfunc.c_function; if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_CALL)) { - js_debug dbg = {0}; + js_debug dbg; js_debug_info(ctx, func_obj, &dbg); ctx->trace_hook(ctx, JS_HOOK_CALL, &dbg, ctx->trace_data); - free_js_debug_info(ctx, &dbg); } switch(cproto) { @@ -10269,12 +10374,8 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, rt->current_stack_frame = sf->prev_frame; - if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET)) { - js_debug dbg = {0}; - js_debug_info(ctx, func_obj, &dbg); - ctx->trace_hook(ctx, JS_HOOK_RET, &dbg, ctx->trace_data); - free_js_debug_info(ctx, &dbg); - } + if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET)) + ctx->trace_hook(ctx, JS_HOOK_RET, NULL, ctx->trace_data); return ret_val; } @@ -10464,10 +10565,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, #endif if (unlikely(caller_ctx->trace_hook) && (caller_ctx->trace_type & JS_HOOK_CALL)) { - js_debug dbg = {0}; + js_debug dbg; js_debug_info(caller_ctx, func_obj, &dbg); caller_ctx->trace_hook(caller_ctx, JS_HOOK_CALL, &dbg, caller_ctx->trace_data); - free_js_debug_info(caller_ctx, &dbg); } if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { @@ -12655,12 +12755,8 @@ CASE(OP_template_concat): } rt->current_stack_frame = sf->prev_frame; - if (unlikely(caller_ctx->trace_hook) && (caller_ctx->trace_type & JS_HOOK_RET)) { - js_debug dbg = {0}; - js_debug_info(caller_ctx, func_obj, &dbg); - caller_ctx->trace_hook(caller_ctx, JS_HOOK_RET, &dbg, caller_ctx->trace_data); - free_js_debug_info(caller_ctx, &dbg); - } + if (unlikely(caller_ctx->trace_hook) && (caller_ctx->trace_type & JS_HOOK_RET)) + caller_ctx->trace_hook(caller_ctx, JS_HOOK_RET, NULL, caller_ctx->trace_data); return ret_val; } @@ -29106,16 +29202,9 @@ static JSValue js_blob_wf(JSContext *ctx, JSValueConst this_val, JSValueConst ar if (!bd) return JS_ThrowTypeError(ctx, "wf: not called on a blob"); float f; - int tag = JS_VALUE_GET_TAG(arg0); - if (tag == JS_TAG_INT) { - f = (float)JS_VALUE_GET_INT(arg0); - } else if (tag == JS_TAG_FLOAT64) { - f = (float)JS_VALUE_GET_FLOAT64(arg0); - } else { - double d; - if (JS_ToFloat64(ctx, &d, arg0) < 0) return JS_EXCEPTION; - f = (float)d; - } + double d; + if (JS_ToFloat64(ctx, &d, arg0) < 0) return JS_EXCEPTION; + f = d; if (blob_write_bytes(bd, &f, sizeof(f)) < 0) return JS_ThrowTypeError(ctx, "wf: cannot write"); @@ -30246,76 +30335,58 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) #define STRLEN(s) (sizeof(s)/sizeof(s[0])) #define CSTR "" -void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg) +static inline void atom_to_buf(JSContext *ctx, JSAtom atom, char *dst, int cap, const char *fallback) { - memset(dbg, 0, sizeof(*dbg)); - - if (!JS_IsFunction(fn)) return; - JSFunction *f = JS_VALUE_GET_FUNCTION(fn); - - const char *fn_name = NULL; - if (f->name != JS_ATOM_NULL) { - fn_name = JS_AtomToCString(js, f->name); - } else if (f->kind == JS_FUNC_KIND_BYTECODE && f->u.func.function_bytecode) { - fn_name = JS_AtomToCString(js, f->u.func.function_bytecode->func_name); + if (atom == JS_ATOM_NULL) { + strncpy(dst, fallback, cap); + dst[cap - 1] = 0; + return; } - - if (!fn_name) - dbg->name = js_strdup(js, ""); - else if (fn_name[0] == 0) { - dbg->name = js_strdup(js, ""); - JS_FreeCString(js,fn_name); - } else { - dbg->name = js_strdup(js, fn_name); - JS_FreeCString(js,fn_name); - } - - dbg->unique = (int)(uintptr_t)f; - - switch(f->kind) { - case JS_FUNC_KIND_BYTECODE: { - JSFunctionBytecode *b = f->u.func.function_bytecode; - // get filename - const char *filename = JS_AtomToCString(js, b->debug.filename); - if (!filename) - dbg->filename = js_strdup(js, "unknown"); - else if (filename[0] == 0) { - dbg->filename = js_strdup(js, "unknown"); - JS_FreeCString(js,filename); - } else { - dbg->filename = js_strdup(js, filename); - JS_FreeCString(js,filename); - } - - dbg->what = "JS"; - dbg->closure_n = b->closure_var_count; - dbg->param_n = b->arg_count; - dbg->vararg = 1; - int pcol_num; - dbg->line = find_line_num(js, b, -1, &pcol_num); - dbg->source = b->debug.source; - dbg->srclen = b->debug.source_len; - break; - } - case JS_FUNC_KIND_C: - case JS_FUNC_KIND_C_DATA: - dbg->filename = js_strdup(js, ""); - dbg->what = "C"; - dbg->nparams = f->length; - dbg->vararg = 1; - dbg->line = 0; - dbg->source = CSTR; - dbg->srclen = STRLEN(CSTR); - break; - default: - break; + JS_AtomGetStr(ctx, dst, cap, atom); + if (dst[0] == 0) { + strncpy(dst, fallback, cap); + dst[cap - 1] = 0; } } -void free_js_debug_info(JSContext *js, js_debug *dbg) +void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg) { - js_free(js, dbg->filename); - js_free(js, dbg->name); + *dbg = (js_debug){0}; + + if (!JS_IsFunction(fn)) return; + + JSFunction *f = JS_VALUE_GET_FUNCTION(fn); + dbg->unique = (int)(uintptr_t)f; + + JSAtom name_atom = JS_ATOM_NULL; + if (f->name != JS_ATOM_NULL) name_atom = f->name; + else if (f->kind == JS_FUNC_KIND_BYTECODE && f->u.func.function_bytecode) name_atom = f->u.func.function_bytecode->func_name; + + atom_to_buf(js, name_atom, dbg->name, sizeof(dbg->name), ""); + + if (f->kind == JS_FUNC_KIND_BYTECODE) { + JSFunctionBytecode *b = f->u.func.function_bytecode; + atom_to_buf(js, b->debug.filename, dbg->filename, sizeof(dbg->filename), "unknown"); + dbg->what = "JS"; + dbg->closure_n = b->closure_var_count; + dbg->param_n = b->arg_count; + dbg->vararg = 1; + dbg->source = b->debug.source; + dbg->srclen = b->debug.source_len; + dbg->line = 0; /* see below */ + return; + } + + if (f->kind == JS_FUNC_KIND_C || f->kind == JS_FUNC_KIND_C_DATA) { + strncpy(dbg->filename, "", sizeof(dbg->filename)); + dbg->filename[sizeof(dbg->filename) - 1] = 0; + dbg->what = "C"; + dbg->param_n = f->length; + dbg->vararg = 1; + dbg->line = 0; + dbg->source = (const uint8_t *)CSTR; + dbg->srclen = STRLEN(CSTR); + } } void js_debug_sethook(JSContext *ctx, js_hook hook, int type, void *user) diff --git a/source/quickjs.h b/source/quickjs.h index 4cf56fa2..c80e1dac 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -912,21 +912,19 @@ void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_op JSValueConst val, const JSPrintValueOptions *options); typedef struct js_debug { - const char *name; // nameof function - const char *what; - const char *source; // source code of function - size_t srclen; - const char *filename; // name of file function is in - int nparams; - int vararg; - int line; // line the function is on + char name[64]; + char filename[96]; + int unique; + int line; int param_n; int closure_n; - uint32_t unique; // a unique identifier for this function + int vararg; + const char *what; + const uint8_t *source; + int srclen; } js_debug; void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg); -void free_js_debug_info(JSContext *js, js_debug *dbg); typedef void (*js_hook)(JSContext*, int type, js_debug *dbg, void *user); #define JS_HOOK_CALL 1