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