1 /* 2 * Heap thread object representation. 3 * 4 * duk_hthread is also the 'context' for public API functions via a 5 * different typedef. Most API calls operate on the topmost frame 6 * of the value stack only. 7 */ 8 9 #if !defined(DUK_HTHREAD_H_INCLUDED) 10 #define DUK_HTHREAD_H_INCLUDED 11 12 /* 13 * Stack constants 14 */ 15 16 /* Initial valstack size, roughly 0.7kiB. */ 17 #define DUK_VALSTACK_INITIAL_SIZE 96U 18 19 /* Internal extra elements assumed on function entry, always added to 20 * user-defined 'extra' for e.g. the duk_check_stack() call. 21 */ 22 #define DUK_VALSTACK_INTERNAL_EXTRA 32U 23 24 /* Number of elements guaranteed to be user accessible (in addition to call 25 * arguments) on Duktape/C function entry. This is the major public API 26 * commitment. 27 */ 28 #define DUK_VALSTACK_API_ENTRY_MINIMUM DUK_API_ENTRY_STACK 29 30 /* 31 * Activation defines 32 */ 33 34 #define DUK_ACT_FLAG_STRICT (1U << 0) /* function executes in strict mode */ 35 #define DUK_ACT_FLAG_TAILCALLED (1U << 1) /* activation has tail called one or more times */ 36 #define DUK_ACT_FLAG_CONSTRUCT (1U << 2) /* function executes as a constructor (called via "new") */ 37 #define DUK_ACT_FLAG_PREVENT_YIELD (1U << 3) /* activation prevents yield (native call or "new") */ 38 #define DUK_ACT_FLAG_DIRECT_EVAL (1U << 4) /* activation is a direct eval call */ 39 #define DUK_ACT_FLAG_CONSTRUCT_PROXY (1U << 5) /* activation is for Proxy 'construct' call, special return value handling */ 40 #define DUK_ACT_FLAG_BREAKPOINT_ACTIVE (1U << 6) /* activation has active breakpoint(s) */ 41 42 #define DUK_ACT_GET_FUNC(act) ((act)->func) 43 44 /* 45 * Flags for __FILE__ / __LINE__ registered into tracedata 46 */ 47 48 #define DUK_TB_FLAG_NOBLAME_FILELINE (1U << 0) /* don't report __FILE__ / __LINE__ as fileName/lineNumber */ 49 50 /* 51 * Catcher defines 52 */ 53 54 /* XXX: remove catcher type entirely */ 55 56 /* flags field: LLLLLLFT, L = label (24 bits), F = flags (4 bits), T = type (4 bits) */ 57 #define DUK_CAT_TYPE_MASK 0x0000000fUL 58 #define DUK_CAT_TYPE_BITS 4 59 #define DUK_CAT_LABEL_MASK 0xffffff00UL 60 #define DUK_CAT_LABEL_BITS 24 61 #define DUK_CAT_LABEL_SHIFT 8 62 63 #define DUK_CAT_FLAG_CATCH_ENABLED (1U << 4) /* catch part will catch */ 64 #define DUK_CAT_FLAG_FINALLY_ENABLED (1U << 5) /* finally part will catch */ 65 #define DUK_CAT_FLAG_CATCH_BINDING_ENABLED (1U << 6) /* request to create catch binding */ 66 #define DUK_CAT_FLAG_LEXENV_ACTIVE (1U << 7) /* catch or with binding is currently active */ 67 68 #define DUK_CAT_TYPE_UNKNOWN 0 69 #define DUK_CAT_TYPE_TCF 1 70 #define DUK_CAT_TYPE_LABEL 2 71 72 #define DUK_CAT_GET_TYPE(c) ((c)->flags & DUK_CAT_TYPE_MASK) 73 #define DUK_CAT_GET_LABEL(c) (((c)->flags & DUK_CAT_LABEL_MASK) >> DUK_CAT_LABEL_SHIFT) 74 75 #define DUK_CAT_HAS_CATCH_ENABLED(c) ((c)->flags & DUK_CAT_FLAG_CATCH_ENABLED) 76 #define DUK_CAT_HAS_FINALLY_ENABLED(c) ((c)->flags & DUK_CAT_FLAG_FINALLY_ENABLED) 77 #define DUK_CAT_HAS_CATCH_BINDING_ENABLED(c) ((c)->flags & DUK_CAT_FLAG_CATCH_BINDING_ENABLED) 78 #define DUK_CAT_HAS_LEXENV_ACTIVE(c) ((c)->flags & DUK_CAT_FLAG_LEXENV_ACTIVE) 79 80 #define DUK_CAT_SET_CATCH_ENABLED(c) do { \ 81 (c)->flags |= DUK_CAT_FLAG_CATCH_ENABLED; \ 82 } while (0) 83 #define DUK_CAT_SET_FINALLY_ENABLED(c) do { \ 84 (c)->flags |= DUK_CAT_FLAG_FINALLY_ENABLED; \ 85 } while (0) 86 #define DUK_CAT_SET_CATCH_BINDING_ENABLED(c) do { \ 87 (c)->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED; \ 88 } while (0) 89 #define DUK_CAT_SET_LEXENV_ACTIVE(c) do { \ 90 (c)->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE; \ 91 } while (0) 92 93 #define DUK_CAT_CLEAR_CATCH_ENABLED(c) do { \ 94 (c)->flags &= ~DUK_CAT_FLAG_CATCH_ENABLED; \ 95 } while (0) 96 #define DUK_CAT_CLEAR_FINALLY_ENABLED(c) do { \ 97 (c)->flags &= ~DUK_CAT_FLAG_FINALLY_ENABLED; \ 98 } while (0) 99 #define DUK_CAT_CLEAR_CATCH_BINDING_ENABLED(c) do { \ 100 (c)->flags &= ~DUK_CAT_FLAG_CATCH_BINDING_ENABLED; \ 101 } while (0) 102 #define DUK_CAT_CLEAR_LEXENV_ACTIVE(c) do { \ 103 (c)->flags &= ~DUK_CAT_FLAG_LEXENV_ACTIVE; \ 104 } while (0) 105 106 /* 107 * Thread defines 108 */ 109 110 #if defined(DUK_USE_ROM_STRINGS) 111 #define DUK_HTHREAD_GET_STRING(thr,idx) \ 112 ((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)])) 113 #else /* DUK_USE_ROM_STRINGS */ 114 #if defined(DUK_USE_HEAPPTR16) 115 #define DUK_HTHREAD_GET_STRING(thr,idx) \ 116 ((duk_hstring *) DUK_USE_HEAPPTR_DEC16((thr)->heap->heap_udata, (thr)->strs16[(idx)])) 117 #else 118 #define DUK_HTHREAD_GET_STRING(thr,idx) \ 119 ((thr)->strs[(idx)]) 120 #endif 121 #endif /* DUK_USE_ROM_STRINGS */ 122 123 /* values for the state field */ 124 #define DUK_HTHREAD_STATE_INACTIVE 1 /* thread not currently running */ 125 #define DUK_HTHREAD_STATE_RUNNING 2 /* thread currently running (only one at a time) */ 126 #define DUK_HTHREAD_STATE_RESUMED 3 /* thread resumed another thread (active but not running) */ 127 #define DUK_HTHREAD_STATE_YIELDED 4 /* thread has yielded */ 128 #define DUK_HTHREAD_STATE_TERMINATED 5 /* thread has terminated */ 129 130 /* Executor interrupt default interval when nothing else requires a 131 * smaller value. The default interval must be small enough to allow 132 * for reasonable execution timeout checking but large enough to keep 133 * impact on execution performance low. 134 */ 135 #if defined(DUK_USE_INTERRUPT_COUNTER) 136 #define DUK_HTHREAD_INTCTR_DEFAULT (256L * 1024L) 137 #endif 138 139 /* 140 * Assert context is valid: non-NULL pointer, fields look sane. 141 * 142 * This is used by public API call entrypoints to catch invalid 'ctx' pointers 143 * as early as possible; invalid 'ctx' pointers cause very odd and difficult to 144 * diagnose behavior so it's worth checking even when the check is not 100%. 145 */ 146 147 #if defined(DUK_USE_ASSERTIONS) 148 /* Assertions for internals. */ 149 DUK_INTERNAL_DECL void duk_hthread_assert_valid(duk_hthread *thr); 150 #define DUK_HTHREAD_ASSERT_VALID(thr) do { duk_hthread_assert_valid((thr)); } while (0) 151 152 /* Assertions for public API calls; a bit stronger. */ 153 DUK_INTERNAL_DECL void duk_ctx_assert_valid(duk_hthread *thr); 154 #define DUK_CTX_ASSERT_VALID(thr) do { duk_ctx_assert_valid((thr)); } while (0) 155 #else 156 #define DUK_HTHREAD_ASSERT_VALID(thr) do {} while (0) 157 #define DUK_CTX_ASSERT_VALID(thr) do {} while (0) 158 #endif 159 160 /* Assertions for API call entry specifically. Checks 'ctx' but also may 161 * check internal state (e.g. not in a debugger transport callback). 162 */ 163 #define DUK_ASSERT_API_ENTRY(thr) do { \ 164 DUK_CTX_ASSERT_VALID((thr)); \ 165 DUK_ASSERT((thr)->heap != NULL); \ 166 DUK_ASSERT((thr)->heap->dbg_calling_transport == 0); \ 167 } while (0) 168 169 /* 170 * Assertion helpers. 171 */ 172 173 #define DUK_ASSERT_STRIDX_VALID(val) \ 174 DUK_ASSERT((duk_uint_t) (val) < DUK_HEAP_NUM_STRINGS) 175 176 #define DUK_ASSERT_BIDX_VALID(val) \ 177 DUK_ASSERT((duk_uint_t) (val) < DUK_NUM_BUILTINS) 178 179 /* 180 * Misc 181 */ 182 183 /* Fast access to 'this' binding. Assumes there's a call in progress. */ 184 #define DUK_HTHREAD_THIS_PTR(thr) \ 185 (DUK_ASSERT_EXPR((thr) != NULL), \ 186 DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \ 187 (thr)->valstack_bottom - 1) 188 189 /* 190 * Struct defines 191 */ 192 193 /* Fields are ordered for alignment/packing. */ 194 struct duk_activation { 195 duk_tval tv_func; /* borrowed: full duk_tval for function being executed; for lightfuncs */ 196 duk_hobject *func; /* borrowed: function being executed; for bound function calls, this is the final, real function, NULL for lightfuncs */ 197 duk_activation *parent; /* previous (parent) activation (or NULL if none) */ 198 duk_hobject *var_env; /* current variable environment (may be NULL if delayed) */ 199 duk_hobject *lex_env; /* current lexical environment (may be NULL if delayed) */ 200 duk_catcher *cat; /* current catcher (or NULL) */ 201 202 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) 203 /* Previous value of 'func' caller, restored when unwound. Only in use 204 * when 'func' is non-strict. 205 */ 206 duk_hobject *prev_caller; 207 #endif 208 209 duk_instr_t *curr_pc; /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */ 210 211 /* bottom_byteoff and retval_byteoff are only used for book-keeping 212 * of ECMAScript-initiated calls, to allow returning to an ECMAScript 213 * function properly. 214 */ 215 216 /* Bottom of valstack for this activation, used to reset 217 * valstack_bottom on return; offset is absolute. There's 218 * no need to track 'top' because native call handling deals 219 * with that using locals, and for ECMAScript returns 'nregs' 220 * indicates the necessary top. 221 */ 222 duk_size_t bottom_byteoff; 223 224 /* Return value when returning to this activation (points to caller 225 * reg, not callee reg); offset is absolute (only set if activation is 226 * not topmost). 227 * 228 * Note: bottom_byteoff is always set, while retval_byteoff is only 229 * applicable for activations below the topmost one. Currently 230 * retval_byteoff for the topmost activation is considered garbage 231 * (and it not initialized on entry or cleared on return; may contain 232 * previous or garbage values). 233 */ 234 duk_size_t retval_byteoff; 235 236 /* Current 'this' binding is the value just below bottom. 237 * Previously, 'this' binding was handled with an index to the 238 * (calling) valstack. This works for everything except tail 239 * calls, which must not "accumulate" valstack temps. 240 */ 241 242 /* Value stack reserve (valstack_end) byte offset to be restored 243 * when returning to this activation. Only used by the bytecode 244 * executor. 245 */ 246 duk_size_t reserve_byteoff; 247 248 #if defined(DUK_USE_DEBUGGER_SUPPORT) 249 duk_uint32_t prev_line; /* needed for stepping */ 250 #endif 251 252 duk_small_uint_t flags; 253 }; 254 255 struct duk_catcher { 256 duk_catcher *parent; /* previous (parent) catcher (or NULL if none) */ 257 duk_hstring *h_varname; /* borrowed reference to catch variable name (or NULL if none) */ 258 /* (reference is valid as long activation exists) */ 259 duk_instr_t *pc_base; /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */ 260 duk_size_t idx_base; /* idx_base and idx_base+1 get completion value and type */ 261 duk_uint32_t flags; /* type and control flags, label number */ 262 /* XXX: could pack 'flags' and 'idx_base' to same value in practice, 263 * on 32-bit targets this would make duk_catcher 16 bytes. 264 */ 265 }; 266 267 struct duk_hthread { 268 /* Shared object part */ 269 duk_hobject obj; 270 271 /* Pointer to bytecode executor's 'curr_pc' variable. Used to copy 272 * the current PC back into the topmost activation when activation 273 * state is about to change (or "syncing" is otherwise needed). This 274 * is rather awkward but important for performance, see execution.rst. 275 */ 276 duk_instr_t **ptr_curr_pc; 277 278 /* Backpointers. */ 279 duk_heap *heap; 280 281 /* Current strictness flag: affects API calls. */ 282 duk_uint8_t strict; 283 284 /* Thread state. */ 285 duk_uint8_t state; 286 duk_uint8_t unused1; 287 duk_uint8_t unused2; 288 289 /* XXX: Valstack and callstack are currently assumed to have non-NULL 290 * pointers. Relaxing this would not lead to big benefits (except 291 * perhaps for terminated threads). 292 */ 293 294 /* Value stack: these are expressed as pointers for faster stack 295 * manipulation. [valstack,valstack_top[ is GC-reachable, 296 * [valstack_top,valstack_alloc_end[ is not GC-reachable but kept 297 * initialized as 'undefined'. [valstack,valstack_end[ is the 298 * guaranteed/reserved space and the valstack cannot be resized to 299 * a smaller size. [valstack_end,valstack_alloc_end[ is currently 300 * allocated slack that can be used to grow the current guaranteed 301 * space but may be shrunk away without notice. 302 * 303 * 304 * <----------------------- guaranteed ---> 305 * <---- slack ---> 306 * <--- frame ---> 307 * .-------------+=============+----------+--------------. 308 * |xxxxxxxxxxxxx|yyyyyyyyyyyyy|uuuuuuuuuu|uuuuuuuuuuuuuu| 309 * `-------------+=============+----------+--------------' 310 * 311 * ^ ^ ^ ^ ^ 312 * | | | | | 313 * valstack bottom top end alloc_end 314 * 315 * xxx = arbitrary values, below current frame 316 * yyy = arbitrary values, inside current frame 317 * uuu = outside active value stack, initialized to 'undefined' 318 */ 319 duk_tval *valstack; /* start of valstack allocation */ 320 duk_tval *valstack_end; /* end of valstack reservation/guarantee (exclusive) */ 321 duk_tval *valstack_alloc_end; /* end of valstack allocation */ 322 duk_tval *valstack_bottom; /* bottom of current frame */ 323 duk_tval *valstack_top; /* top of current frame (exclusive) */ 324 325 /* Call stack, represented as a linked list starting from the current 326 * activation (or NULL if nothing is active). 327 */ 328 duk_activation *callstack_curr; /* current activation (or NULL if none) */ 329 duk_size_t callstack_top; /* number of activation records in callstack (0 if none) */ 330 duk_size_t callstack_preventcount; /* number of activation records in callstack preventing a yield */ 331 332 /* Yield/resume book-keeping. */ 333 duk_hthread *resumer; /* who resumed us (if any) */ 334 335 /* Current compiler state (if any), used for augmenting SyntaxErrors. */ 336 duk_compiler_ctx *compile_ctx; 337 338 #if defined(DUK_USE_INTERRUPT_COUNTER) 339 /* Interrupt counter for triggering a slow path check for execution 340 * timeout, debugger interaction such as breakpoints, etc. The value 341 * is valid for the current running thread, and both the init and 342 * counter values are copied whenever a thread switch occurs. It's 343 * important for the counter to be conveniently accessible for the 344 * bytecode executor inner loop for performance reasons. 345 */ 346 duk_int_t interrupt_counter; /* countdown state */ 347 duk_int_t interrupt_init; /* start value for current countdown */ 348 #endif 349 350 /* Builtin-objects; may or may not be shared with other threads, 351 * threads existing in different "compartments" will have different 352 * built-ins. Must be stored on a per-thread basis because there 353 * is no intermediate structure for a thread group / compartment. 354 * This takes quite a lot of space, currently 43x4 = 172 bytes on 355 * 32-bit platforms. 356 * 357 * In some cases the builtins array could be ROM based, but it's 358 * sometimes edited (e.g. for sandboxing) so it's better to keep 359 * this array in RAM. 360 */ 361 duk_hobject *builtins[DUK_NUM_BUILTINS]; 362 363 /* Convenience copies from heap/vm for faster access. */ 364 #if defined(DUK_USE_ROM_STRINGS) 365 /* No field needed when strings are in ROM. */ 366 #else 367 #if defined(DUK_USE_HEAPPTR16) 368 duk_uint16_t *strs16; 369 #else 370 duk_hstring **strs; 371 #endif 372 #endif 373 }; 374 375 /* 376 * Prototypes 377 */ 378 379 DUK_INTERNAL_DECL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to); 380 DUK_INTERNAL_DECL void duk_hthread_create_builtin_objects(duk_hthread *thr); 381 DUK_INTERNAL_DECL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr); 382 DUK_INTERNAL_DECL void duk_hthread_terminate(duk_hthread *thr); 383 384 DUK_INTERNAL_DECL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr); 385 DUK_INTERNAL_DECL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act); 386 DUK_INTERNAL_DECL void duk_hthread_activation_unwind_norz(duk_hthread *thr); 387 DUK_INTERNAL_DECL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr); 388 DUK_INTERNAL_DECL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level); 389 390 DUK_INTERNAL_DECL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr); 391 DUK_INTERNAL_DECL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat); 392 DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act); 393 DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act); 394 395 #if defined(DUK_USE_FINALIZER_TORTURE) 396 DUK_INTERNAL_DECL void duk_hthread_valstack_torture_realloc(duk_hthread *thr); 397 #endif 398 399 DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ 400 401 #if defined(DUK_USE_DEBUGGER_SUPPORT) 402 DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act); 403 #endif 404 DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act); 405 DUK_INTERNAL_DECL void duk_hthread_sync_currpc(duk_hthread *thr); 406 DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); 407 408 #endif /* DUK_HTHREAD_H_INCLUDED */ 409