runs
This commit is contained in:
265
source/quickjs.c
265
source/quickjs.c
@@ -363,6 +363,8 @@ typedef struct {
|
|||||||
int call_argc;
|
int call_argc;
|
||||||
int call_has_this;
|
int call_has_this;
|
||||||
int is_tail_call;
|
int is_tail_call;
|
||||||
|
int is_constructor; /* 1 if this is a constructor call (new) */
|
||||||
|
int pre_argc; /* number of stack slots before argv (func, this/new_target) */
|
||||||
} VMCallInfo;
|
} VMCallInfo;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -13085,16 +13087,57 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
|||||||
JS_ThrowTypeError(caller_ctx, "not a function");
|
JS_ThrowTypeError(caller_ctx, "not a function");
|
||||||
goto exception_unwind;
|
goto exception_unwind;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret_val = call_func(caller_ctx, call_info.func_obj, call_info.this_obj,
|
int call_flags = 0;
|
||||||
call_info.argc, (JSValueConst *)call_info.argv, 0);
|
JSValueConst this_for_call = call_info.this_obj;
|
||||||
if (JS_IsException(ret_val))
|
|
||||||
|
if (call_info.is_constructor) {
|
||||||
|
/* Constructor call: use JS_CALL_FLAG_CONSTRUCTOR and pass new_target as this
|
||||||
|
(QuickJS convention for C constructors) */
|
||||||
|
call_flags = JS_CALL_FLAG_CONSTRUCTOR;
|
||||||
|
this_for_call = call_info.new_target;
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
printf("[C-CTOR] calling C constructor class_id=%d argc=%d new_target_tag=%d\n",
|
||||||
|
callee_p->class_id, call_info.argc, JS_VALUE_GET_TAG(call_info.new_target));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ret_val = call_func(caller_ctx, call_info.func_obj, this_for_call,
|
||||||
|
call_info.argc, (JSValueConst *)call_info.argv, call_flags);
|
||||||
|
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
if (call_info.is_constructor) {
|
||||||
|
printf("[C-CTOR] returned tag=%d exception=%d\n",
|
||||||
|
JS_VALUE_GET_TAG(ret_val), JS_IsException(ret_val));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (JS_IsException(ret_val)) {
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
printf("[EXCEPTION] C call failed: caller=%s is_ctor=%d callee_class=%d\n",
|
||||||
|
frame->b->func_name ? JS_AtomToCString(caller_ctx, frame->b->func_name) : "<anon>",
|
||||||
|
call_info.is_constructor, callee_p->class_id);
|
||||||
|
#endif
|
||||||
goto exception_unwind;
|
goto exception_unwind;
|
||||||
|
}
|
||||||
|
|
||||||
if (call_info.is_tail_call) {
|
if (call_info.is_tail_call) {
|
||||||
|
/* Tail-call to C function: call completed, now we must return
|
||||||
|
from the current frame (not resume it). A tail-call has no
|
||||||
|
continuation in the calling function. */
|
||||||
const uint8_t *ret_pc = frame->ret_pc;
|
const uint8_t *ret_pc = frame->ret_pc;
|
||||||
int ret_sp_offset = frame->ret_sp_offset;
|
int ret_sp_offset = frame->ret_sp_offset;
|
||||||
|
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
{
|
||||||
|
JSFunctionBytecode *caller_b = frame->b;
|
||||||
|
printf("[TAILCALL-C] C func returned, popping frame=%s "
|
||||||
|
"will_return_to ret_pc=%p ret_sp_offset=%d\n",
|
||||||
|
caller_b->func_name ? JS_AtomToCString(caller_ctx, caller_b->func_name) : "<anon>",
|
||||||
|
ret_pc, ret_sp_offset);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
vm_pop_frame(caller_ctx);
|
vm_pop_frame(caller_ctx);
|
||||||
|
|
||||||
if (caller_ctx->frame_stack_top < initial_frame_top) {
|
if (caller_ctx->frame_stack_top < initial_frame_top) {
|
||||||
@@ -13104,6 +13147,20 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
|||||||
|
|
||||||
frame = &caller_ctx->frame_stack[caller_ctx->frame_stack_top];
|
frame = &caller_ctx->frame_stack[caller_ctx->frame_stack_top];
|
||||||
|
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
{
|
||||||
|
JSFunctionBytecode *parent_b = frame->b;
|
||||||
|
const uint8_t *bc = parent_b->byte_code_buf;
|
||||||
|
int bc_len = parent_b->byte_code_len;
|
||||||
|
printf("[TAILCALL-C] resuming frame=%s ret_pc_offset=%td bc_len=%d\n",
|
||||||
|
parent_b->func_name ? JS_AtomToCString(caller_ctx, parent_b->func_name) : "<anon>",
|
||||||
|
ret_pc - bc, bc_len);
|
||||||
|
if (ret_pc < bc || ret_pc >= bc + bc_len) {
|
||||||
|
printf(" ERROR: ret_pc=%p not in [%p, %p)\n", ret_pc, bc, bc + bc_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
JSValue *stack_base = &caller_ctx->value_stack[frame->value_stack_base];
|
JSValue *stack_base = &caller_ctx->value_stack[frame->value_stack_base];
|
||||||
JSValue *current_sp = stack_base + frame->sp_offset;
|
JSValue *current_sp = stack_base + frame->sp_offset;
|
||||||
JSValue *target_sp = stack_base + ret_sp_offset;
|
JSValue *target_sp = stack_base + ret_sp_offset;
|
||||||
@@ -13113,11 +13170,32 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
|||||||
*current_sp++ = ret_val;
|
*current_sp++ = ret_val;
|
||||||
frame->sp_offset = current_sp - stack_base;
|
frame->sp_offset = current_sp - stack_base;
|
||||||
frame->pc = ret_pc;
|
frame->pc = ret_pc;
|
||||||
|
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
{
|
||||||
|
JSFunctionBytecode *fb = frame->b;
|
||||||
|
const uint8_t *bc = fb->byte_code_buf;
|
||||||
|
int bc_len = fb->byte_code_len;
|
||||||
|
if (frame->pc < bc || frame->pc >= bc + bc_len) {
|
||||||
|
printf("[TAILCALL-C] ERROR: frame->pc=%p not in [%p, %p) func=%s\n",
|
||||||
|
frame->pc, bc, bc + bc_len,
|
||||||
|
fb->func_name ? JS_AtomToCString(caller_ctx, fb->func_name) : "<anon>");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
uint8_t op = *frame->pc;
|
||||||
|
if (op == 0 || op == OP_invalid) {
|
||||||
|
printf("[TAILCALL-C] BAD resume opcode=0x%02x at offset=%td func=%s\n",
|
||||||
|
op, frame->pc - bc,
|
||||||
|
fb->func_name ? JS_AtomToCString(caller_ctx, fb->func_name) : "<anon>");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Manual stack cleanup for C call */
|
/* Manual stack cleanup for regular C call (not tail-call) */
|
||||||
frame = &caller_ctx->frame_stack[caller_ctx->frame_stack_top];
|
frame = &caller_ctx->frame_stack[caller_ctx->frame_stack_top];
|
||||||
JSValue *stack_base = &caller_ctx->value_stack[frame->value_stack_base];
|
JSValue *stack_base = &caller_ctx->value_stack[frame->value_stack_base];
|
||||||
JSValue *current_sp = stack_base + frame->sp_offset;
|
JSValue *current_sp = stack_base + frame->sp_offset;
|
||||||
@@ -13129,16 +13207,57 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
|||||||
*current_sp++ = ret_val;
|
*current_sp++ = ret_val;
|
||||||
frame->sp_offset = current_sp - stack_base;
|
frame->sp_offset = current_sp - stack_base;
|
||||||
frame->pc = call_info.ret_pc;
|
frame->pc = call_info.ret_pc;
|
||||||
|
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
{
|
||||||
|
JSFunctionBytecode *fb = frame->b;
|
||||||
|
const uint8_t *bc = fb->byte_code_buf;
|
||||||
|
int bc_len = fb->byte_code_len;
|
||||||
|
if (frame->pc < bc || frame->pc >= bc + bc_len) {
|
||||||
|
printf("[C-CALL] ERROR: frame->pc=%p not in [%p, %p) func=%s\n",
|
||||||
|
frame->pc, bc, bc + bc_len,
|
||||||
|
fb->func_name ? JS_AtomToCString(caller_ctx, fb->func_name) : "<anon>");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
uint8_t op = *frame->pc;
|
||||||
|
if (op == 0 || op == OP_invalid) {
|
||||||
|
printf("[C-CALL] BAD resume opcode=0x%02x at offset=%td func=%s\n",
|
||||||
|
op, frame->pc - bc,
|
||||||
|
fb->func_name ? JS_AtomToCString(caller_ctx, fb->func_name) : "<anon>");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (call_info.is_tail_call) {
|
if (call_info.is_tail_call) {
|
||||||
/* Tail call optimization */
|
/* Tail call optimization for bytecode callee.
|
||||||
|
Key insight: a tail-call has no continuation in the current function.
|
||||||
|
The callee should return directly to our caller, so we save our
|
||||||
|
frame's continuation BEFORE popping and use that for the new frame. */
|
||||||
JSValue *saved_argv = NULL;
|
JSValue *saved_argv = NULL;
|
||||||
JSValue func_saved = JS_DupValue(caller_ctx, call_info.func_obj);
|
JSValue func_saved = JS_DupValue(caller_ctx, call_info.func_obj);
|
||||||
JSValue this_saved = JS_DupValue(caller_ctx, call_info.this_obj);
|
JSValue this_saved = JS_DupValue(caller_ctx, call_info.this_obj);
|
||||||
JSValue new_target_saved = JS_DupValue(caller_ctx, call_info.new_target);
|
JSValue new_target_saved = JS_DupValue(caller_ctx, call_info.new_target);
|
||||||
|
|
||||||
|
/* Save current frame's continuation BEFORE popping - this is where
|
||||||
|
the tail-called function should return to (our caller). */
|
||||||
|
const uint8_t *saved_ret_pc = frame->ret_pc;
|
||||||
|
int saved_ret_sp_offset = frame->ret_sp_offset;
|
||||||
|
int saved_call_argc = frame->call_argc;
|
||||||
|
int saved_call_has_this = frame->call_has_this;
|
||||||
|
|
||||||
|
#ifdef DEBUG_VM
|
||||||
|
{
|
||||||
|
JSFunctionBytecode *caller_b = frame->b;
|
||||||
|
printf("[TAILCALL-BC] popping frame=%s will_return_to ret_pc=%p "
|
||||||
|
"ret_sp_offset=%d (owner=caller's caller)\n",
|
||||||
|
caller_b->func_name ? JS_AtomToCString(caller_ctx, caller_b->func_name) : "<anon>",
|
||||||
|
saved_ret_pc, saved_ret_sp_offset);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (call_info.argc > 0) {
|
if (call_info.argc > 0) {
|
||||||
saved_argv = js_malloc(caller_ctx, sizeof(JSValue) * call_info.argc);
|
saved_argv = js_malloc(caller_ctx, sizeof(JSValue) * call_info.argc);
|
||||||
if (!saved_argv) {
|
if (!saved_argv) {
|
||||||
@@ -13154,18 +13273,13 @@ static JSValue JS_CallTrampoline(JSContext *caller_ctx, JSValueConst func_obj,
|
|||||||
|
|
||||||
vm_pop_frame(caller_ctx);
|
vm_pop_frame(caller_ctx);
|
||||||
|
|
||||||
if (caller_ctx->frame_stack_top < initial_frame_top) {
|
/* Push new frame with the saved continuation from the popped frame.
|
||||||
frame = vm_push_frame(caller_ctx, func_saved, this_saved, new_target_saved,
|
This makes the tail-called function return directly to our caller. */
|
||||||
call_info.argc, saved_argv, JS_CALL_FLAG_COPY_ARGV,
|
frame = vm_push_frame(caller_ctx, func_saved, this_saved, new_target_saved,
|
||||||
NULL, 0, 0, 0);
|
call_info.argc, saved_argv, JS_CALL_FLAG_COPY_ARGV,
|
||||||
} else {
|
saved_ret_pc, saved_ret_sp_offset,
|
||||||
struct VMFrame *parent = &caller_ctx->frame_stack[caller_ctx->frame_stack_top];
|
saved_call_argc, saved_call_has_this);
|
||||||
frame = vm_push_frame(caller_ctx, func_saved, this_saved, new_target_saved,
|
|
||||||
call_info.argc, saved_argv, JS_CALL_FLAG_COPY_ARGV,
|
|
||||||
parent->ret_pc, parent->ret_sp_offset,
|
|
||||||
parent->call_argc, parent->call_has_this);
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_FreeValue(caller_ctx, func_saved);
|
JS_FreeValue(caller_ctx, func_saved);
|
||||||
JS_FreeValue(caller_ctx, this_saved);
|
JS_FreeValue(caller_ctx, this_saved);
|
||||||
JS_FreeValue(caller_ctx, new_target_saved);
|
JS_FreeValue(caller_ctx, new_target_saved);
|
||||||
@@ -13560,34 +13674,45 @@ CASE(OP_push_i32):
|
|||||||
call_info->call_argc = call_argc;
|
call_info->call_argc = call_argc;
|
||||||
call_info->call_has_this = 0;
|
call_info->call_has_this = 0;
|
||||||
call_info->is_tail_call = (opcode == OP_tail_call);
|
call_info->is_tail_call = (opcode == OP_tail_call);
|
||||||
|
call_info->is_constructor = 0; /* not a constructor call */
|
||||||
|
call_info->pre_argc = 1; /* 1 slot before argv: func */
|
||||||
|
|
||||||
/* Move refs back to heap frame */
|
/* Move refs back to heap frame */
|
||||||
if (!list_empty(&sf->var_ref_list)) {
|
if (!list_empty(&sf->var_ref_list)) {
|
||||||
list_splice(&frame->var_ref_list, &sf->var_ref_list); /* dst=frame, src=sf */
|
list_splice(&frame->var_ref_list, &sf->var_ref_list);
|
||||||
|
init_list_head(&sf->var_ref_list); /* reinit source after splice */
|
||||||
}
|
}
|
||||||
rt->current_stack_frame = sf->prev_frame;
|
rt->current_stack_frame = sf->prev_frame;
|
||||||
|
|
||||||
/* Debug: print call info */
|
#ifdef DEBUG_VM
|
||||||
|
/* Debug: print call info with tail-call awareness */
|
||||||
{
|
{
|
||||||
const char *caller_name = b->debug.filename ? b->debug.filename : "<unknown>";
|
|
||||||
if (b->debug.pc2line_len > 0) {
|
|
||||||
JSAtom func_name_atom = b->debug.filename ? b->func_name : JS_ATOM_NULL;
|
|
||||||
caller_name = JS_AtomGetStr(ctx, NULL, 0, func_name_atom);
|
|
||||||
}
|
|
||||||
int pc_offset = (int)(pc - b->byte_code_buf);
|
int pc_offset = (int)(pc - b->byte_code_buf);
|
||||||
int ret_pc_offset = call_info->ret_pc ? (int)(call_info->ret_pc - b->byte_code_buf) : -1;
|
int ret_pc_offset = call_info->ret_pc ? (int)(call_info->ret_pc - b->byte_code_buf) : -1;
|
||||||
|
int bc_len = b->byte_code_len;
|
||||||
printf("[VM_EXEC_CALL] caller=%s pc_offset=%d ret_pc_offset=%d bytes=[",
|
int at_end = (ret_pc_offset >= bc_len);
|
||||||
|
|
||||||
|
printf("[VM_EXEC_CALL] caller=%s bc_len=%d pc_off=%d ret_pc_off=%d",
|
||||||
b->func_name ? JS_AtomToCString(ctx, b->func_name) : "<anon>",
|
b->func_name ? JS_AtomToCString(ctx, b->func_name) : "<anon>",
|
||||||
pc_offset, ret_pc_offset);
|
bc_len, pc_offset, ret_pc_offset);
|
||||||
if (call_info->ret_pc) {
|
|
||||||
|
if (call_info->is_tail_call) {
|
||||||
|
printf(" TAIL_CALL=1");
|
||||||
|
if (at_end) {
|
||||||
|
printf(" (ret_pc at END - expected for tail call, will use frame->ret_pc instead)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" bytes=[");
|
||||||
|
if (call_info->ret_pc && !at_end) {
|
||||||
for (int di = 0; di < 8 && call_info->ret_pc + di < b->byte_code_buf + b->byte_code_len; di++)
|
for (int di = 0; di < 8 && call_info->ret_pc + di < b->byte_code_buf + b->byte_code_len; di++)
|
||||||
printf("%02x ", call_info->ret_pc[di]);
|
printf("%02x ", call_info->ret_pc[di]);
|
||||||
|
} else if (at_end) {
|
||||||
|
printf("END");
|
||||||
}
|
}
|
||||||
printf("] argc=%d has_this=%d is_tail=%d ",
|
printf("] argc=%d has_this=%d ",
|
||||||
call_info->call_argc, call_info->call_has_this,
|
call_info->call_argc, call_info->call_has_this);
|
||||||
call_info->is_tail_call);
|
|
||||||
|
|
||||||
if (JS_VALUE_GET_TAG(call_info->func_obj) == JS_TAG_OBJECT) {
|
if (JS_VALUE_GET_TAG(call_info->func_obj) == JS_TAG_OBJECT) {
|
||||||
JSObject *callee_p = JS_VALUE_GET_OBJ(call_info->func_obj);
|
JSObject *callee_p = JS_VALUE_GET_OBJ(call_info->func_obj);
|
||||||
if (callee_p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
if (callee_p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||||
@@ -13601,6 +13726,7 @@ CASE(OP_push_i32):
|
|||||||
printf("callee=non-object\n");
|
printf("callee=non-object\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return VM_EXEC_CALL;
|
return VM_EXEC_CALL;
|
||||||
}
|
}
|
||||||
@@ -13617,7 +13743,7 @@ CASE(OP_push_i32):
|
|||||||
frame->sp_offset = sp - stack_buf;
|
frame->sp_offset = sp - stack_buf;
|
||||||
|
|
||||||
call_info->func_obj = call_argv[-2];
|
call_info->func_obj = call_argv[-2];
|
||||||
call_info->this_obj = JS_NULL;
|
call_info->this_obj = JS_NULL; /* constructor creates its own this */
|
||||||
call_info->new_target = call_argv[-1];
|
call_info->new_target = call_argv[-1];
|
||||||
call_info->argc = call_argc;
|
call_info->argc = call_argc;
|
||||||
call_info->argv = call_argv;
|
call_info->argv = call_argv;
|
||||||
@@ -13625,31 +13751,38 @@ CASE(OP_push_i32):
|
|||||||
/* call_argv[-2] is func, call_argv[-1] is new.target */
|
/* call_argv[-2] is func, call_argv[-1] is new.target */
|
||||||
call_info->ret_sp_offset = (call_argv - 2) - stack_buf;
|
call_info->ret_sp_offset = (call_argv - 2) - stack_buf;
|
||||||
call_info->call_argc = call_argc;
|
call_info->call_argc = call_argc;
|
||||||
call_info->call_has_this = 1; /* consumes 2 slots before args */
|
call_info->call_has_this = 0; /* NOT a this-call, it's a constructor */
|
||||||
call_info->is_tail_call = 0;
|
call_info->is_tail_call = 0;
|
||||||
|
call_info->is_constructor = 1; /* this is a constructor call (new) */
|
||||||
|
call_info->pre_argc = 2; /* 2 slots before argv: func + new_target */
|
||||||
|
|
||||||
/* Move refs back to heap frame */
|
/* Move refs back to heap frame */
|
||||||
if (!list_empty(&sf->var_ref_list)) {
|
if (!list_empty(&sf->var_ref_list)) {
|
||||||
list_splice(&frame->var_ref_list, &sf->var_ref_list); /* dst=frame, src=sf */
|
list_splice(&frame->var_ref_list, &sf->var_ref_list);
|
||||||
|
init_list_head(&sf->var_ref_list); /* reinit source after splice */
|
||||||
}
|
}
|
||||||
rt->current_stack_frame = sf->prev_frame;
|
rt->current_stack_frame = sf->prev_frame;
|
||||||
|
|
||||||
/* Debug: print call info */
|
#ifdef DEBUG_VM
|
||||||
|
/* Debug: print call info for constructor */
|
||||||
{
|
{
|
||||||
int pc_offset = (int)(pc - b->byte_code_buf);
|
int pc_offset = (int)(pc - b->byte_code_buf);
|
||||||
int ret_pc_offset = call_info->ret_pc ? (int)(call_info->ret_pc - b->byte_code_buf) : -1;
|
int ret_pc_offset = call_info->ret_pc ? (int)(call_info->ret_pc - b->byte_code_buf) : -1;
|
||||||
|
int sp_off_before = frame->sp_offset;
|
||||||
printf("[VM_EXEC_CALL] caller=%s pc_offset=%d ret_pc_offset=%d bytes=[",
|
|
||||||
|
printf("[VM_EXEC_CALL] caller=%s bc_len=%d pc_off=%d ret_pc_off=%d "
|
||||||
|
"sp_off=%d ret_sp_off=%d IS_CTOR=1 new_target_tag=%d bytes=[",
|
||||||
b->func_name ? JS_AtomToCString(ctx, b->func_name) : "<anon>",
|
b->func_name ? JS_AtomToCString(ctx, b->func_name) : "<anon>",
|
||||||
pc_offset, ret_pc_offset);
|
b->byte_code_len, pc_offset, ret_pc_offset,
|
||||||
|
sp_off_before, call_info->ret_sp_offset,
|
||||||
|
JS_VALUE_GET_TAG(call_info->new_target));
|
||||||
if (call_info->ret_pc) {
|
if (call_info->ret_pc) {
|
||||||
for (int di = 0; di < 8 && call_info->ret_pc + di < b->byte_code_buf + b->byte_code_len; di++)
|
for (int di = 0; di < 8 && call_info->ret_pc + di < b->byte_code_buf + b->byte_code_len; di++)
|
||||||
printf("%02x ", call_info->ret_pc[di]);
|
printf("%02x ", call_info->ret_pc[di]);
|
||||||
}
|
}
|
||||||
printf("] argc=%d has_this=%d is_tail=%d ",
|
printf("] argc=%d pre_argc=%d ",
|
||||||
call_info->call_argc, call_info->call_has_this,
|
call_info->call_argc, call_info->pre_argc);
|
||||||
call_info->is_tail_call);
|
|
||||||
|
|
||||||
if (JS_VALUE_GET_TAG(call_info->func_obj) == JS_TAG_OBJECT) {
|
if (JS_VALUE_GET_TAG(call_info->func_obj) == JS_TAG_OBJECT) {
|
||||||
JSObject *callee_p = JS_VALUE_GET_OBJ(call_info->func_obj);
|
JSObject *callee_p = JS_VALUE_GET_OBJ(call_info->func_obj);
|
||||||
if (callee_p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
if (callee_p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||||
@@ -13663,6 +13796,7 @@ CASE(OP_push_i32):
|
|||||||
printf("callee=non-object\n");
|
printf("callee=non-object\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return VM_EXEC_CALL;
|
return VM_EXEC_CALL;
|
||||||
}
|
}
|
||||||
@@ -13690,29 +13824,45 @@ CASE(OP_push_i32):
|
|||||||
call_info->call_argc = call_argc;
|
call_info->call_argc = call_argc;
|
||||||
call_info->call_has_this = 1; /* consumes 2 slots before args */
|
call_info->call_has_this = 1; /* consumes 2 slots before args */
|
||||||
call_info->is_tail_call = (opcode == OP_tail_call_method);
|
call_info->is_tail_call = (opcode == OP_tail_call_method);
|
||||||
|
call_info->is_constructor = 0; /* not a constructor call */
|
||||||
|
call_info->pre_argc = 2; /* 2 slots before argv: this + func */
|
||||||
|
|
||||||
/* Move refs back to heap frame */
|
/* Move refs back to heap frame */
|
||||||
if (!list_empty(&sf->var_ref_list)) {
|
if (!list_empty(&sf->var_ref_list)) {
|
||||||
list_splice(&frame->var_ref_list, &sf->var_ref_list); /* dst=frame, src=sf */
|
list_splice(&frame->var_ref_list, &sf->var_ref_list);
|
||||||
|
init_list_head(&sf->var_ref_list); /* reinit source after splice */
|
||||||
}
|
}
|
||||||
rt->current_stack_frame = sf->prev_frame;
|
rt->current_stack_frame = sf->prev_frame;
|
||||||
|
|
||||||
/* Debug: print call info */
|
#ifdef DEBUG_VM
|
||||||
|
/* Debug: print call info with tail-call awareness */
|
||||||
{
|
{
|
||||||
int pc_offset = (int)(pc - b->byte_code_buf);
|
int pc_offset = (int)(pc - b->byte_code_buf);
|
||||||
int ret_pc_offset = call_info->ret_pc ? (int)(call_info->ret_pc - b->byte_code_buf) : -1;
|
int ret_pc_offset = call_info->ret_pc ? (int)(call_info->ret_pc - b->byte_code_buf) : -1;
|
||||||
|
int bc_len = b->byte_code_len;
|
||||||
printf("[VM_EXEC_CALL] caller=%s pc_offset=%d ret_pc_offset=%d bytes=[",
|
int at_end = (ret_pc_offset >= bc_len);
|
||||||
|
|
||||||
|
printf("[VM_EXEC_CALL] caller=%s bc_len=%d pc_off=%d ret_pc_off=%d",
|
||||||
b->func_name ? JS_AtomToCString(ctx, b->func_name) : "<anon>",
|
b->func_name ? JS_AtomToCString(ctx, b->func_name) : "<anon>",
|
||||||
pc_offset, ret_pc_offset);
|
bc_len, pc_offset, ret_pc_offset);
|
||||||
if (call_info->ret_pc) {
|
|
||||||
|
if (call_info->is_tail_call) {
|
||||||
|
printf(" TAIL_CALL=1");
|
||||||
|
if (at_end) {
|
||||||
|
printf(" (ret_pc at END - expected for tail call, will use frame->ret_pc instead)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" bytes=[");
|
||||||
|
if (call_info->ret_pc && !at_end) {
|
||||||
for (int di = 0; di < 8 && call_info->ret_pc + di < b->byte_code_buf + b->byte_code_len; di++)
|
for (int di = 0; di < 8 && call_info->ret_pc + di < b->byte_code_buf + b->byte_code_len; di++)
|
||||||
printf("%02x ", call_info->ret_pc[di]);
|
printf("%02x ", call_info->ret_pc[di]);
|
||||||
|
} else if (at_end) {
|
||||||
|
printf("END");
|
||||||
}
|
}
|
||||||
printf("] argc=%d has_this=%d is_tail=%d ",
|
printf("] argc=%d has_this=%d ",
|
||||||
call_info->call_argc, call_info->call_has_this,
|
call_info->call_argc, call_info->call_has_this);
|
||||||
call_info->is_tail_call);
|
|
||||||
|
|
||||||
if (JS_VALUE_GET_TAG(call_info->func_obj) == JS_TAG_OBJECT) {
|
if (JS_VALUE_GET_TAG(call_info->func_obj) == JS_TAG_OBJECT) {
|
||||||
JSObject *callee_p = JS_VALUE_GET_OBJ(call_info->func_obj);
|
JSObject *callee_p = JS_VALUE_GET_OBJ(call_info->func_obj);
|
||||||
if (callee_p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
if (callee_p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||||
@@ -13726,6 +13876,7 @@ CASE(OP_push_i32):
|
|||||||
printf("callee=non-object\n");
|
printf("callee=non-object\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return VM_EXEC_CALL;
|
return VM_EXEC_CALL;
|
||||||
}
|
}
|
||||||
|
|||||||
5
test.ce
5
test.ce
@@ -5,6 +5,11 @@ var time = use('time')
|
|||||||
var json = use('json')
|
var json = use('json')
|
||||||
var blob = use('blob')
|
var blob = use('blob')
|
||||||
|
|
||||||
|
var newblob = new blob(100)
|
||||||
|
for (var i in newblob) log.console(i)
|
||||||
|
newblob.write_bit(true)
|
||||||
|
log.console(newblob.length)
|
||||||
|
|
||||||
if (!args) args = []
|
if (!args) args = []
|
||||||
|
|
||||||
var target_pkg = null // null = current package
|
var target_pkg = null // null = current package
|
||||||
|
|||||||
Reference in New Issue
Block a user