new opcodes
This commit is contained in:
@@ -52,6 +52,7 @@ FMT(key)
|
||||
FMT(key_u8)
|
||||
FMT(key_u16)
|
||||
FMT(key_label_u16)
|
||||
FMT(u8_u16) /* 1 byte + 2 bytes for upvalue access */
|
||||
#undef FMT
|
||||
#endif /* FMT */
|
||||
|
||||
@@ -211,6 +212,15 @@ DEF( or, 1, 2, 1, none)
|
||||
/* template literal concatenation - pops N parts, pushes concatenated string */
|
||||
DEF(template_concat, 3, 0, 1, npop_u16)
|
||||
|
||||
/* Upvalue access (closures via outer_frame chain) */
|
||||
DEF( get_up, 4, 0, 1, u8_u16) /* depth:u8, slot:u16 -> value */
|
||||
DEF( set_up, 4, 1, 0, u8_u16) /* value, depth:u8, slot:u16 -> */
|
||||
|
||||
/* Name resolution with bytecode patching */
|
||||
DEF( get_name, 5, 0, 1, const) /* cpool_idx -> value, patches itself */
|
||||
DEF( get_env_slot, 3, 0, 1, u16) /* slot -> value (patched from get_name) */
|
||||
DEF(get_global_slot, 3, 0, 1, u16) /* slot -> value (patched from get_name) */
|
||||
|
||||
/* must be the last non short and non temporary opcode */
|
||||
DEF( nop, 1, 0, 0, none)
|
||||
|
||||
|
||||
156
source/quickjs.c
156
source/quickjs.c
@@ -448,6 +448,17 @@ struct VMFrame {
|
||||
int stack_size_allocated; /* total size allocated for this frame */
|
||||
};
|
||||
|
||||
/* GC-managed frame for closures - lives on GC heap with OBJ_FRAME type
|
||||
Used by the link-time relocation model where closures reference
|
||||
outer frames via (depth, slot) addressing */
|
||||
typedef struct JSFrame {
|
||||
objhdr_t header; /* OBJ_FRAME, cap56 = slot count */
|
||||
struct JSFunction *function;
|
||||
struct JSFrame *caller;
|
||||
uint32_t return_pc;
|
||||
JSValue slots[]; /* args, captured, locals, temps */
|
||||
} JSFrame;
|
||||
|
||||
/* Execution state returned by vm_execute_frame */
|
||||
typedef enum {
|
||||
VM_EXEC_NORMAL, /* Continue executing current frame */
|
||||
@@ -1604,7 +1615,7 @@ typedef enum {
|
||||
typedef struct JSFunction {
|
||||
objhdr_t header; /* must come first */
|
||||
JSValue name; /* function name as JSValue text */
|
||||
uint16_t length; /* arity: max allowed arguments */
|
||||
int16_t length; /* arity: max allowed arguments (-1 = variadic) */
|
||||
uint8_t kind;
|
||||
uint8_t free_mark : 1;
|
||||
union {
|
||||
@@ -1616,7 +1627,9 @@ typedef struct JSFunction {
|
||||
} cfunc;
|
||||
struct {
|
||||
struct JSFunctionBytecode *function_bytecode;
|
||||
JSVarRef **var_refs;
|
||||
JSVarRef **var_refs; /* legacy closure refs (to be removed) */
|
||||
struct JSFrame *outer_frame; /* lexical parent for closures */
|
||||
JSValue env_record; /* stone record, module environment */
|
||||
} func;
|
||||
struct JSBoundFunction *bound_function;
|
||||
} u;
|
||||
@@ -2361,6 +2374,11 @@ static size_t gc_object_size (void *ptr) {
|
||||
}
|
||||
case OBJ_FUNCTION:
|
||||
return gc_align_up (sizeof (JSFunction));
|
||||
case OBJ_FRAME: {
|
||||
/* JSFrame + slots array. cap56 stores slot count */
|
||||
uint64_t slot_count = cap;
|
||||
return gc_align_up (sizeof (JSFrame) + slot_count * sizeof (JSValue));
|
||||
}
|
||||
default:
|
||||
/* Unknown type - fatal error, heap is corrupt or scan desync'd */
|
||||
fflush(stdout);
|
||||
@@ -2482,6 +2500,14 @@ static void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8
|
||||
if (b->has_debug) {
|
||||
b->debug.filename = gc_copy_value (ctx, b->debug.filename, from_base, from_end, to_base, to_free, to_end);
|
||||
}
|
||||
/* Scan outer_frame (for closures) */
|
||||
if (fn->u.func.outer_frame) {
|
||||
JSValue frame_val = JS_MKPTR (fn->u.func.outer_frame);
|
||||
frame_val = gc_copy_value (ctx, frame_val, from_base, from_end, to_base, to_free, to_end);
|
||||
fn->u.func.outer_frame = (JSFrame *)JS_VALUE_GET_PTR (frame_val);
|
||||
}
|
||||
/* Scan env_record (stone record / module environment) */
|
||||
fn->u.func.env_record = gc_copy_value (ctx, fn->u.func.env_record, from_base, from_end, to_base, to_free, to_end);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2499,6 +2525,27 @@ static void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8
|
||||
/* Note: cpool, vardefs, closure_var, byte_code_buf are allocated via js_malloc */
|
||||
break;
|
||||
}
|
||||
case OBJ_FRAME: {
|
||||
/* JSFrame - scan function, caller, and slots */
|
||||
JSFrame *frame = (JSFrame *)ptr;
|
||||
/* function and caller are pointers - need to copy them as values */
|
||||
if (frame->function) {
|
||||
JSValue fn_val = JS_MKPTR (frame->function);
|
||||
fn_val = gc_copy_value (ctx, fn_val, from_base, from_end, to_base, to_free, to_end);
|
||||
frame->function = (JSFunction *)JS_VALUE_GET_PTR (fn_val);
|
||||
}
|
||||
if (frame->caller) {
|
||||
JSValue caller_val = JS_MKPTR (frame->caller);
|
||||
caller_val = gc_copy_value (ctx, caller_val, from_base, from_end, to_base, to_free, to_end);
|
||||
frame->caller = (JSFrame *)JS_VALUE_GET_PTR (caller_val);
|
||||
}
|
||||
/* Scan all slots */
|
||||
uint64_t slot_count = objhdr_cap56 (frame->header);
|
||||
for (uint64_t i = 0; i < slot_count; i++) {
|
||||
frame->slots[i] = gc_copy_value (ctx, frame->slots[i], from_base, from_end, to_base, to_free, to_end);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Unknown type during scan - fatal error */
|
||||
fprintf (stderr, "gc_scan_object: unknown object type %d at %p\n", type, ptr);
|
||||
@@ -3033,7 +3080,7 @@ void JS_SetMaxStackSize (JSRuntime *rt, size_t stack_size) {
|
||||
}
|
||||
|
||||
void JS_UpdateStackTop (JSRuntime *rt) {
|
||||
rt->stack_top = js_get_stack_pointer ();
|
||||
rt->stack_top = (const uint8_t *)js_get_stack_pointer ();
|
||||
update_stack_limit (rt);
|
||||
}
|
||||
|
||||
@@ -3246,7 +3293,7 @@ static no_inline JSText *pretext_realloc (JSContext *ctx, JSText *s, int new_len
|
||||
JSText *new_str = js_realloc (ctx, s, new_size_bytes);
|
||||
if (!new_str) return NULL;
|
||||
new_cap = min_int (new_cap, JS_STRING_LEN_MAX);
|
||||
objhdr_set_cap56 (&new_str->hdr, new_cap);
|
||||
new_str->hdr = objhdr_set_cap56 (new_str->hdr, new_cap);
|
||||
return new_str;
|
||||
}
|
||||
|
||||
@@ -3598,7 +3645,7 @@ static BOOL JS_ConcatStringInPlace (JSContext *ctx, JSText *p1, JSValue op2) {
|
||||
for (int64_t i = 0; i < len2; i++) {
|
||||
string_put (p1, len1 + i, string_get (p2, i));
|
||||
}
|
||||
objhdr_set_cap56 (&p1->hdr, new_len);
|
||||
p1->hdr = objhdr_set_cap56 (p1->hdr, new_len);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -3944,9 +3991,49 @@ static JSValue js_new_function (JSContext *ctx, JSFunctionKind kind) {
|
||||
func->kind = kind;
|
||||
func->name = JS_NULL;
|
||||
func->length = 0;
|
||||
/* Initialize closure fields for bytecode functions */
|
||||
if (kind == JS_FUNC_KIND_BYTECODE) {
|
||||
func->u.func.outer_frame = NULL;
|
||||
func->u.func.env_record = JS_NULL;
|
||||
}
|
||||
return JS_MKPTR (func);
|
||||
}
|
||||
|
||||
/* Allocate GC-managed frame for closure support */
|
||||
static JSFrame *js_new_frame (JSContext *ctx, JSFunction *func,
|
||||
JSFrame *caller, uint32_t ret_pc) {
|
||||
JSFunctionBytecode *b = func->u.func.function_bytecode;
|
||||
uint16_t slot_count = b->arg_count + b->var_count + b->stack_size;
|
||||
|
||||
size_t size = sizeof (JSFrame) + slot_count * sizeof (JSValue);
|
||||
JSFrame *f = js_mallocz (ctx, size);
|
||||
if (!f) return NULL;
|
||||
|
||||
f->header = objhdr_make (slot_count, OBJ_FRAME, false, false, false, false);
|
||||
f->function = func;
|
||||
f->caller = caller;
|
||||
f->return_pc = ret_pc;
|
||||
|
||||
/* Initialize all slots to JS_NULL */
|
||||
for (int i = 0; i < slot_count; i++)
|
||||
f->slots[i] = JS_NULL;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/* Get pointer to an upvalue in outer scope frame chain.
|
||||
depth=0 is current frame, depth=1 is immediate outer, etc.
|
||||
Returns NULL if depth exceeds the frame chain. */
|
||||
static inline JSValue *get_upvalue_ptr (JSFrame *frame, int depth, int slot) {
|
||||
while (depth > 0) {
|
||||
JSFunction *fn = frame->function;
|
||||
frame = fn->u.func.outer_frame;
|
||||
if (!frame) return NULL;
|
||||
depth--;
|
||||
}
|
||||
return &frame->slots[slot];
|
||||
}
|
||||
|
||||
/* Compute memory used by various object types */
|
||||
/* XXX: poor man's approach to handling multiply referenced objects */
|
||||
typedef struct JSMemoryUsage_helper {
|
||||
@@ -4222,15 +4309,15 @@ static void build_backtrace (JSContext *ctx, JSValue error_obj, const char *file
|
||||
dbuf_printf (&dbuf, " at %s", filename);
|
||||
if (line_num != -1) dbuf_printf (&dbuf, ":%d:%d", line_num, col_num);
|
||||
dbuf_putc (&dbuf, '\n');
|
||||
/* Use short immediate strings for keys to avoid GC issues */
|
||||
JSValue key_fileName = MIST_TryNewImmediateASCII ("file", 4);
|
||||
JSValue key_lineNumber = MIST_TryNewImmediateASCII ("line", 4);
|
||||
JSValue key_columnNumber = MIST_TryNewImmediateASCII ("col", 3);
|
||||
str = JS_NewString (ctx, filename);
|
||||
if (JS_IsException (str)) {
|
||||
JS_PopGCRef (ctx, &err_ref);
|
||||
return;
|
||||
}
|
||||
/* Note: SpiderMonkey does that, could update once there is a standard */
|
||||
JSValue key_fileName = JS_KEY_STR (ctx, "fileName");
|
||||
JSValue key_lineNumber = JS_KEY_STR (ctx, "lineNumber");
|
||||
JSValue key_columnNumber = JS_KEY_STR (ctx, "columnNumber");
|
||||
if (JS_SetPropertyInternal (ctx, err_ref.val, key_fileName, str) < 0
|
||||
|| JS_SetPropertyInternal (ctx, err_ref.val, key_lineNumber, JS_NewInt32 (ctx, line_num))
|
||||
< 0
|
||||
@@ -6242,10 +6329,10 @@ static __maybe_unused void JS_DumpObject (JSRuntime *rt, JSRecord *rec) {
|
||||
|
||||
static __maybe_unused void JS_DumpGCObject (JSRuntime *rt,
|
||||
objhdr_t *p) {
|
||||
if (objhdr_type (p) == OBJ_RECORD) {
|
||||
if (objhdr_type (*p) == OBJ_RECORD) {
|
||||
JS_DumpObject (rt, (JSRecord *)p);
|
||||
} else {
|
||||
switch (objhdr_type (p)) {
|
||||
switch (objhdr_type (*p)) {
|
||||
case OBJ_CODE:
|
||||
printf ("[function bytecode]");
|
||||
break;
|
||||
@@ -6256,7 +6343,7 @@ static __maybe_unused void JS_DumpGCObject (JSRuntime *rt,
|
||||
printf ("[record]");
|
||||
break;
|
||||
default:
|
||||
printf ("[unknown %d]", objhdr_type (p));
|
||||
printf ("[unknown %d]", objhdr_type (*p));
|
||||
break;
|
||||
}
|
||||
printf ("\n");
|
||||
@@ -8885,6 +8972,51 @@ restart:
|
||||
}
|
||||
BREAK;
|
||||
|
||||
/* Upvalue access via outer_frame chain (Phase 5) */
|
||||
CASE (OP_get_up) : {
|
||||
int depth = *pc++;
|
||||
int slot = get_u16 (pc); pc += 2;
|
||||
(void)depth; (void)slot;
|
||||
JS_ThrowInternalError (ctx, "OP_get_up not yet implemented");
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE (OP_set_up) : {
|
||||
int depth = *pc++;
|
||||
int slot = get_u16 (pc); pc += 2;
|
||||
(void)depth; (void)slot;
|
||||
--sp;
|
||||
JS_ThrowInternalError (ctx, "OP_set_up not yet implemented");
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
/* Name resolution with bytecode patching (Phase 6) */
|
||||
CASE (OP_get_name) : {
|
||||
uint32_t idx = get_u32 (pc); pc += 4;
|
||||
(void)idx;
|
||||
JS_ThrowInternalError (ctx, "OP_get_name not yet implemented");
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE (OP_get_env_slot) : {
|
||||
int slot = get_u16 (pc); pc += 2;
|
||||
(void)slot;
|
||||
JS_ThrowInternalError (ctx, "OP_get_env_slot not yet implemented");
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE (OP_get_global_slot) : {
|
||||
int slot = get_u16 (pc); pc += 2;
|
||||
(void)slot;
|
||||
JS_ThrowInternalError (ctx, "OP_get_global_slot not yet implemented");
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE (OP_nop) : BREAK;
|
||||
CASE (OP_is_null) : if (JS_VALUE_GET_TAG (sp[-1]) == JS_TAG_NULL) {
|
||||
goto set_true;
|
||||
|
||||
Reference in New Issue
Block a user