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