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