This commit is contained in:
2026-01-26 20:13:44 -06:00
parent 06f7791159
commit 378ad6dc98
2 changed files with 169 additions and 100 deletions

View File

@@ -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 "<native C>"
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, "<anonymous>");
else if (fn_name[0] == 0) {
dbg->name = js_strdup(js, "<anonymous>");
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, "<native C>");
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), "<anonymous>");
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, "<native C>", 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)

View File

@@ -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