]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
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 */ |