diff --git a/src/duk_api_call.c b/src/duk_api_call.c index 38dfc0e8..a33fd54b 100644 --- a/src/duk_api_call.c +++ b/src/duk_api_call.c @@ -395,6 +395,7 @@ DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) { */ #ifdef DUK_USE_AUGMENT_ERROR_CREATE + duk_hthread_sync_currpc(thr); duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/); #endif diff --git a/src/duk_api_stack.c b/src/duk_api_stack.c index 263aaa30..fc5c2c62 100644 --- a/src/duk_api_stack.c +++ b/src/duk_api_stack.c @@ -4103,6 +4103,12 @@ DUK_EXTERNAL void duk_throw(duk_context *ctx) { * just before an error is thrown. */ + /* Sync needed before Duktape.errThrow augmentation, not strictly + * needed otherwise: bytecode longjmp handler syncs at the latest + * if we were executing bytecode. + */ + duk_hthread_sync_currpc(thr); + #if defined(DUK_USE_AUGMENT_ERROR_THROW) DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1))); duk_err_augment_error_throw(thr); diff --git a/src/duk_bi_duktape.c b/src/duk_bi_duktape.c index 540342d9..fe283c6a 100644 --- a/src/duk_bi_duktape.c +++ b/src/duk_bi_duktape.c @@ -145,14 +145,11 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) { duk_push_tval(ctx, &act->tv_func); - pc = (duk_uint_fast32_t) act->pc; - if (pc > 0) { - /* Relevant PC is just before current one because PC is - * post-incremented. This should match what error augment - * code does. - */ - pc--; - } + /* Relevant PC is just before current one because PC is + * post-incremented. This should match what error augment + * code does. + */ + pc = duk_hthread_get_act_prev_pc(thr, act); duk_push_uint(ctx, (duk_uint_t) pc); #if defined(DUK_USE_PC2LINE) diff --git a/src/duk_debugger.c b/src/duk_debugger.c index 7cf362ca..a65b6994 100644 --- a/src/duk_debugger.c +++ b/src/duk_debugger.c @@ -804,7 +804,7 @@ DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) { * has already been executed.) */ - pc = (duk_uint_fast32_t) act->pc; + pc = duk_hthread_get_act_curr_pc(thr, act); /* XXX: this should be optimized to be a raw query and avoid valstack * operations if possible. @@ -840,7 +840,7 @@ DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) { duk_pop_3(ctx); /* Report next pc/line to be executed. */ duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr)); - duk_debug_write_uint(thr, (duk_uint32_t) act->pc); + duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act)); } duk_debug_write_eom(thr); @@ -1165,7 +1165,7 @@ DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_safe_to_string(ctx, -1); duk_debug_write_hstring(thr, duk_get_hstring(ctx, -1)); - pc = curr_act->pc; + pc = duk_hthread_get_act_curr_pc(thr, curr_act); if (i != curr_thr->callstack_top - 1 && pc > 0) { pc--; } diff --git a/src/duk_error_augment.c b/src/duk_error_augment.c index 86d9791d..fb46a2f0 100644 --- a/src/duk_error_augment.c +++ b/src/duk_error_augment.c @@ -261,10 +261,7 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, * PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ - pc = thr_callstack->callstack[i].pc; - if (pc > 0) { - pc--; - } + pc = duk_hthread_get_act_prev_pc(thr_callstack, thr_callstack->callstack + i); DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ d = ((duk_double_t) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc; @@ -346,10 +343,7 @@ DUK_LOCAL void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr /* PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ - pc = act->pc; - if (pc > 0) { - pc--; - } + pc = duk_hthread_get_act_prev_pc(thr, act); DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ act = NULL; /* invalidated by pushes, so get out of the way */ diff --git a/src/duk_error_throw.c b/src/duk_error_throw.c index ca1834c7..13c98eb7 100644 --- a/src/duk_error_throw.c +++ b/src/duk_error_throw.c @@ -41,6 +41,8 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) thr->heap->handling_error = 1; + duk_hthread_sync_currpc(thr); /* ensure PC is consistent for traceback */ + /* * Create and push an error object onto the top of stack. * If a "double error" occurs, use a fixed error instance diff --git a/src/duk_hthread.h b/src/duk_hthread.h index 3313fefe..001af007 100644 --- a/src/duk_hthread.h +++ b/src/duk_hthread.h @@ -188,11 +188,11 @@ struct duk_activation { duk_hobject *prev_caller; #endif - duk_small_uint_t flags; - duk_uint32_t pc; /* next instruction to execute */ + duk_instr_t *curr_pc; /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */ #if defined(DUK_USE_DEBUGGER_SUPPORT) duk_uint32_t prev_line; /* needed for stepping */ #endif + duk_small_uint_t flags; /* idx_bottom and idx_retval are only used for book-keeping of * Ecmascript-initiated calls, to allow returning to an Ecmascript @@ -231,16 +231,23 @@ struct duk_activation { struct duk_catcher { duk_hstring *h_varname; /* borrowed reference to catch variable name (or NULL if none) */ /* (reference is valid as long activation exists) */ + duk_instr_t *pc_base; /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */ duk_size_t callstack_index; /* callstack index of related activation */ duk_size_t idx_base; /* idx_base and idx_base+1 get completion value and type */ - duk_uint32_t pc_base; /* resume execution from pc_base or pc_base+1 */ duk_uint32_t flags; /* type and control flags, label number */ }; struct duk_hthread { - /* shared object part */ + /* Shared object part */ duk_hobject obj; + /* Next opcode to execute. Conceptually this matches curr_pc of the + * topmost activation, but having it here is important for opcode + * dispatch performance. The downside is that whenever the activation + * changes this field and activation curr_pc must be carefully managed. + */ + duk_instr_t *curr_pc; + /* backpointers */ duk_heap *heap; @@ -333,4 +340,10 @@ DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud); DUK_INTERNAL_DECL void *duk_hthread_get_callstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ DUK_INTERNAL_DECL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ +#if defined(DUK_USE_DEBUGGER_SUPPORT) +DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act); +#endif +DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act); +DUK_INTERNAL_DECL void duk_hthread_sync_currpc(duk_hthread *thr); + #endif /* DUK_HTHREAD_H_INCLUDED */ diff --git a/src/duk_hthread_misc.c b/src/duk_hthread_misc.c index a3e1733b..aff6a404 100644 --- a/src/duk_hthread_misc.c +++ b/src/duk_hthread_misc.c @@ -42,3 +42,56 @@ DUK_INTERNAL duk_activation *duk_hthread_get_current_activation(duk_hthread *thr return NULL; } } + +#if defined(DUK_USE_DEBUGGER_SUPPORT) +DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act) { + duk_instr_t *bcode; + + DUK_ASSERT(thr != NULL); + DUK_ASSERT(act != NULL); + DUK_UNREF(thr); + + /* XXX: store 'bcode' pointer to activation for faster lookup? */ + if (act->func && DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func)) { + bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) (act->func)); + return (duk_uint_fast32_t) (act->curr_pc - bcode); + } + return 0; +} +#endif /* DUK_USE_DEBUGGER_SUPPORT */ + +DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act) { + duk_instr_t *bcode; + duk_uint_fast32_t ret; + + DUK_ASSERT(thr != NULL); + DUK_ASSERT(act != NULL); + DUK_UNREF(thr); + + if (act->func && DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func)) { + bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) (act->func)); + ret = (duk_uint_fast32_t) (act->curr_pc - bcode); + if (ret > 0) { + ret--; + } + return ret; + } + return 0; +} + +/* Write thr->curr_pc back to topmost activation (if any). */ +DUK_INTERNAL void duk_hthread_sync_currpc(duk_hthread *thr) { + duk_activation *act; + + DUK_ASSERT(thr != NULL); + + if (thr->callstack_top > 0) { + /* For native calls the assignment is OK as long as thr->curr_pc + * is NULL for the duration of a native call. + */ + act = thr->callstack + thr->callstack_top - 1; + DUK_ASSERT(((act->func == NULL || DUK_HOBJECT_HAS_COMPILEDFUNCTION(act->func)) && thr->curr_pc != NULL) || \ + ((act->func != NULL && DUK_HOBJECT_HAS_NATIVEFUNCTION(act->func)) && thr->curr_pc == NULL)); + act->curr_pc = thr->curr_pc; + } +} diff --git a/src/duk_js_bytecode.h b/src/duk_js_bytecode.h index 3d171d40..b4023f41 100644 --- a/src/duk_js_bytecode.h +++ b/src/duk_js_bytecode.h @@ -177,11 +177,6 @@ typedef duk_uint32_t duk_instr_t; #define DUK_EXTRAOP_LABEL 32 #define DUK_EXTRAOP_ENDLABEL 33 -/* DUK_OP_EXTRA for debugging */ -#define DUK_EXTRAOP_DUMPREG 128 -#define DUK_EXTRAOP_DUMPREGS 129 -#define DUK_EXTRAOP_LOGMARK 130 - /* DUK_OP_CALL flags in A */ #define DUK_BC_CALL_FLAG_TAILCALL (1 << 0) #define DUK_BC_CALL_FLAG_EVALCALL (1 << 1) diff --git a/src/duk_js_call.c b/src/duk_js_call.c index 99f727a9..6f55f1f7 100644 --- a/src/duk_js_call.c +++ b/src/duk_js_call.c @@ -832,6 +832,7 @@ duk_int_t duk_handle_call(duk_hthread *thr, duk_int_t entry_call_recursion_depth; duk_hthread *entry_curr_thread; duk_uint_fast8_t entry_thread_state; + duk_instr_t *entry_thr_curr_pc; volatile duk_bool_t need_setjmp; duk_jmpbuf * volatile old_jmpbuf_ptr = NULL; /* ptr is volatile (not the target) */ duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */ @@ -873,6 +874,10 @@ duk_int_t duk_handle_call(duk_hthread *thr, entry_call_recursion_depth = thr->heap->call_recursion_depth; entry_curr_thread = thr->heap->curr_thread; /* Note: may be NULL if first call */ entry_thread_state = thr->state; + entry_thr_curr_pc = NULL; /* not actually needed, avoid warning */ + if (entry_curr_thread) { + entry_thr_curr_pc = entry_curr_thread->curr_pc; + } idx_func = duk_normalize_index(ctx, -num_stack_args - 2); /* idx_func must be valid, note: non-throwing! */ idx_args = idx_func + 2; /* idx_args is not necessarily valid if num_stack_args == 0 (idx_args then equals top) */ @@ -966,6 +971,11 @@ duk_int_t duk_handle_call(duk_hthread *thr, DUK_ASSERT(thr->callstack_top >= entry_callstack_top); DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); + /* We don't need to sync back thr->curr_pc here because the + * bytecode executor always has a setjmp catchpoint which + * does that before errors propagate to here. + */ + /* * Restore previous setjmp catchpoint */ @@ -1217,7 +1227,7 @@ duk_int_t duk_handle_call(duk_hthread *thr, #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY act->prev_caller = NULL; #endif - act->pc = 0; + act->curr_pc = NULL; #if defined(DUK_USE_DEBUGGER_SUPPORT) act->prev_line = 0; #endif @@ -1360,6 +1370,8 @@ duk_int_t duk_handle_call(duk_hthread *thr, * other invalid */ + thr->curr_pc = NULL; + if (func) { rc = ((duk_hnativefunction *) func)->func((duk_context *) thr); } else { @@ -1446,6 +1458,10 @@ duk_int_t duk_handle_call(duk_hthread *thr, * Shift to new valstack_bottom. */ + DUK_ASSERT(func != NULL); + DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func)); + act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func); + thr->valstack_bottom = thr->valstack_bottom + idx_args; /* keep current valstack_top */ DUK_ASSERT(thr->valstack_bottom >= thr->valstack); @@ -1566,6 +1582,13 @@ duk_int_t duk_handle_call(duk_hthread *thr, DUK_DDD(DUK_DDDPRINT("setjmp catchpoint torn down")); } + /* Restore entry thread curr_pc (could just reset from topmost + * activation too). + */ + if (entry_curr_thread) { + entry_curr_thread->curr_pc = entry_thr_curr_pc; + } + DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ thr->state = (duk_uint8_t) entry_thread_state; @@ -2020,6 +2043,9 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); + /* store current pc if there's a running ecmascript function */ + duk_hthread_sync_currpc(thr); + /* if a tail call: * - an Ecmascript activation must be on top of the callstack * - there cannot be any active catchstack entries @@ -2203,7 +2229,10 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY act->prev_caller = NULL; #endif - act->pc = 0; /* don't want an intermediate exposed state with invalid pc */ + DUK_ASSERT(func != NULL); + DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func)); + /* don't want an intermediate exposed state with invalid pc */ + act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func); #if defined(DUK_USE_DEBUGGER_SUPPORT) act->prev_line = 0; #endif @@ -2232,7 +2261,6 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func); /* already updated */ DUK_ASSERT(act->var_env == NULL); /* already NULLed (by unwind) */ DUK_ASSERT(act->lex_env == NULL); /* already NULLed (by unwind) */ - DUK_ASSERT(act->pc == 0); /* already zeroed */ act->idx_bottom = entry_valstack_bottom_index; /* tail call -> reuse current "frame" */ DUK_ASSERT(nregs >= 0); #if 0 /* topmost activation idx_retval is considered garbage, no need to init */ @@ -2307,7 +2335,9 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY act->prev_caller = NULL; #endif - act->pc = 0; + DUK_ASSERT(func != NULL); + DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func)); + act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func); #if defined(DUK_USE_DEBUGGER_SUPPORT) act->prev_line = 0; #endif diff --git a/src/duk_js_executor.c b/src/duk_js_executor.c index cb89c2e1..9fa39bf1 100644 --- a/src/duk_js_executor.c +++ b/src/duk_js_executor.c @@ -690,7 +690,7 @@ DUK_LOCAL void duk__handle_catch_or_finally(duk_hthread *thr, duk_size_t cat_idx * Reset PC: resume execution from catch or finally jump slot. */ - (thr->callstack + thr->callstack_top - 1)->pc = + (thr->callstack + thr->callstack_top - 1)->curr_pc = thr->catchstack[cat_idx].pc_base + (is_finally ? 1 : 0); /* @@ -784,7 +784,7 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx) { DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(act))); /* +0 = break, +1 = continue */ - act->pc = thr->catchstack[cat_idx].pc_base + (thr->heap->lj.type == DUK_LJ_TYPE_CONTINUE ? 1 : 0); + act->curr_pc = thr->catchstack[cat_idx].pc_base + (thr->heap->lj.type == DUK_LJ_TYPE_CONTINUE ? 1 : 0); act = NULL; /* invalidated */ duk_hthread_catchstack_unwind(thr, cat_idx + 1); /* keep label catcher */ @@ -1534,9 +1534,6 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ line = duk_debug_curr_line(thr); if (act->prev_line != line) { - DUK_DDD(DUK_DDDPRINT("PC=%ld line=%ld; line transition: %ld -> %ld", - (long) act->pc, (long) line, (long) act->prev_line, (long) line)); - /* Stepped? Step out is handled by callstack unwind. */ if ((thr->heap->dbg_step_type == DUK_STEP_TYPE_INTO || thr->heap->dbg_step_type == DUK_STEP_TYPE_OVER) && @@ -1576,7 +1573,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ } } } else { - DUK_DDD(DUK_DDDPRINT("PC=%ld line=%ld", (long) act->pc, (long) line)); + ; } act->prev_line = line; @@ -1770,9 +1767,6 @@ DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) { ctr = 1; } - DUK_DDD(DUK_DDDPRINT("executor interrupt finished, cstop=%ld, pc=%ld, nextctr=%ld", - (long) thr->callstack_top, (long) act->pc, (long) ctr)); - /* The counter value is one less than the init value: init value should * indicate how many instructions are executed before interrupt. To * execute 1 instruction (after interrupt handler return), counter must @@ -1978,9 +1972,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* "hot" variables for interpretation -- not volatile, value not guaranteed in setjmp error handling */ duk_hthread *thr; /* stable */ - duk_activation *act; /* semi-stable (ok as long as callstack not resized) */ duk_hcompiledfunction *fun; /* stable */ - duk_instr_t *bcode; /* stable */ /* 'consts' is computed on-the-fly */ /* 'funcs' is quite rarely used, so no local for it */ @@ -2058,6 +2050,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { DUK_ASSERT(entry_thread != NULL); thr = entry_thread->heap->curr_thread; + DUK_ASSERT(thr->callstack_top > 0); /* we were executing bytecode */ + duk_hthread_sync_currpc(thr); + /* XXX: signalling the need to shrink check (only if unwound) */ /* Must be restored here to handle e.g. yields properly. */ @@ -2098,7 +2093,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { * Return from bytecode executor with a return value. */ DUK_ASSERT(lj_ret == DUK__LONGJMP_FINISHED); - /* XXX: return assertions for valstack, callstack, catchstack */ DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); @@ -2116,6 +2110,10 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { * The number of local variables should be kept to a minimum: if * the variables are spilled, they will need to be loaded from * memory anyway. + * + * Any 'goto restart_execution;' code path in opcode dispatch must + * ensure thr->curr_pc is synced back to act->curr_pc before the + * goto takes place. */ restart_execution: @@ -2133,15 +2131,21 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* Assume interrupt init/counter are properly initialized here. */ /* assume that thr->valstack_bottom has been set-up before getting here */ - act = thr->callstack + thr->callstack_top - 1; - fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act); - DUK_ASSERT(fun != NULL); - DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs); - bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun); + { + duk_activation *act; + + act = thr->callstack + thr->callstack_top - 1; + fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act); + DUK_ASSERT(fun != NULL); + DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs); + } #if defined(DUK_USE_DEBUGGER_SUPPORT) if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap) && !thr->heap->dbg_processing) { + duk_activation *act; + thr->heap->dbg_processing = 1; + act = thr->callstack + thr->callstack_top - 1; duk__executor_handle_debugger(thr, act, fun); thr->heap->dbg_processing = 0; } @@ -2178,18 +2182,14 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { */ DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); DUK_ASSERT(fun != NULL); - DUK_ASSERT(bcode != NULL); - DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act %p (idx %ld), fun %p, bcode %p, " + DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act idx %ld, fun %p," "consts %p, funcs %p, lev %ld, regbot %ld, regtop %ld, catchstack_top=%ld, " "preventcount=%ld", (void *) thr, - (void *) act, (long) (thr->callstack_top - 1), (void *) fun, - (void *) bcode, (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, fun), (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, fun), (long) (thr->callstack_top - 1), @@ -2202,6 +2202,13 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { valstack_top_base = (duk_size_t) (thr->valstack_top - thr->valstack); #endif + /* Set up thr->curr_pc for opcode dispatch. */ + { + duk_activation *act; + act = thr->callstack + thr->callstack_top - 1; + thr->curr_pc = act->curr_pc; + } + for (;;) { DUK_ASSERT(thr->callstack_top >= 1); DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs); @@ -2221,8 +2228,17 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* Trigger at zero or below */ duk_small_uint_t exec_int_ret; + /* Write curr_pc back for the debugger. */ + DUK_ASSERT(thr->callstack_top > 0); + { + duk_activation *act; + act = thr->callstack + thr->callstack_top - 1; + act->curr_pc = thr->curr_pc; + } + exec_int_ret = duk__executor_interrupt(thr); if (exec_int_ret == DUK__INT_RESTART) { + /* thr->curr_pc synced back above */ goto restart_execution; } } @@ -2231,31 +2247,24 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { thr->heap->inst_count_exec++; #endif - /* Because ANY DECREF potentially invalidates 'act' now (through - * finalization), we need to re-lookup 'act' in almost every case. - * - * XXX: future work for performance optimization: - * This is not nice; it would be nice if the program counter was a - * behind a stable pointer. For instance, put a raw bytecode pointer - * into duk_hthread struct (not into the callstack); since bytecode - * has a stable pointer this would work nicely. Whenever a call is - * made, the bytecode pointer could be backed up as an integer index - * to the calling activation. Perhaps add a macro for setting up a - * new activation (same as for setting up / switching threads)? - */ - - act = thr->callstack + thr->callstack_top - 1; - DUK_ASSERT(bcode + act->pc >= DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)); - DUK_ASSERT(bcode + act->pc < DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, fun)); - - DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld --> %!I", - (long) act->pc, - (unsigned long) bcode[act->pc], - (long) DUK_DEC_OP(bcode[act->pc]), - (long) (thr->valstack_top - thr->valstack), - (long) (thr->valstack_end - thr->valstack), - (long) (fun ? fun->nregs : -1), - (duk_instr_t) bcode[act->pc])); +#if defined(DUK_USE_ASSERTIONS) || defined(DUK_USE_DEBUG) + { + duk_activation *act; + act = thr->callstack + thr->callstack_top - 1; + DUK_ASSERT(thr->curr_pc >= DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)); + DUK_ASSERT(thr->curr_pc < DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, fun)); + DUK_UNREF(act); /* if debugging disabled */ + + DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld, ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld --> %!I", + (long) (thr->curr_pc - DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)), + (unsigned long) *thr->curr_pc, + (long) DUK_DEC_OP(*thr->curr_pc), + (long) (thr->valstack_top - thr->valstack), + (long) (thr->valstack_end - thr->valstack), + (long) (fun ? fun->nregs : -1), + (duk_instr_t) *thr->curr_pc)); + } +#endif #if defined(DUK_USE_ASSERTIONS) /* Quite heavy assert: check that valstack is in correctly @@ -2273,7 +2282,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { } #endif - ins = bcode[act->pc++]; + ins = *thr->curr_pc++; /* Typing: use duk_small_(u)int_fast_t when decoding small * opcode fields (op, A, B, C) and duk_(u)int_fast_t when @@ -2532,11 +2541,15 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { * C -> num args (N) */ - /* Note: duk_new() will call the constuctor using duk_handle_call(). + /* duk_new() will call the constuctor using duk_handle_call(). * A constructor call prevents a yield from inside the constructor, * even if the constructor is an Ecmascript function. */ + /* Don't need to sync thr->curr_pc here; duk_new() will do that + * when it augments the created error. + */ + /* XXX: unnecessary copying of values? Just set 'top' to * b + c, and let the return handling fix up the stack frame? */ @@ -2641,6 +2654,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { case DUK_OP_GETVAR: { duk_context *ctx = (duk_context *) thr; + duk_activation *act; duk_small_uint_fast_t a = DUK_DEC_A(ins); duk_uint_fast_t bc = DUK_DEC_BC(ins); duk_tval *tv1; @@ -2651,6 +2665,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); DUK_DDD(DUK_DDDPRINT("GETVAR: '%!O'", (duk_heaphdr *) name)); + act = thr->callstack + thr->callstack_top - 1; (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ duk_pop(ctx); /* 'this' binding is not needed here */ @@ -2659,6 +2674,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { } case DUK_OP_PUTVAR: { + duk_activation *act; duk_small_uint_fast_t a = DUK_DEC_A(ins); duk_uint_fast_t bc = DUK_DEC_BC(ins); duk_tval *tv1; @@ -2674,11 +2690,13 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { */ tv1 = DUK__REGP(a); /* val */ + act = thr->callstack + thr->callstack_top - 1; duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); break; } case DUK_OP_DECLVAR: { + duk_activation *act; duk_context *ctx = (duk_context *) thr; duk_small_uint_fast_t a = DUK_DEC_A(ins); duk_small_uint_fast_t b = DUK_DEC_B(ins); @@ -2713,6 +2731,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { } tv1 = duk_get_tval(ctx, -1); + act = thr->callstack + thr->callstack_top - 1; if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) { /* already declared, must update binding value */ tv1 = duk_get_tval(ctx, -1); @@ -2724,6 +2743,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { } case DUK_OP_DELVAR: { + duk_activation *act; duk_context *ctx = (duk_context *) thr; duk_small_uint_fast_t a = DUK_DEC_A(ins); duk_small_uint_fast_t b = DUK_DEC_B(ins); @@ -2736,6 +2756,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); DUK_DDD(DUK_DDDPRINT("DELVAR '%!O'", (duk_heaphdr *) name)); + act = thr->callstack + thr->callstack_top - 1; rc = duk_js_delvar_activation(thr, act, name); duk_push_boolean(ctx, rc); @@ -2755,6 +2776,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { */ duk_context *ctx = (duk_context *) thr; + duk_activation *act; duk_small_uint_fast_t b = DUK_DEC_B(ins); duk_uint_fast_t idx; duk_tval *tv1; @@ -2764,6 +2786,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); + act = thr->callstack + thr->callstack_top - 1; (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ /* Note: target registers a and a+1 may overlap with DUK__REGCONSTP(b) @@ -2792,6 +2815,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { case DUK_OP_CLOSURE: { duk_context *ctx = (duk_context *) thr; + duk_activation *act; duk_small_uint_fast_t a = DUK_DEC_A(ins); duk_uint_fast_t bc = DUK_DEC_BC(ins); duk_hobject *fun_temp; @@ -2812,6 +2836,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { DUK_DDD(DUK_DDDPRINT("CLOSURE: function template is: %p -> %!O", (void *) fun_temp, (duk_heaphdr *) fun_temp)); + act = thr->callstack + thr->callstack_top - 1; if (act->lex_env == NULL) { DUK_ASSERT(act->var_env == NULL); duk_js_init_activation_environment_records_delayed(thr, act); @@ -3139,7 +3164,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { tmp = duk_js_toboolean(DUK__REGCONSTP(b)); if (tmp == (duk_bool_t) a) { /* if boolean matches A, skip next inst */ - act->pc++; + thr->curr_pc++; } else { ; } @@ -3149,7 +3174,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { case DUK_OP_JUMP: { duk_int_fast_t abc = DUK_DEC_ABC(ins); - act->pc += abc - DUK_BC_JUMP_BIAS; + thr->curr_pc += abc - DUK_BC_JUMP_BIAS; break; } @@ -3182,6 +3207,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { entry_thread, entry_callstack_top)) { DUK_DDD(DUK_DDDPRINT("FASTRETURN success a=%ld b=%ld", (long) a, (long) b)); + duk_hthread_sync_currpc(thr); /* must sync explicitly */ goto restart_execution; } } @@ -3299,6 +3325,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { * Avoid C recursion by being clever. */ DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution")); + /* thr->curr_pc synced by duk_handle_ecma_call_setup() */ goto restart_execution; } @@ -3378,6 +3405,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* When debugger is enabled, we need to recheck the activation * status after returning. */ + /* call handling has synced thr->curr_pc */ goto restart_execution; #endif break; @@ -3385,6 +3413,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { case DUK_OP_TRYCATCH: { duk_context *ctx = (duk_context *) thr; + duk_activation *act; duk_catcher *cat; duk_tval *tv1; duk_small_uint_fast_t a; @@ -3431,6 +3460,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { a = DUK_DEC_A(ins); bc = DUK_DEC_BC(ins); + act = thr->callstack + thr->callstack_top - 1; DUK_ASSERT(thr->callstack_top >= 1); /* 'with' target must be created first, in case we run out of memory */ @@ -3524,7 +3554,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { cat = thr->catchstack + thr->catchstack_top - 1; /* relookup (side effects) */ cat->callstack_index = thr->callstack_top - 1; - cat->pc_base = act->pc; /* pre-incremented, points to first jump slot */ + cat->pc_base = thr->curr_pc; /* pre-incremented, points to first jump slot */ cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc; DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, " @@ -3532,7 +3562,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { (unsigned long) cat->flags, (long) cat->callstack_index, (long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname)); - act->pc += 2; /* skip jump slots */ + thr->curr_pc += 2; /* skip jump slots */ break; } @@ -3624,6 +3654,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { case DUK_OP_POSTINCV: case DUK_OP_POSTDECV: { duk_context *ctx = (duk_context *) thr; + duk_activation *act; duk_small_uint_fast_t a = DUK_DEC_A(ins); duk_uint_fast_t bc = DUK_DEC_BC(ins); duk_double_t x, y; @@ -3642,6 +3673,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); + act = thr->callstack + thr->callstack_top - 1; (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ /* XXX: fastint fast path would be very useful here */ @@ -3846,6 +3878,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { case DUK_EXTRAOP_TYPEOFID: { duk_context *ctx = (duk_context *) thr; + duk_activation *act; duk_small_uint_fast_t b = DUK_DEC_B(ins); duk_small_uint_fast_t c = DUK_DEC_C(ins); duk_hstring *name; @@ -3858,6 +3891,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { tv = DUK__REGCONSTP(c); /* XXX: this could be a DUK__CONSTP instead */ DUK_ASSERT(DUK_TVAL_IS_STRING(tv)); name = DUK_TVAL_GET_STRING(tv); + act = thr->callstack + thr->callstack_top - 1; if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) { /* -> [... val this] */ tv = duk_get_tval(ctx, -2); @@ -3928,7 +3962,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* [ ... enum ] -> [ ... next_key ] */ DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ", (duk_tval *) duk_get_tval(ctx, -1))); - act->pc++;; + thr->curr_pc++; } else { /* [ ... enum ] -> [ ... ] */ DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot")); @@ -4048,11 +4082,12 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* no need to unwind callstack */ } - act->pc = cat->pc_base + 1; + thr->curr_pc = cat->pc_base + 1; break; } case DUK_EXTRAOP_ENDCATCH: { + duk_activation *act; duk_catcher *cat; duk_tval tv_tmp; duk_tval *tv1; @@ -4064,6 +4099,8 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { cat = thr->catchstack + thr->catchstack_top - 1; DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat)); /* cleared before entering catch part */ + act = thr->callstack + thr->callstack_top - 1; + if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) { duk_hobject *prev_env; @@ -4104,7 +4141,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* no need to unwind callstack */ } - act->pc = cat->pc_base + 1; + thr->curr_pc = cat->pc_base + 1; break; } @@ -4168,6 +4205,12 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { * break re-throwing for instance. */ + /* Sync needed before Duktape.errThrow augmentation, not + * strictly needed otherwise: bytecode longjmp handler + * syncs at the latest. + */ + duk_hthread_sync_currpc(thr); + duk_dup(ctx, (duk_idx_t) bc); DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1))); @@ -4210,6 +4253,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { DUK_D(DUK_DPRINT("DEBUGGER statement encountered, halt execution")); if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { DUK_HEAP_SET_PAUSED(thr->heap); + duk_hthread_sync_currpc(thr); /* must sync explicitly */ goto restart_execution; } #else @@ -4310,7 +4354,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { cat->flags = DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT); cat->callstack_index = thr->callstack_top - 1; - cat->pc_base = act->pc; /* pre-incremented, points to first jump slot */ + cat->pc_base = thr->curr_pc; /* pre-incremented, points to first jump slot */ cat->idx_base = 0; /* unused for label */ cat->h_varname = NULL; @@ -4319,7 +4363,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { (long) cat->flags, (long) cat->callstack_index, (long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname, (long) DUK_CAT_GET_LABEL(cat))); - act->pc += 2; /* skip jump slots */ + thr->curr_pc += 2; /* skip jump slots */ break; } @@ -4344,32 +4388,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { break; } -#ifdef DUK_USE_DEBUG - case DUK_EXTRAOP_DUMPREG: { - DUK_D(DUK_DPRINT("DUMPREG: %ld -> %!T", - (long) DUK_DEC_BC(ins), - (duk_tval *) duk_get_tval((duk_context *) thr, (duk_idx_t) DUK_DEC_BC(ins)))); - break; - } - - case DUK_EXTRAOP_DUMPREGS: { - duk_idx_t i, i_top; - i_top = duk_get_top((duk_context *) thr); - DUK_D(DUK_DPRINT("DUMPREGS: %ld regs", (long) i_top)); - for (i = 0; i < i_top; i++) { - DUK_D(DUK_DPRINT(" r%ld -> %!dT", - (long) i, - (duk_tval *) duk_get_tval((duk_context *) thr, i))); - } - break; - } - - case DUK_EXTRAOP_LOGMARK: { - DUK_D(DUK_DPRINT("LOGMARK: mark %ld at pc %ld", (long) DUK_DEC_BC(ins), (long) (act->pc - 1))); /* -1, autoinc */ - break; - } -#endif /* DUK_USE_DEBUG */ - default: { DUK__INTERNAL_ERROR("invalid extra opcode"); }