]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * duk_heap allocation and freeing. | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
7 | /* constants for built-in string data depacking */ | |
8 | #define DUK__BITPACK_LETTER_LIMIT 26 | |
9 | #define DUK__BITPACK_UNDERSCORE 26 | |
10 | #define DUK__BITPACK_FF 27 | |
11 | #define DUK__BITPACK_SWITCH1 29 | |
12 | #define DUK__BITPACK_SWITCH 30 | |
13 | #define DUK__BITPACK_SEVENBIT 31 | |
14 | ||
15 | /* | |
16 | * Free a heap object. | |
17 | * | |
18 | * Free heap object and its internal (non-heap) pointers. Assumes that | |
19 | * caller has removed the object from heap allocated list or the string | |
20 | * intern table, and any weak references (which strings may have) have | |
21 | * been already dealt with. | |
22 | */ | |
23 | ||
24 | DUK_INTERNAL void duk_free_hobject_inner(duk_heap *heap, duk_hobject *h) { | |
25 | DUK_ASSERT(heap != NULL); | |
26 | DUK_ASSERT(h != NULL); | |
27 | ||
28 | DUK_FREE(heap, DUK_HOBJECT_GET_PROPS(heap, h)); | |
29 | ||
30 | if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { | |
31 | duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; | |
32 | DUK_UNREF(f); | |
33 | /* Currently nothing to free; 'data' is a heap object */ | |
34 | } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { | |
35 | duk_hnativefunction *f = (duk_hnativefunction *) h; | |
36 | DUK_UNREF(f); | |
37 | /* Currently nothing to free */ | |
38 | } else if (DUK_HOBJECT_IS_THREAD(h)) { | |
39 | duk_hthread *t = (duk_hthread *) h; | |
40 | DUK_FREE(heap, t->valstack); | |
41 | DUK_FREE(heap, t->callstack); | |
42 | DUK_FREE(heap, t->catchstack); | |
43 | /* Don't free h->resumer because it exists in the heap. | |
44 | * Callstack entries also contain function pointers which | |
45 | * are not freed for the same reason. | |
46 | */ | |
47 | ||
48 | /* XXX: with 'caller' property the callstack would need | |
49 | * to be unwound to update the 'caller' properties of | |
50 | * functions in the callstack. | |
51 | */ | |
52 | } | |
53 | } | |
54 | ||
55 | DUK_INTERNAL void duk_free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h) { | |
56 | DUK_ASSERT(heap != NULL); | |
57 | DUK_ASSERT(h != NULL); | |
58 | ||
59 | if (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h)) { | |
60 | duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; | |
61 | DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, g))); | |
62 | DUK_FREE(heap, DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, g)); | |
63 | } | |
64 | } | |
65 | ||
66 | DUK_INTERNAL void duk_free_hstring_inner(duk_heap *heap, duk_hstring *h) { | |
67 | DUK_ASSERT(heap != NULL); | |
68 | DUK_ASSERT(h != NULL); | |
69 | ||
70 | DUK_UNREF(heap); | |
71 | DUK_UNREF(h); | |
72 | ||
73 | #if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_FREE) | |
74 | if (DUK_HSTRING_HAS_EXTDATA(h)) { | |
75 | DUK_DDD(DUK_DDDPRINT("free extstr: hstring %!O, extdata: %p", | |
76 | h, DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h))); | |
77 | DUK_USE_EXTSTR_FREE(heap->heap_udata, (const void *) DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h)); | |
78 | } | |
79 | #endif | |
80 | } | |
81 | ||
82 | DUK_INTERNAL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr) { | |
83 | DUK_ASSERT(heap); | |
84 | DUK_ASSERT(hdr); | |
85 | ||
86 | DUK_DDD(DUK_DDDPRINT("free heaphdr %p, htype %ld", (void *) hdr, (long) DUK_HEAPHDR_GET_TYPE(hdr))); | |
87 | ||
88 | switch ((int) DUK_HEAPHDR_GET_TYPE(hdr)) { | |
89 | case DUK_HTYPE_STRING: | |
90 | duk_free_hstring_inner(heap, (duk_hstring *) hdr); | |
91 | break; | |
92 | case DUK_HTYPE_OBJECT: | |
93 | duk_free_hobject_inner(heap, (duk_hobject *) hdr); | |
94 | break; | |
95 | case DUK_HTYPE_BUFFER: | |
96 | duk_free_hbuffer_inner(heap, (duk_hbuffer *) hdr); | |
97 | break; | |
98 | default: | |
99 | DUK_UNREACHABLE(); | |
100 | } | |
101 | ||
102 | DUK_FREE(heap, hdr); | |
103 | } | |
104 | ||
105 | /* | |
106 | * Free the heap. | |
107 | * | |
108 | * Frees heap-related non-heap-tracked allocations such as the | |
109 | * string intern table; then frees the heap allocated objects; | |
110 | * and finally frees the heap structure itself. Reference counts | |
111 | * and GC markers are ignored (and not updated) in this process, | |
112 | * and finalizers won't be called. | |
113 | * | |
114 | * The heap pointer and heap object pointers must not be used | |
115 | * after this call. | |
116 | */ | |
117 | ||
118 | DUK_LOCAL void duk__free_allocated(duk_heap *heap) { | |
119 | duk_heaphdr *curr; | |
120 | duk_heaphdr *next; | |
121 | ||
122 | curr = heap->heap_allocated; | |
123 | while (curr) { | |
124 | /* We don't log or warn about freeing zero refcount objects | |
125 | * because they may happen with finalizer processing. | |
126 | */ | |
127 | ||
128 | DUK_DDD(DUK_DDDPRINT("FINALFREE (allocated): %!iO", | |
129 | (duk_heaphdr *) curr)); | |
130 | next = DUK_HEAPHDR_GET_NEXT(heap, curr); | |
131 | duk_heap_free_heaphdr_raw(heap, curr); | |
132 | curr = next; | |
133 | } | |
134 | } | |
135 | ||
136 | #ifdef DUK_USE_REFERENCE_COUNTING | |
137 | DUK_LOCAL void duk__free_refzero_list(duk_heap *heap) { | |
138 | duk_heaphdr *curr; | |
139 | duk_heaphdr *next; | |
140 | ||
141 | curr = heap->refzero_list; | |
142 | while (curr) { | |
143 | DUK_DDD(DUK_DDDPRINT("FINALFREE (refzero_list): %!iO", | |
144 | (duk_heaphdr *) curr)); | |
145 | next = DUK_HEAPHDR_GET_NEXT(heap, curr); | |
146 | duk_heap_free_heaphdr_raw(heap, curr); | |
147 | curr = next; | |
148 | } | |
149 | } | |
150 | #endif | |
151 | ||
152 | #ifdef DUK_USE_MARK_AND_SWEEP | |
153 | DUK_LOCAL void duk__free_markandsweep_finalize_list(duk_heap *heap) { | |
154 | duk_heaphdr *curr; | |
155 | duk_heaphdr *next; | |
156 | ||
157 | curr = heap->finalize_list; | |
158 | while (curr) { | |
159 | DUK_DDD(DUK_DDDPRINT("FINALFREE (finalize_list): %!iO", | |
160 | (duk_heaphdr *) curr)); | |
161 | next = DUK_HEAPHDR_GET_NEXT(heap, curr); | |
162 | duk_heap_free_heaphdr_raw(heap, curr); | |
163 | curr = next; | |
164 | } | |
165 | } | |
166 | #endif | |
167 | ||
168 | DUK_LOCAL void duk__free_stringtable(duk_heap *heap) { | |
169 | /* strings are only tracked by stringtable */ | |
170 | duk_heap_free_strtab(heap); | |
171 | } | |
172 | ||
173 | DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { | |
174 | duk_hthread *thr; | |
175 | duk_heaphdr *curr; | |
176 | #ifdef DUK_USE_DEBUG | |
177 | duk_size_t count_obj = 0; | |
178 | #endif | |
179 | ||
180 | DUK_ASSERT(heap != NULL); | |
181 | DUK_ASSERT(heap->heap_thread != NULL); | |
182 | #ifdef DUK_USE_REFERENCE_COUNTING | |
183 | DUK_ASSERT(heap->refzero_list == NULL); /* refzero not running -> must be empty */ | |
184 | #endif | |
185 | #ifdef DUK_USE_MARK_AND_SWEEP | |
186 | DUK_ASSERT(heap->finalize_list == NULL); /* mark-and-sweep not running -> must be empty */ | |
187 | #endif | |
188 | ||
189 | /* XXX: here again finalizer thread is the heap_thread which needs | |
190 | * to be coordinated with finalizer thread fixes. | |
191 | */ | |
192 | thr = heap->heap_thread; | |
193 | DUK_ASSERT(thr != NULL); | |
194 | ||
195 | curr = heap->heap_allocated; | |
196 | while (curr) { | |
197 | if (DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT) { | |
198 | /* Only objects in heap_allocated may have finalizers. Check that | |
199 | * the object itself has a _Finalizer property so that we don't | |
200 | * execute finalizers for e.g. Proxy objects. | |
201 | */ | |
202 | DUK_ASSERT(thr != NULL); | |
203 | DUK_ASSERT(curr != NULL); | |
204 | ||
205 | if (duk_hobject_hasprop_raw(thr, (duk_hobject *) curr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) { | |
206 | duk_hobject_run_finalizer(thr, (duk_hobject *) curr); | |
207 | } | |
208 | #ifdef DUK_USE_DEBUG | |
209 | count_obj++; | |
210 | #endif | |
211 | } | |
212 | curr = DUK_HEAPHDR_GET_NEXT(heap, curr); | |
213 | } | |
214 | ||
215 | /* Note: count includes all objects, not only those with an actual finalizer. */ | |
216 | #ifdef DUK_USE_DEBUG | |
217 | DUK_D(DUK_DPRINT("checked %ld objects for finalizers before freeing heap", (long) count_obj)); | |
218 | #endif | |
219 | } | |
220 | ||
221 | DUK_INTERNAL void duk_heap_free(duk_heap *heap) { | |
222 | DUK_D(DUK_DPRINT("free heap: %p", (void *) heap)); | |
223 | ||
224 | #if defined(DUK_USE_DEBUG) | |
225 | duk_heap_dump_strtab(heap); | |
226 | #endif | |
227 | ||
228 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
229 | /* Detach a debugger if attached (can be called multiple times) | |
230 | * safely. | |
231 | */ | |
232 | duk_debug_do_detach(heap); | |
233 | #endif | |
234 | ||
235 | /* Execute finalizers before freeing the heap, even for reachable | |
236 | * objects, and regardless of whether or not mark-and-sweep is | |
237 | * enabled. This gives finalizers the chance to free any native | |
238 | * resources like file handles, allocations made outside Duktape, | |
239 | * etc. | |
240 | * | |
241 | * XXX: this perhaps requires an execution time limit. | |
242 | */ | |
243 | DUK_D(DUK_DPRINT("execute finalizers before freeing heap")); | |
244 | #ifdef DUK_USE_MARK_AND_SWEEP | |
245 | /* run mark-and-sweep a few times just in case (unreachable | |
246 | * object finalizers run already here) | |
247 | */ | |
248 | duk_heap_mark_and_sweep(heap, 0); | |
249 | duk_heap_mark_and_sweep(heap, 0); | |
250 | #endif | |
251 | duk__free_run_finalizers(heap); | |
252 | ||
253 | /* Note: heap->heap_thread, heap->curr_thread, and heap->heap_object | |
254 | * are on the heap allocated list. | |
255 | */ | |
256 | ||
257 | DUK_D(DUK_DPRINT("freeing heap objects of heap: %p", (void *) heap)); | |
258 | duk__free_allocated(heap); | |
259 | ||
260 | #ifdef DUK_USE_REFERENCE_COUNTING | |
261 | DUK_D(DUK_DPRINT("freeing refzero list of heap: %p", (void *) heap)); | |
262 | duk__free_refzero_list(heap); | |
263 | #endif | |
264 | ||
265 | #ifdef DUK_USE_MARK_AND_SWEEP | |
266 | DUK_D(DUK_DPRINT("freeing mark-and-sweep finalize list of heap: %p", (void *) heap)); | |
267 | duk__free_markandsweep_finalize_list(heap); | |
268 | #endif | |
269 | ||
270 | DUK_D(DUK_DPRINT("freeing string table of heap: %p", (void *) heap)); | |
271 | duk__free_stringtable(heap); | |
272 | ||
273 | DUK_D(DUK_DPRINT("freeing heap structure: %p", (void *) heap)); | |
274 | heap->free_func(heap->heap_udata, heap); | |
275 | } | |
276 | ||
277 | /* | |
278 | * Allocate a heap. | |
279 | * | |
280 | * String table is initialized with built-in strings from genstrings.py. | |
281 | */ | |
282 | ||
283 | /* intern built-in strings from precooked data (genstrings.py) */ | |
284 | DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { | |
285 | duk_bitdecoder_ctx bd_ctx; | |
286 | duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ | |
287 | duk_small_uint_t i, j; | |
288 | ||
289 | DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); | |
290 | bd->data = (const duk_uint8_t *) duk_strings_data; | |
291 | bd->length = (duk_size_t) DUK_STRDATA_DATA_LENGTH; | |
292 | ||
293 | for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { | |
294 | duk_uint8_t tmp[DUK_STRDATA_MAX_STRLEN]; | |
295 | duk_hstring *h; | |
296 | duk_small_uint_t len; | |
297 | duk_small_uint_t mode; | |
298 | duk_small_uint_t t; | |
299 | ||
300 | len = duk_bd_decode(bd, 5); | |
301 | mode = 32; /* 0 = uppercase, 32 = lowercase (= 'a' - 'A') */ | |
302 | for (j = 0; j < len; j++) { | |
303 | t = duk_bd_decode(bd, 5); | |
304 | if (t < DUK__BITPACK_LETTER_LIMIT) { | |
305 | t = t + DUK_ASC_UC_A + mode; | |
306 | } else if (t == DUK__BITPACK_UNDERSCORE) { | |
307 | t = DUK_ASC_UNDERSCORE; | |
308 | } else if (t == DUK__BITPACK_FF) { | |
309 | /* Internal keys are prefixed with 0xFF in the stringtable | |
310 | * (which makes them invalid UTF-8 on purpose). | |
311 | */ | |
312 | t = 0xff; | |
313 | } else if (t == DUK__BITPACK_SWITCH1) { | |
314 | t = duk_bd_decode(bd, 5); | |
315 | DUK_ASSERT_DISABLE(t >= 0); /* unsigned */ | |
316 | DUK_ASSERT(t <= 25); | |
317 | t = t + DUK_ASC_UC_A + (mode ^ 32); | |
318 | } else if (t == DUK__BITPACK_SWITCH) { | |
319 | mode = mode ^ 32; | |
320 | t = duk_bd_decode(bd, 5); | |
321 | DUK_ASSERT_DISABLE(t >= 0); | |
322 | DUK_ASSERT(t <= 25); | |
323 | t = t + DUK_ASC_UC_A + mode; | |
324 | } else if (t == DUK__BITPACK_SEVENBIT) { | |
325 | t = duk_bd_decode(bd, 7); | |
326 | } | |
327 | tmp[j] = (duk_uint8_t) t; | |
328 | } | |
329 | ||
330 | /* No need to length check string: it will never exceed even | |
331 | * the 16-bit length maximum. | |
332 | */ | |
333 | DUK_ASSERT(len <= 0xffffUL); | |
334 | DUK_DDD(DUK_DDDPRINT("intern built-in string %ld", (long) i)); | |
335 | h = duk_heap_string_intern(heap, tmp, len); | |
336 | if (!h) { | |
337 | goto error; | |
338 | } | |
339 | ||
340 | /* Special flags checks. Since these strings are always | |
341 | * reachable and a string cannot appear twice in the string | |
342 | * table, there's no need to check/set these flags elsewhere. | |
343 | * The 'internal' flag is set by string intern code. | |
344 | */ | |
345 | if (i == DUK_STRIDX_EVAL || i == DUK_STRIDX_LC_ARGUMENTS) { | |
346 | DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(h); | |
347 | } | |
348 | if (i >= DUK_STRIDX_START_RESERVED && i < DUK_STRIDX_END_RESERVED) { | |
349 | DUK_HSTRING_SET_RESERVED_WORD(h); | |
350 | if (i >= DUK_STRIDX_START_STRICT_RESERVED) { | |
351 | DUK_HSTRING_SET_STRICT_RESERVED_WORD(h); | |
352 | } | |
353 | } | |
354 | ||
355 | DUK_DDD(DUK_DDDPRINT("interned: %!O", (duk_heaphdr *) h)); | |
356 | ||
357 | /* XXX: The incref macro takes a thread pointer but doesn't | |
358 | * use it right now. | |
359 | */ | |
360 | DUK_HSTRING_INCREF(_never_referenced_, h); | |
361 | ||
362 | #if defined(DUK_USE_HEAPPTR16) | |
363 | heap->strs16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); | |
364 | #else | |
365 | heap->strs[i] = h; | |
366 | #endif | |
367 | } | |
368 | ||
369 | return 1; | |
370 | ||
371 | error: | |
372 | return 0; | |
373 | } | |
374 | ||
375 | DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) { | |
376 | duk_hthread *thr; | |
377 | ||
378 | DUK_DD(DUK_DDPRINT("heap init: alloc heap thread")); | |
379 | thr = duk_hthread_alloc(heap, | |
380 | DUK_HOBJECT_FLAG_EXTENSIBLE | | |
381 | DUK_HOBJECT_FLAG_THREAD | | |
382 | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); | |
383 | if (!thr) { | |
384 | DUK_D(DUK_DPRINT("failed to alloc heap_thread")); | |
385 | return 0; | |
386 | } | |
387 | thr->state = DUK_HTHREAD_STATE_INACTIVE; | |
388 | #if defined(DUK_USE_HEAPPTR16) | |
389 | thr->strs16 = heap->strs16; | |
390 | #else | |
391 | thr->strs = heap->strs; | |
392 | #endif | |
393 | ||
394 | heap->heap_thread = thr; | |
395 | DUK_HTHREAD_INCREF(thr, thr); /* Note: first argument not really used */ | |
396 | ||
397 | /* 'thr' is now reachable */ | |
398 | ||
399 | if (!duk_hthread_init_stacks(heap, thr)) { | |
400 | return 0; | |
401 | } | |
402 | ||
403 | /* XXX: this may now fail, and is not handled correctly */ | |
404 | duk_hthread_create_builtin_objects(thr); | |
405 | ||
406 | /* default prototype (Note: 'thr' must be reachable) */ | |
407 | DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) thr, thr->builtins[DUK_BIDX_THREAD_PROTOTYPE]); | |
408 | ||
409 | return 1; | |
410 | } | |
411 | ||
412 | #ifdef DUK_USE_DEBUG | |
413 | #define DUK__DUMPSZ(t) do { \ | |
414 | DUK_D(DUK_DPRINT("" #t "=%ld", (long) sizeof(t))); \ | |
415 | } while (0) | |
416 | ||
417 | /* These is not 100% because format would need to be non-portable "long long". | |
418 | * Also print out as doubles to catch cases where the "long" type is not wide | |
419 | * enough; the limits will then not be printed accurately but the magnitude | |
420 | * will be correct. | |
421 | */ | |
422 | #define DUK__DUMPLM_SIGNED_RAW(t,a,b) do { \ | |
423 | DUK_D(DUK_DPRINT(t "=[%ld,%ld]=[%lf,%lf]", \ | |
424 | (long) (a), (long) (b), \ | |
425 | (double) (a), (double) (b))); \ | |
426 | } while(0) | |
427 | #define DUK__DUMPLM_UNSIGNED_RAW(t,a,b) do { \ | |
428 | DUK_D(DUK_DPRINT(t "=[%lu,%lu]=[%lf,%lf]", \ | |
429 | (unsigned long) (a), (unsigned long) (b), \ | |
430 | (double) (a), (double) (b))); \ | |
431 | } while(0) | |
432 | #define DUK__DUMPLM_SIGNED(t) do { \ | |
433 | DUK__DUMPLM_SIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \ | |
434 | } while(0) | |
435 | #define DUK__DUMPLM_UNSIGNED(t) do { \ | |
436 | DUK__DUMPLM_UNSIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \ | |
437 | } while(0) | |
438 | ||
439 | DUK_LOCAL void duk__dump_type_sizes(void) { | |
440 | DUK_D(DUK_DPRINT("sizeof()")); | |
441 | ||
442 | /* basic platform types */ | |
443 | DUK__DUMPSZ(char); | |
444 | DUK__DUMPSZ(short); | |
445 | DUK__DUMPSZ(int); | |
446 | DUK__DUMPSZ(long); | |
447 | DUK__DUMPSZ(double); | |
448 | DUK__DUMPSZ(void *); | |
449 | DUK__DUMPSZ(size_t); | |
450 | ||
451 | /* basic types from duk_features.h */ | |
452 | DUK__DUMPSZ(duk_uint8_t); | |
453 | DUK__DUMPSZ(duk_int8_t); | |
454 | DUK__DUMPSZ(duk_uint16_t); | |
455 | DUK__DUMPSZ(duk_int16_t); | |
456 | DUK__DUMPSZ(duk_uint32_t); | |
457 | DUK__DUMPSZ(duk_int32_t); | |
458 | DUK__DUMPSZ(duk_uint64_t); | |
459 | DUK__DUMPSZ(duk_int64_t); | |
460 | DUK__DUMPSZ(duk_uint_least8_t); | |
461 | DUK__DUMPSZ(duk_int_least8_t); | |
462 | DUK__DUMPSZ(duk_uint_least16_t); | |
463 | DUK__DUMPSZ(duk_int_least16_t); | |
464 | DUK__DUMPSZ(duk_uint_least32_t); | |
465 | DUK__DUMPSZ(duk_int_least32_t); | |
466 | #if defined(DUK_USE_64BIT_OPS) | |
467 | DUK__DUMPSZ(duk_uint_least64_t); | |
468 | DUK__DUMPSZ(duk_int_least64_t); | |
469 | #endif | |
470 | DUK__DUMPSZ(duk_uint_fast8_t); | |
471 | DUK__DUMPSZ(duk_int_fast8_t); | |
472 | DUK__DUMPSZ(duk_uint_fast16_t); | |
473 | DUK__DUMPSZ(duk_int_fast16_t); | |
474 | DUK__DUMPSZ(duk_uint_fast32_t); | |
475 | DUK__DUMPSZ(duk_int_fast32_t); | |
476 | #if defined(DUK_USE_64BIT_OPS) | |
477 | DUK__DUMPSZ(duk_uint_fast64_t); | |
478 | DUK__DUMPSZ(duk_int_fast64_t); | |
479 | #endif | |
480 | DUK__DUMPSZ(duk_uintptr_t); | |
481 | DUK__DUMPSZ(duk_intptr_t); | |
482 | DUK__DUMPSZ(duk_uintmax_t); | |
483 | DUK__DUMPSZ(duk_intmax_t); | |
484 | DUK__DUMPSZ(duk_double_t); | |
485 | ||
486 | /* important chosen base types */ | |
487 | DUK__DUMPSZ(duk_int_t); | |
488 | DUK__DUMPSZ(duk_uint_t); | |
489 | DUK__DUMPSZ(duk_int_fast_t); | |
490 | DUK__DUMPSZ(duk_uint_fast_t); | |
491 | DUK__DUMPSZ(duk_small_int_t); | |
492 | DUK__DUMPSZ(duk_small_uint_t); | |
493 | DUK__DUMPSZ(duk_small_int_fast_t); | |
494 | DUK__DUMPSZ(duk_small_uint_fast_t); | |
495 | ||
496 | /* some derived types */ | |
497 | DUK__DUMPSZ(duk_codepoint_t); | |
498 | DUK__DUMPSZ(duk_ucodepoint_t); | |
499 | DUK__DUMPSZ(duk_idx_t); | |
500 | DUK__DUMPSZ(duk_errcode_t); | |
501 | DUK__DUMPSZ(duk_uarridx_t); | |
502 | ||
503 | /* tval */ | |
504 | DUK__DUMPSZ(duk_double_union); | |
505 | DUK__DUMPSZ(duk_tval); | |
506 | ||
507 | /* structs from duk_forwdecl.h */ | |
508 | DUK__DUMPSZ(duk_jmpbuf); | |
509 | DUK__DUMPSZ(duk_heaphdr); | |
510 | DUK__DUMPSZ(duk_heaphdr_string); | |
511 | DUK__DUMPSZ(duk_hstring); | |
512 | DUK__DUMPSZ(duk_hstring_external); | |
513 | DUK__DUMPSZ(duk_hobject); | |
514 | DUK__DUMPSZ(duk_hcompiledfunction); | |
515 | DUK__DUMPSZ(duk_hnativefunction); | |
516 | DUK__DUMPSZ(duk_hthread); | |
517 | DUK__DUMPSZ(duk_hbuffer); | |
518 | DUK__DUMPSZ(duk_hbuffer_fixed); | |
519 | DUK__DUMPSZ(duk_hbuffer_dynamic); | |
520 | DUK__DUMPSZ(duk_hbuffer_external); | |
521 | DUK__DUMPSZ(duk_propaccessor); | |
522 | DUK__DUMPSZ(duk_propvalue); | |
523 | DUK__DUMPSZ(duk_propdesc); | |
524 | DUK__DUMPSZ(duk_heap); | |
525 | #if defined(DUK_USE_STRTAB_CHAIN) | |
526 | DUK__DUMPSZ(duk_strtab_entry); | |
527 | #endif | |
528 | DUK__DUMPSZ(duk_activation); | |
529 | DUK__DUMPSZ(duk_catcher); | |
530 | DUK__DUMPSZ(duk_strcache); | |
531 | DUK__DUMPSZ(duk_ljstate); | |
532 | DUK__DUMPSZ(duk_fixedbuffer); | |
533 | DUK__DUMPSZ(duk_bitdecoder_ctx); | |
534 | DUK__DUMPSZ(duk_bitencoder_ctx); | |
535 | DUK__DUMPSZ(duk_token); | |
536 | DUK__DUMPSZ(duk_re_token); | |
537 | DUK__DUMPSZ(duk_lexer_point); | |
538 | DUK__DUMPSZ(duk_lexer_ctx); | |
539 | DUK__DUMPSZ(duk_compiler_instr); | |
540 | DUK__DUMPSZ(duk_compiler_func); | |
541 | DUK__DUMPSZ(duk_compiler_ctx); | |
542 | DUK__DUMPSZ(duk_re_matcher_ctx); | |
543 | DUK__DUMPSZ(duk_re_compiler_ctx); | |
544 | } | |
545 | DUK_LOCAL void duk__dump_type_limits(void) { | |
546 | DUK_D(DUK_DPRINT("limits")); | |
547 | ||
548 | /* basic types */ | |
549 | DUK__DUMPLM_SIGNED(INT8); | |
550 | DUK__DUMPLM_UNSIGNED(UINT8); | |
551 | DUK__DUMPLM_SIGNED(INT_FAST8); | |
552 | DUK__DUMPLM_UNSIGNED(UINT_FAST8); | |
553 | DUK__DUMPLM_SIGNED(INT_LEAST8); | |
554 | DUK__DUMPLM_UNSIGNED(UINT_LEAST8); | |
555 | DUK__DUMPLM_SIGNED(INT16); | |
556 | DUK__DUMPLM_UNSIGNED(UINT16); | |
557 | DUK__DUMPLM_SIGNED(INT_FAST16); | |
558 | DUK__DUMPLM_UNSIGNED(UINT_FAST16); | |
559 | DUK__DUMPLM_SIGNED(INT_LEAST16); | |
560 | DUK__DUMPLM_UNSIGNED(UINT_LEAST16); | |
561 | DUK__DUMPLM_SIGNED(INT32); | |
562 | DUK__DUMPLM_UNSIGNED(UINT32); | |
563 | DUK__DUMPLM_SIGNED(INT_FAST32); | |
564 | DUK__DUMPLM_UNSIGNED(UINT_FAST32); | |
565 | DUK__DUMPLM_SIGNED(INT_LEAST32); | |
566 | DUK__DUMPLM_UNSIGNED(UINT_LEAST32); | |
567 | #if defined(DUK_USE_64BIT_OPS) | |
568 | DUK__DUMPLM_SIGNED(INT64); | |
569 | DUK__DUMPLM_UNSIGNED(UINT64); | |
570 | DUK__DUMPLM_SIGNED(INT_FAST64); | |
571 | DUK__DUMPLM_UNSIGNED(UINT_FAST64); | |
572 | DUK__DUMPLM_SIGNED(INT_LEAST64); | |
573 | DUK__DUMPLM_UNSIGNED(UINT_LEAST64); | |
574 | #endif | |
575 | DUK__DUMPLM_SIGNED(INTPTR); | |
576 | DUK__DUMPLM_UNSIGNED(UINTPTR); | |
577 | DUK__DUMPLM_SIGNED(INTMAX); | |
578 | DUK__DUMPLM_UNSIGNED(UINTMAX); | |
579 | ||
580 | /* derived types */ | |
581 | DUK__DUMPLM_SIGNED(INT); | |
582 | DUK__DUMPLM_UNSIGNED(UINT); | |
583 | DUK__DUMPLM_SIGNED(INT_FAST); | |
584 | DUK__DUMPLM_UNSIGNED(UINT_FAST); | |
585 | DUK__DUMPLM_SIGNED(SMALL_INT); | |
586 | DUK__DUMPLM_UNSIGNED(SMALL_UINT); | |
587 | DUK__DUMPLM_SIGNED(SMALL_INT_FAST); | |
588 | DUK__DUMPLM_UNSIGNED(SMALL_UINT_FAST); | |
589 | } | |
590 | #undef DUK__DUMPSZ | |
591 | #undef DUK__DUMPLM_SIGNED_RAW | |
592 | #undef DUK__DUMPLM_UNSIGNED_RAW | |
593 | #undef DUK__DUMPLM_SIGNED | |
594 | #undef DUK__DUMPLM_UNSIGNED | |
595 | ||
596 | DUK_LOCAL void duk__dump_misc_options(void) { | |
597 | DUK_D(DUK_DPRINT("DUK_VERSION: %ld", (long) DUK_VERSION)); | |
598 | DUK_D(DUK_DPRINT("DUK_GIT_DESCRIBE: %s", DUK_GIT_DESCRIBE)); | |
599 | #if defined(DUK_USE_PACKED_TVAL) | |
600 | DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: yes")); | |
601 | #else | |
602 | DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: no")); | |
603 | #endif | |
604 | #if defined(DUK_USE_INTEGER_LE) | |
605 | DUK_D(DUK_DPRINT("Integer endianness: little")); | |
606 | #elif defined(DUK_USE_INTEGER_ME) | |
607 | DUK_D(DUK_DPRINT("Integer endianness: mixed")); | |
608 | #elif defined(DUK_USE_INTEGER_BE) | |
609 | DUK_D(DUK_DPRINT("Integer endianness: big")); | |
610 | #else | |
611 | DUK_D(DUK_DPRINT("Integer endianness: ???")); | |
612 | #endif | |
613 | #if defined(DUK_USE_DOUBLE_LE) | |
614 | DUK_D(DUK_DPRINT("IEEE double endianness: little")); | |
615 | #elif defined(DUK_USE_DOUBLE_ME) | |
616 | DUK_D(DUK_DPRINT("IEEE double endianness: mixed")); | |
617 | #elif defined(DUK_USE_DOUBLE_BE) | |
618 | DUK_D(DUK_DPRINT("IEEE double endianness: big")); | |
619 | #else | |
620 | DUK_D(DUK_DPRINT("IEEE double endianness: ???")); | |
621 | #endif | |
622 | } | |
623 | #endif /* DUK_USE_DEBUG */ | |
624 | ||
625 | DUK_INTERNAL | |
626 | duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, | |
627 | duk_realloc_function realloc_func, | |
628 | duk_free_function free_func, | |
629 | void *heap_udata, | |
630 | duk_fatal_function fatal_func) { | |
631 | duk_heap *res = NULL; | |
632 | ||
633 | /* Silence a few global unused warnings here. */ | |
634 | DUK_UNREF(duk_str_unsupported); | |
635 | ||
636 | DUK_D(DUK_DPRINT("allocate heap")); | |
637 | ||
638 | /* | |
639 | * Debug dump type sizes | |
640 | */ | |
641 | ||
642 | #ifdef DUK_USE_DEBUG | |
643 | duk__dump_misc_options(); | |
644 | duk__dump_type_sizes(); | |
645 | duk__dump_type_limits(); | |
646 | #endif | |
647 | ||
648 | /* | |
649 | * If selftests enabled, run them as early as possible | |
650 | */ | |
651 | #ifdef DUK_USE_SELF_TESTS | |
652 | DUK_D(DUK_DPRINT("running self tests")); | |
653 | duk_selftest_run_tests(); | |
654 | DUK_D(DUK_DPRINT("self tests passed")); | |
655 | #endif | |
656 | ||
657 | /* | |
658 | * Computed values (e.g. INFINITY) | |
659 | */ | |
660 | ||
661 | #ifdef DUK_USE_COMPUTED_NAN | |
662 | do { | |
663 | /* Workaround for some exotic platforms where NAN is missing | |
664 | * and the expression (0.0 / 0.0) does NOT result in a NaN. | |
665 | * Such platforms use the global 'duk_computed_nan' which must | |
666 | * be initialized at runtime. Use 'volatile' to ensure that | |
667 | * the compiler will actually do the computation and not try | |
668 | * to do constant folding which might result in the original | |
669 | * problem. | |
670 | */ | |
671 | volatile double dbl1 = 0.0; | |
672 | volatile double dbl2 = 0.0; | |
673 | duk_computed_nan = dbl1 / dbl2; | |
674 | } while (0); | |
675 | #endif | |
676 | ||
677 | #ifdef DUK_USE_COMPUTED_INFINITY | |
678 | do { | |
679 | /* Similar workaround for INFINITY. */ | |
680 | volatile double dbl1 = 1.0; | |
681 | volatile double dbl2 = 0.0; | |
682 | duk_computed_infinity = dbl1 / dbl2; | |
683 | } while (0); | |
684 | #endif | |
685 | ||
686 | /* | |
687 | * Allocate heap struct | |
688 | * | |
689 | * Use a raw call, all macros expect the heap to be initialized | |
690 | */ | |
691 | ||
692 | res = (duk_heap *) alloc_func(heap_udata, sizeof(duk_heap)); | |
693 | if (!res) { | |
694 | goto error; | |
695 | } | |
696 | ||
697 | /* | |
698 | * Zero the struct, and start initializing roughly in order | |
699 | */ | |
700 | ||
701 | DUK_MEMZERO(res, sizeof(*res)); | |
702 | ||
703 | /* explicit NULL inits */ | |
704 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
705 | res->heap_udata = NULL; | |
706 | res->heap_allocated = NULL; | |
707 | #ifdef DUK_USE_REFERENCE_COUNTING | |
708 | res->refzero_list = NULL; | |
709 | res->refzero_list_tail = NULL; | |
710 | #endif | |
711 | #ifdef DUK_USE_MARK_AND_SWEEP | |
712 | res->finalize_list = NULL; | |
713 | #endif | |
714 | res->heap_thread = NULL; | |
715 | res->curr_thread = NULL; | |
716 | res->heap_object = NULL; | |
717 | #if defined(DUK_USE_STRTAB_CHAIN) | |
718 | /* nothing to NULL */ | |
719 | #elif defined(DUK_USE_STRTAB_PROBE) | |
720 | #if defined(DUK_USE_HEAPPTR16) | |
721 | res->strtable16 = (duk_uint16_t *) NULL; | |
722 | #else | |
723 | res->strtable = (duk_hstring **) NULL; | |
724 | #endif | |
725 | #endif | |
726 | #if defined(DUK_USE_HEAPPTR16) | |
727 | /* res->strs16[] is zeroed and zero decodes to NULL, so no NULL inits. */ | |
728 | #else | |
729 | { | |
730 | duk_small_uint_t i; | |
731 | for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { | |
732 | res->strs[i] = NULL; | |
733 | } | |
734 | } | |
735 | #endif | |
736 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
737 | res->dbg_read_cb = NULL; | |
738 | res->dbg_write_cb = NULL; | |
739 | res->dbg_peek_cb = NULL; | |
740 | res->dbg_read_flush_cb = NULL; | |
741 | res->dbg_write_flush_cb = NULL; | |
742 | res->dbg_udata = NULL; | |
743 | res->dbg_step_thread = NULL; | |
744 | #endif | |
745 | #endif /* DUK_USE_EXPLICIT_NULL_INIT */ | |
746 | ||
747 | res->alloc_func = alloc_func; | |
748 | res->realloc_func = realloc_func; | |
749 | res->free_func = free_func; | |
750 | res->heap_udata = heap_udata; | |
751 | res->fatal_func = fatal_func; | |
752 | ||
753 | #if defined(DUK_USE_HEAPPTR16) | |
754 | /* XXX: zero assumption */ | |
755 | res->heapptr_null16 = DUK_USE_HEAPPTR_ENC16(res->heap_udata, (void *) NULL); | |
756 | res->heapptr_deleted16 = DUK_USE_HEAPPTR_ENC16(res->heap_udata, (void *) DUK_STRTAB_DELETED_MARKER(res)); | |
757 | #endif | |
758 | ||
759 | /* res->mark_and_sweep_trigger_counter == 0 -> now causes immediate GC; which is OK */ | |
760 | ||
761 | res->call_recursion_depth = 0; | |
762 | res->call_recursion_limit = DUK_USE_NATIVE_CALL_RECLIMIT; | |
763 | ||
764 | /* XXX: use the pointer as a seed for now: mix in time at least */ | |
765 | ||
766 | /* The casts through duk_intr_pt is to avoid the following GCC warning: | |
767 | * | |
768 | * warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] | |
769 | * | |
770 | * This still generates a /Wp64 warning on VS2010 when compiling for x86. | |
771 | */ | |
772 | res->hash_seed = (duk_uint32_t) (duk_intptr_t) res; | |
773 | res->rnd_state = (duk_uint32_t) (duk_intptr_t) res; | |
774 | ||
775 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
776 | res->lj.jmpbuf_ptr = NULL; | |
777 | #endif | |
778 | DUK_ASSERT(res->lj.type == DUK_LJ_TYPE_UNKNOWN); /* zero */ | |
779 | ||
780 | DUK_TVAL_SET_UNDEFINED_UNUSED(&res->lj.value1); | |
781 | DUK_TVAL_SET_UNDEFINED_UNUSED(&res->lj.value2); | |
782 | ||
783 | #if (DUK_STRTAB_INITIAL_SIZE < DUK_UTIL_MIN_HASH_PRIME) | |
784 | #error initial heap stringtable size is defined incorrectly | |
785 | #endif | |
786 | ||
787 | /* | |
788 | * Init stringtable: fixed variant | |
789 | */ | |
790 | ||
791 | #if defined(DUK_USE_STRTAB_CHAIN) | |
792 | DUK_MEMZERO(res->strtable, sizeof(duk_strtab_entry) * DUK_STRTAB_CHAIN_SIZE); | |
793 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
794 | { | |
795 | duk_small_uint_t i; | |
796 | for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { | |
797 | #if defined(DUK_USE_HEAPPTR16) | |
798 | res->strtable[i].u.str16 = res->heapptr_null16; | |
799 | #else | |
800 | res->strtable[i].u.str = NULL; | |
801 | #endif | |
802 | } | |
803 | } | |
804 | #endif /* DUK_USE_EXPLICIT_NULL_INIT */ | |
805 | #endif /* DUK_USE_STRTAB_CHAIN */ | |
806 | ||
807 | /* | |
808 | * Init stringtable: probe variant | |
809 | */ | |
810 | ||
811 | #if defined(DUK_USE_STRTAB_PROBE) | |
812 | #if defined(DUK_USE_HEAPPTR16) | |
813 | res->strtable16 = (duk_uint16_t *) alloc_func(heap_udata, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); | |
814 | if (!res->strtable16) { | |
815 | goto error; | |
816 | } | |
817 | #else /* DUK_USE_HEAPPTR16 */ | |
818 | res->strtable = (duk_hstring **) alloc_func(heap_udata, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); | |
819 | if (!res->strtable) { | |
820 | goto error; | |
821 | } | |
822 | #endif /* DUK_USE_HEAPPTR16 */ | |
823 | res->st_size = DUK_STRTAB_INITIAL_SIZE; | |
824 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
825 | { | |
826 | duk_small_uint_t i; | |
827 | DUK_ASSERT(res->st_size == DUK_STRTAB_INITIAL_SIZE); | |
828 | for (i = 0; i < DUK_STRTAB_INITIAL_SIZE; i++) { | |
829 | #if defined(DUK_USE_HEAPPTR16) | |
830 | res->strtable16[i] = res->heapptr_null16; | |
831 | #else | |
832 | res->strtable[i] = NULL; | |
833 | #endif | |
834 | } | |
835 | } | |
836 | #else /* DUK_USE_EXPLICIT_NULL_INIT */ | |
837 | #if defined(DUK_USE_HEAPPTR16) | |
838 | DUK_MEMZERO(res->strtable16, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); | |
839 | #else | |
840 | DUK_MEMZERO(res->strtable, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); | |
841 | #endif | |
842 | #endif /* DUK_USE_EXPLICIT_NULL_INIT */ | |
843 | #endif /* DUK_USE_STRTAB_PROBE */ | |
844 | ||
845 | /* | |
846 | * Init stringcache | |
847 | */ | |
848 | ||
849 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
850 | { | |
851 | duk_small_uint_t i; | |
852 | for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { | |
853 | res->strcache[i].h = NULL; | |
854 | } | |
855 | } | |
856 | #endif | |
857 | ||
858 | /* XXX: error handling is incomplete. It would be cleanest if | |
859 | * there was a setjmp catchpoint, so that all init code could | |
860 | * freely throw errors. If that were the case, the return code | |
861 | * passing here could be removed. | |
862 | */ | |
863 | ||
864 | /* | |
865 | * Init built-in strings | |
866 | */ | |
867 | ||
868 | DUK_DD(DUK_DDPRINT("HEAP: INIT STRINGS")); | |
869 | if (!duk__init_heap_strings(res)) { | |
870 | goto error; | |
871 | } | |
872 | ||
873 | /* | |
874 | * Init the heap thread | |
875 | */ | |
876 | ||
877 | DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP THREAD")); | |
878 | if (!duk__init_heap_thread(res)) { | |
879 | goto error; | |
880 | } | |
881 | ||
882 | /* | |
883 | * Init the heap object | |
884 | */ | |
885 | ||
886 | DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP OBJECT")); | |
887 | DUK_ASSERT(res->heap_thread != NULL); | |
888 | res->heap_object = duk_hobject_alloc(res, DUK_HOBJECT_FLAG_EXTENSIBLE | | |
889 | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT)); | |
890 | if (!res->heap_object) { | |
891 | goto error; | |
892 | } | |
893 | DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object); | |
894 | ||
895 | /* | |
896 | * All done | |
897 | */ | |
898 | ||
899 | DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res)); | |
900 | return res; | |
901 | ||
902 | error: | |
903 | DUK_D(DUK_DPRINT("heap allocation failed")); | |
904 | ||
905 | if (res) { | |
906 | /* assumes that allocated pointers and alloc funcs are valid | |
907 | * if res exists | |
908 | */ | |
909 | DUK_ASSERT(res->alloc_func != NULL); | |
910 | DUK_ASSERT(res->realloc_func != NULL); | |
911 | DUK_ASSERT(res->free_func != NULL); | |
912 | duk_heap_free(res); | |
913 | } | |
914 | return NULL; | |
915 | } |