works
This commit is contained in:
2
Makefile
2
Makefile
@@ -56,7 +56,7 @@ static:
|
||||
# Bootstrap: build cell from scratch using meson (only needed once)
|
||||
# Also installs core scripts to ~/.cell/core
|
||||
bootstrap:
|
||||
meson setup build_bootstrap -Dbuildtype=debug
|
||||
meson setup build_bootstrap -Dbuildtype=debugoptimized
|
||||
meson compile -C build_bootstrap
|
||||
cp build_bootstrap/cell .
|
||||
cp build_bootstrap/libcell_runtime.dylib .
|
||||
|
||||
167
source/quickjs.c
167
source/quickjs.c
@@ -88,7 +88,7 @@
|
||||
64: dump compute_stack_size
|
||||
*/
|
||||
//#define DUMP_BYTECODE (1)
|
||||
#define DEBUG_VM
|
||||
//#define DEBUG_VM
|
||||
/* dump the occurence of the automatic GC */
|
||||
//#define DUMP_GC
|
||||
/* dump objects freed by the garbage collector */
|
||||
@@ -341,6 +341,13 @@ struct VMFrame {
|
||||
int arg_count;
|
||||
int js_mode;
|
||||
int stack_size_allocated; /* total size allocated for this frame */
|
||||
|
||||
/* Constructor support */
|
||||
int is_constructor; /* 1 if this is a constructor call */
|
||||
JSValue ctor_this_obj; /* the 'this' object created for non-derived constructor */
|
||||
|
||||
/* Exception handling */
|
||||
int pending_exception; /* 1 if a call returned exception, need to check catch */
|
||||
};
|
||||
|
||||
/* Execution state returned by vm_execute_frame */
|
||||
@@ -938,6 +945,8 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx,
|
||||
JSValueConst func_obj,
|
||||
JSValueConst new_target,
|
||||
int argc, JSValue *argv, int flags);
|
||||
static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
|
||||
int class_id);
|
||||
static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv);
|
||||
static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
|
||||
@@ -12901,6 +12910,13 @@ static struct VMFrame *vm_push_frame(JSContext *ctx,
|
||||
frame->call_argc = call_argc;
|
||||
frame->call_has_this = call_has_this;
|
||||
|
||||
/* Constructor support - initialized by caller if needed */
|
||||
frame->is_constructor = 0;
|
||||
frame->ctor_this_obj = JS_NULL;
|
||||
|
||||
/* Exception handling */
|
||||
frame->pending_exception = 0;
|
||||
|
||||
/* Bump value stack top */
|
||||
ctx->value_stack_top += total_slots;
|
||||
|
||||
@@ -12939,6 +12955,12 @@ static void vm_pop_frame(JSContext *ctx)
|
||||
JS_FreeValue(ctx, frame->this_obj);
|
||||
JS_FreeValue(ctx, frame->new_target);
|
||||
|
||||
/* Free constructor this object if it wasn't consumed */
|
||||
if (!JS_IsNull(frame->ctor_this_obj)) {
|
||||
JS_FreeValue(ctx, frame->ctor_this_obj);
|
||||
frame->ctor_this_obj = JS_NULL;
|
||||
}
|
||||
|
||||
/* Pop frame and value stack */
|
||||
ctx->value_stack_top -= frame->stack_size_allocated;
|
||||
ctx->frame_stack_top--;
|
||||
@@ -13014,24 +13036,50 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
|
||||
switch (state) {
|
||||
case VM_EXEC_RETURN: {
|
||||
|
||||
|
||||
/* 1. Capture continuation info before popping */
|
||||
/* 1. Capture continuation and constructor info before popping */
|
||||
const uint8_t *ret_pc = frame->ret_pc;
|
||||
int ret_sp_offset = frame->ret_sp_offset;
|
||||
int was_constructor = frame->is_constructor;
|
||||
JSValue ctor_this_obj = frame->ctor_this_obj;
|
||||
|
||||
/* 2. Pop the frame */
|
||||
/* Clear ctor_this_obj in frame so vm_pop_frame doesn't free it */
|
||||
frame->ctor_this_obj = JS_NULL;
|
||||
|
||||
/* 2. Handle constructor return value semantics */
|
||||
if (was_constructor) {
|
||||
/* JS constructor semantics:
|
||||
- If return value is an object, use it
|
||||
- Otherwise, use the constructed 'this' object */
|
||||
if (JS_VALUE_GET_TAG(ret_val) == JS_TAG_OBJECT) {
|
||||
/* Return value is object, free the constructed this */
|
||||
JS_FreeValue(caller_ctx, ctor_this_obj);
|
||||
#ifdef DEBUG_VM
|
||||
printf("[CTOR-RET] constructor returned object, using ret_val tag=%d\n",
|
||||
JS_VALUE_GET_TAG(ret_val));
|
||||
#endif
|
||||
} else {
|
||||
/* Return value is not an object, use constructed this */
|
||||
JS_FreeValue(caller_ctx, ret_val);
|
||||
ret_val = ctor_this_obj;
|
||||
#ifdef DEBUG_VM
|
||||
printf("[CTOR-RET] constructor returned non-object (tag=%d), using ctor_this_obj\n",
|
||||
JS_VALUE_GET_TAG(ret_val));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Pop the frame */
|
||||
vm_pop_frame(caller_ctx);
|
||||
|
||||
/* 3. Check if finished */
|
||||
/* 4. Check if finished */
|
||||
if (caller_ctx->frame_stack_top < initial_frame_top) {
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* 4. Resume caller */
|
||||
/* 5. Resume caller */
|
||||
frame = &caller_ctx->frame_stack[caller_ctx->frame_stack_top];
|
||||
|
||||
/* 5. Clean up caller stack (pop Func, This, Args) using the saved offset */
|
||||
|
||||
/* 6. Clean up caller stack (pop Func, This, Args) using the saved offset */
|
||||
JSValue *stack_base = &caller_ctx->value_stack[frame->value_stack_base];
|
||||
JSValue *current_sp = stack_base + frame->sp_offset;
|
||||
JSValue *target_sp = stack_base + ret_sp_offset;
|
||||
@@ -13040,32 +13088,36 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
JS_FreeValue(caller_ctx, *--current_sp);
|
||||
}
|
||||
|
||||
/* 6. Push return value */
|
||||
/* 7. Push return value */
|
||||
*current_sp++ = ret_val;
|
||||
|
||||
/* 7. Restore state */
|
||||
/* 8. Restore state */
|
||||
frame->sp_offset = current_sp - stack_base;
|
||||
|
||||
#ifdef DEBUG_VM
|
||||
const uint8_t *bc = frame->b->byte_code_buf;
|
||||
const uint8_t *end = bc + frame->b->byte_code_len;
|
||||
if (ret_pc < bc || ret_pc >= end) {
|
||||
printf("BAD ret_pc: caller=%s ret_pc=%p not in [%p,%p)\n",
|
||||
"dbg", ret_pc, bc, end);
|
||||
abort();
|
||||
#ifdef DEBUG_VM
|
||||
{
|
||||
const uint8_t *bc = frame->b->byte_code_buf;
|
||||
const uint8_t *end = bc + frame->b->byte_code_len;
|
||||
if (ret_pc < bc || ret_pc >= end) {
|
||||
printf("BAD ret_pc: caller=%s ret_pc=%p not in [%p,%p)\n",
|
||||
frame->b->func_name ? JS_AtomToCString(caller_ctx, frame->b->func_name) : "<anon>",
|
||||
ret_pc, bc, end);
|
||||
abort();
|
||||
}
|
||||
uint8_t op = *ret_pc;
|
||||
if (op == 0 || op == OP_invalid) {
|
||||
ptrdiff_t off = ret_pc - bc;
|
||||
printf("BAD resume opcode: caller=%s off=%td op=0x%02x\n",
|
||||
frame->b->func_name ? JS_AtomToCString(caller_ctx, frame->b->func_name) : "<anon>",
|
||||
off, op);
|
||||
/* dump a few bytes */
|
||||
for (int di = -8; di < 16; di++)
|
||||
printf("%02x ", ret_pc[di]);
|
||||
printf("\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
uint8_t op = *ret_pc;
|
||||
if (op == 0 || op == OP_invalid) {
|
||||
ptrdiff_t off = ret_pc - bc;
|
||||
printf("BAD resume opcode: caller=%s off=%td op=0x%02x\n",
|
||||
"dbg", off, op);
|
||||
/* dump a few bytes */
|
||||
for (int i = -8; i < 16; i++)
|
||||
printf("%02x ", ret_pc[i]);
|
||||
printf("\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
frame->pc = ret_pc;
|
||||
break;
|
||||
@@ -13286,20 +13338,59 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
for (int i = 0; i < call_info.argc; i++) JS_FreeValue(caller_ctx, saved_argv[i]);
|
||||
js_free(caller_ctx, saved_argv);
|
||||
} else {
|
||||
/* Regular call */
|
||||
// frame->ret_pc = call_info.ret_pc;
|
||||
// frame->ret_sp_offset = call_info.ret_sp_offset;
|
||||
/* Regular call (including constructor calls) */
|
||||
JSValue this_for_call = call_info.this_obj;
|
||||
JSValue ctor_this_obj = JS_NULL;
|
||||
|
||||
/* Handle bytecode constructor: create 'this' object */
|
||||
if (call_info.is_constructor) {
|
||||
JSObject *callee_obj = JS_VALUE_GET_OBJ(call_info.func_obj);
|
||||
JSFunctionBytecode *callee_b = callee_obj->u.func.function_bytecode;
|
||||
|
||||
if (callee_b->is_derived_class_constructor) {
|
||||
/* Derived class: this is null until super() is called */
|
||||
this_for_call = JS_NULL;
|
||||
#ifdef DEBUG_VM
|
||||
printf("[CTOR-BC] derived class constructor, this=NULL until super()\n");
|
||||
#endif
|
||||
} else {
|
||||
/* Non-derived: create 'this' object from prototype */
|
||||
ctor_this_obj = js_create_from_ctor(caller_ctx, call_info.new_target, JS_CLASS_OBJECT);
|
||||
if (JS_IsException(ctor_this_obj)) {
|
||||
#ifdef DEBUG_VM
|
||||
printf("[CTOR-BC] js_create_from_ctor FAILED\n");
|
||||
#endif
|
||||
goto exception_unwind;
|
||||
}
|
||||
this_for_call = ctor_this_obj;
|
||||
#ifdef DEBUG_VM
|
||||
printf("[CTOR-BC] created this object: tag=%d ptr=%p new_target_tag=%d\n",
|
||||
JS_VALUE_GET_TAG(ctor_this_obj),
|
||||
JS_VALUE_GET_PTR(ctor_this_obj),
|
||||
JS_VALUE_GET_TAG(call_info.new_target));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
frame = vm_push_frame(caller_ctx, call_info.func_obj,
|
||||
call_info.this_obj, call_info.new_target,
|
||||
this_for_call, call_info.new_target,
|
||||
call_info.argc, call_info.argv,
|
||||
JS_CALL_FLAG_COPY_ARGV,
|
||||
call_info.ret_pc, call_info.ret_sp_offset,
|
||||
call_info.call_argc, call_info.call_has_this);
|
||||
}
|
||||
|
||||
if (!frame) {
|
||||
JS_ThrowStackOverflow(caller_ctx);
|
||||
goto exception_unwind;
|
||||
if (!frame) {
|
||||
if (!JS_IsNull(ctor_this_obj))
|
||||
JS_FreeValue(caller_ctx, ctor_this_obj);
|
||||
JS_ThrowStackOverflow(caller_ctx);
|
||||
goto exception_unwind;
|
||||
}
|
||||
|
||||
/* Store constructor info in the frame for return handling */
|
||||
if (call_info.is_constructor) {
|
||||
frame->is_constructor = 1;
|
||||
frame->ctor_this_obj = ctor_this_obj; /* ownership transferred to frame */
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user