static duk_hstring *duk__find_matching_string(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_uint32_t i; duk_uint32_t step; DUK_ASSERT(size > 0); i = DUK__HASH_INITIAL(strhash, size); step = DUK__HASH_PROBE_STEP(strhash); for (;;) { duk_hstring *e; e = entries[i]; if (!e) { return NULL; } if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) { if (DUK_MEMCMP(str, DUK_HSTRING_GET_DATA(e), blen) == 0) { DUK_DDD(DUK_DDDPRINT("find matching hit: %d (step %d, size %d)", i, step, size)); return e; } } DUK_DDD(DUK_DDDPRINT("find matching miss: %d (step %d, size %d)", i, step, size)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size)); } DUK_UNREACHABLE(); }
static void duk__remove_matching_hstring(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) { duk_uint32_t i; duk_uint32_t step; DUK_ASSERT(size > 0); i = DUK__HASH_INITIAL(h->hash, size); step = DUK__HASH_PROBE_STEP(h->hash); for (;;) { duk_hstring *e; e = entries[i]; if (!e) { DUK_UNREACHABLE(); break; } if (e == h) { /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("free matching hit: %d", i)); entries[i] = DUK__DELETED_MARKER(heap); break; } DUK_DDD(DUK_DDDPRINT("free matching miss: %d", i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(h->hash, size)); } }
/* Prepare value stack for a method call through an object property. * May currently throw an error e.g. when getting the property. */ DUK_LOCAL void duk__call_prop_prep_stack(duk_context *ctx, duk_idx_t normalized_obj_idx, duk_idx_t nargs) { DUK_ASSERT_CTX_VALID(ctx); DUK_DDD(DUK_DDDPRINT("duk__call_prop_prep_stack, normalized_obj_idx=%ld, nargs=%ld, stacktop=%ld", (long) normalized_obj_idx, (long) nargs, (long) duk_get_top(ctx))); /* [... key arg1 ... argN] */ /* duplicate key */ duk_dup(ctx, -nargs - 1); /* Note: -nargs alone would fail for nargs == 0, this is OK */ duk_get_prop(ctx, normalized_obj_idx); DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(ctx, -1))); /* [... key arg1 ... argN func] */ duk_replace(ctx, -nargs - 2); /* [... func arg1 ... argN] */ duk_dup(ctx, normalized_obj_idx); duk_insert(ctx, -nargs - 1); /* [... func this arg1 ... argN] */ }
DUK_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk_hstring *h, duk_uint_t pos) { duk_uint32_t boff; const duk_uint8_t *p, *p_start, *p_end; duk_ucodepoint_t cp; /* Caller must check character offset to be inside the string. */ DUK_ASSERT(thr != NULL); DUK_ASSERT(h != NULL); DUK_ASSERT_DISABLE(pos >= 0); /* unsigned */ DUK_ASSERT(pos < (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h)); boff = duk_heap_strcache_offset_char2byte(thr, h, (duk_uint32_t) pos); DUK_DDD(DUK_DDDPRINT("charCodeAt: pos=%ld -> boff=%ld, str=%!O", (long) pos, (long) boff, (duk_heaphdr *) h)); DUK_ASSERT_DISABLE(boff >= 0); DUK_ASSERT(boff < DUK_HSTRING_GET_BYTELEN(h)); p_start = DUK_HSTRING_GET_DATA(h); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h); p = p_start + boff; DUK_DDD(DUK_DDDPRINT("p_start=%p, p_end=%p, p=%p", (void *) p_start, (void *) p_end, (void *) p)); /* This may throw an error though not for valid E5 strings. */ cp = duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); return cp; }
static void duk__insert_hstring(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { duk_uint32_t i; duk_uint32_t step; DUK_ASSERT(size > 0); i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size); step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(h)); for (;;) { duk_hstring *e; e = entries[i]; if (e == NULL) { DUK_DDD(DUK_DDDPRINT("insert hit (null): %d", i)); entries[i] = h; (*p_used)++; break; } else if (e == DUK__DELETED_MARKER(heap)) { /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %d", i)); entries[i] = h; break; } DUK_DDD(DUK_DDDPRINT("insert miss: %d", i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size)); } }
DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, const char *str) { struct tm tm; time_t t; char buf[DUK__STRPTIME_BUF_SIZE]; /* copy to buffer with spare to avoid Valgrind gripes from strptime */ DUK_ASSERT(str != NULL); DUK_MEMZERO(buf, sizeof(buf)); /* valgrind whine without this */ DUK_SNPRINTF(buf, sizeof(buf), "%s", (const char *) str); buf[sizeof(buf) - 1] = (char) 0; DUK_DDD(DUK_DDDPRINT("parsing: '%s'", (const char *) buf)); DUK_MEMZERO(&tm, sizeof(tm)); if (strptime((const char *) buf, "%c", &tm) != NULL) { DUK_DDD(DUK_DDDPRINT("before mktime: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld," "wday:%ld,yday:%ld,isdst:%ld}", (long) tm.tm_sec, (long) tm.tm_min, (long) tm.tm_hour, (long) tm.tm_mday, (long) tm.tm_mon, (long) tm.tm_year, (long) tm.tm_wday, (long) tm.tm_yday, (long) tm.tm_isdst)); tm.tm_isdst = -1; /* negative: dst info not available */ t = mktime(&tm); DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t)); if (t >= 0) { duk_push_number(ctx, ((duk_double_t) t) * 1000.0); return 1; } } return 0; }
DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) { duk_context *ctx; /* Append a "(line NNN)" to the "message" property of any error * thrown during compilation. Usually compilation errors are * SyntaxErrors but they can also be out-of-memory errors and * the like. */ /* [ ... error ] */ ctx = (duk_context *) thr; DUK_ASSERT(duk_is_object(ctx, -1)); if (!(thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL)) { return; } DUK_DDD(DUK_DDDPRINT("compile error, before adding line info: %!T", (duk_tval *) duk_get_tval(ctx, -1))); if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_MESSAGE)) { duk_push_sprintf(ctx, " (line %ld)", (long) thr->compile_ctx->curr_token.start_line); duk_concat(ctx, 2); duk_put_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE); } else { duk_pop(ctx); } DUK_DDD(DUK_DDDPRINT("compile error, after adding line info: %!T", (duk_tval *) duk_get_tval(ctx, -1))); }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { (void) duk_push_this_coercible_to_object(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_JOIN); /* [ ... this func ] */ if (!duk_is_callable(ctx, -1)) { /* Fall back to the initial (original) Object.toString(). We don't * currently have pointers to the built-in functions, only the top * level global objects (like "Array") so this is now done in a bit * of a hacky manner. It would be cleaner to push the (original) * function and use duk_call_method(). */ /* XXX: 'this' will be ToObject() coerced twice, which is incorrect * but should have no visible side effects. */ DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString")); duk_set_top(ctx, 0); return duk_bi_object_prototype_to_string(ctx); /* has access to 'this' binding */ } /* [ ... this func ] */ duk_insert(ctx, -2); /* [ ... func this ] */ DUK_DDD(DUK_DDDPRINT("calling: func=%!iT, this=%!iT", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); duk_call_method(ctx, 0); return 1; }
static duk_double_t duk__push_this_number_plain(duk_context *ctx) { duk_hobject *h; /* Number built-in accepts a plain number or a Number object (whose * internal value is operated on). Other types cause TypeError. */ duk_push_this(ctx); if (duk_is_number(ctx, -1)) { DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(ctx, -1))); goto done; } h = duk_get_hobject(ctx, -1); if (!h || (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_NUMBER)) { DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(ctx, -1))); DUK_ERROR((duk_hthread *) ctx, DUK_ERR_TYPE_ERROR, "expected a number"); } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE); DUK_ASSERT(duk_is_number(ctx, -1)); DUK_DDD(DUK_DDDPRINT("number object: %!T, internal value: %!T", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); duk_remove(ctx, -2); done: return duk_get_number(ctx, -1); }
DUK_LOCAL duk_ret_t duk__finalize_helper(duk_context *ctx, void *udata) { duk_hthread *thr; DUK_ASSERT(ctx != NULL); thr = (duk_hthread *) ctx; DUK_UNREF(udata); DUK_DDD(DUK_DDDPRINT("protected finalization helper running")); /* [... obj] */ /* XXX: Finalizer lookup should traverse the prototype chain (to allow * inherited finalizers) but should not invoke accessors or proxy object * behavior. At the moment this lookup will invoke proxy behavior, so * caller must ensure that this function is not called if the target is * a Proxy. */ duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_FINALIZER); /* -> [... obj finalizer] */ if (!duk_is_callable(ctx, -1)) { DUK_DDD(DUK_DDDPRINT("-> no finalizer or finalizer not callable")); return 0; } duk_dup_m2(ctx); duk_push_boolean(ctx, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap)); DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling finalizer")); duk_call(ctx, 2); /* [ ... obj finalizer obj heapDestruct ] -> [ ... obj retval ] */ DUK_DDD(DUK_DDDPRINT("finalizer finished successfully")); return 0; /* Note: we rely on duk_safe_call() to fix up the stack for the caller, * so we don't need to pop stuff here. There is no return value; * caller determines rescued status based on object refcount. */ }
DUK_INTERNAL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj) { duk_context *ctx = (duk_context *) thr; duk_ret_t rc; #if defined(DUK_USE_ASSERTIONS) duk_idx_t entry_top; #endif DUK_DDD(DUK_DDDPRINT("running object finalizer for object: %p", (void *) obj)); DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT_VALSTACK_SPACE(thr, 1); #if defined(DUK_USE_ASSERTIONS) entry_top = duk_get_top(ctx); #endif /* * Get and call the finalizer. All of this must be wrapped * in a protected call, because even getting the finalizer * may trigger an error (getter may throw one, for instance). */ DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) { DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj)); return; } DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj); /* ensure never re-entered until rescue cycle complete */ if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) { /* This shouldn't happen; call sites should avoid looking up * _Finalizer "through" a Proxy, but ignore if we come here * with a Proxy to avoid finalizer re-entry. */ DUK_D(DUK_DPRINT("object is a proxy, skip finalizer call")); return; } /* XXX: use a NULL error handler for the finalizer call? */ DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling wrapped finalize helper")); duk_push_hobject(ctx, obj); /* this also increases refcount by one */ rc = duk_safe_call(ctx, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/); /* -> [... obj retval/error] */ DUK_ASSERT_TOP(ctx, entry_top + 2); /* duk_safe_call discipline */ if (rc != DUK_EXEC_SUCCESS) { /* Note: we ask for one return value from duk_safe_call to get this * error debugging here. */ DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", (void *) obj, (duk_tval *) duk_get_tval(ctx, -1))); } duk_pop_2(ctx); /* -> [...] */ DUK_ASSERT_TOP(ctx, entry_top); }
DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) { duk_context *ctx = (duk_context *) thr; duk_hobject *obj; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); DUK_ASSERT(ctx != NULL); /* [ ... error ] */ /* * Criteria for augmenting: * * - augmentation enabled in build (naturally) * - error value internal prototype chain contains the built-in * Error prototype object (i.e. 'val instanceof Error') * * Additional criteria for built-in augmenting: * * - error value is an extensible object */ obj = duk_get_hobject(ctx, -1); if (!obj) { DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment")); return; } if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) { /* If the value has a prototype loop, it's critical not to * throw here. Instead, assume the value is not to be * augmented. */ DUK_DDD(DUK_DDDPRINT("value is not an error instance, skip both built-in and user augment")); return; } if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) { DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment")); duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, noblame_fileline, obj); } else { DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment")); } /* [ ... error ] */ #if defined(DUK_USE_ERRCREATE) duk__err_augment_user(thr, DUK_STRIDX_ERR_CREATE); #endif }
/* Debug print which visualizes the qsort partitioning process. */ DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int_t hi, duk_int_t pivot) { char buf[4096]; char *ptr = buf; duk_int_t i, n; n = (duk_int_t) duk_get_length(ctx, 1); if (n > 4000) { n = 4000; } *ptr++ = '['; for (i = 0; i < n; i++) { if (i == pivot) { *ptr++ = '|'; } else if (i == lo) { *ptr++ = '<'; } else if (i == hi) { *ptr++ = '>'; } else if (i >= lo && i <= hi) { *ptr++ = '-'; } else { *ptr++ = ' '; } } *ptr++ = ']'; *ptr++ = '\0'; DUK_DDD(DUK_DDDPRINT("%s (lo=%ld, hi=%ld, pivot=%ld)", (const char *) buf, (long) lo, (long) hi, (long) pivot)); }
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) { duk_idx_t nargs; /* Step 1 is not necessary because duk_call_method() will take * care of it. */ /* vararg function, thisArg needs special handling */ nargs = duk_get_top(ctx); /* = 1 + arg count */ if (nargs == 0) { duk_push_undefined(ctx); nargs++; } DUK_ASSERT(nargs >= 1); /* [ thisArg arg1 ... argN ] */ duk_push_this(ctx); /* 'func' in the algorithm */ duk_insert(ctx, 0); /* [ func thisArg arg1 ... argN ] */ DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%ld, top=%ld", (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 1), (long) (nargs - 1), (long) duk_get_top(ctx))); duk_call_method(ctx, nargs - 1); return 1; }
/* Debug print which visualizes the qsort partitioning process. */ static void duk__debuglog_qsort_state(duk_context *ctx, int lo, int hi, int pivot) { char buf[4096]; char *ptr = buf; int i, n; n = duk_get_length(ctx, 1); if (n > 4000) { n = 4000; } *ptr++ = '['; for (i = 0; i < n; i++) { if (i == pivot) { *ptr++ = '|'; } else if (i == lo) { *ptr++ = '<'; } else if (i == hi) { *ptr++ = '>'; } else if (i >= lo && i <= hi) { *ptr++ = '-'; } else { *ptr++ = ' '; } } *ptr++ = ']'; *ptr++ = '\0'; DUK_DDD(DUK_DDDPRINT("%s (lo=%d, hi=%d, pivot=%d)", buf, lo, hi, pivot)); }
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_int_t pos; duk_hstring *h; duk_bool_t clamped; /* XXX: faster implementation */ DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(ctx, 0))); h = duk_push_this_coercible_to_string(ctx); DUK_ASSERT(h != NULL); pos = duk_to_int_clamped_raw(ctx, 0 /*index*/, 0 /*min(incl)*/, DUK_HSTRING_GET_CHARLEN(h) - 1 /*max(incl)*/, &clamped /*out_clamped*/); if (clamped) { duk_push_number(ctx, DUK_DOUBLE_NAN); return 1; } duk_push_u32(ctx, (duk_uint32_t) duk_hstring_char_code_at_raw(thr, h, pos)); return 1; }
DUK_LOCAL void duk__finalize_refcounts(duk_heap *heap) { duk_hthread *thr; duk_heaphdr *hdr; thr = duk__get_temp_hthread(heap); DUK_ASSERT(thr != NULL); DUK_DD(DUK_DDPRINT("duk__finalize_refcounts: heap=%p, hthread=%p", (void *) heap, (void *) thr)); hdr = heap->heap_allocated; while (hdr) { if (!DUK_HEAPHDR_HAS_REACHABLE(hdr)) { /* * Unreachable object about to be swept. Finalize target refcounts * (objects which the unreachable object points to) without doing * refzero processing. Recursive decrefs are also prevented when * refzero processing is disabled. * * Value cannot be a finalizable object, as they have been made * temporarily reachable for this round. */ DUK_DDD(DUK_DDDPRINT("unreachable object, refcount finalize before sweeping: %p", (void *) hdr)); duk_heaphdr_refcount_finalize(thr, hdr); } hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); } }
/* recursion tracking happens here only */ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { DUK_DDD(DUK_DDDPRINT("duk__mark_heaphdr %p, type %ld", (void *) h, (h != NULL ? (long) DUK_HEAPHDR_GET_TYPE(h) : (long) -1))); if (!h) { return; } #if defined(DUK_USE_ROM_OBJECTS) if (DUK_HEAPHDR_HAS_READONLY(h)) { DUK_DDD(DUK_DDDPRINT("readonly object %p, skip", (void *) h)); return; } #endif if (DUK_HEAPHDR_HAS_REACHABLE(h)) { DUK_DDD(DUK_DDDPRINT("already marked reachable, skip")); return; } DUK_HEAPHDR_SET_REACHABLE(h); if (heap->mark_and_sweep_recursion_depth >= DUK_USE_MARK_AND_SWEEP_RECLIMIT) { /* log this with a normal debug level because this should be relatively rare */ DUK_D(DUK_DPRINT("mark-and-sweep recursion limit reached, marking as temproot: %p", (void *) h)); DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap); DUK_HEAPHDR_SET_TEMPROOT(h); return; } heap->mark_and_sweep_recursion_depth++; switch (DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: duk__mark_hstring(heap, (duk_hstring *) h); break; case DUK_HTYPE_OBJECT: duk__mark_hobject(heap, (duk_hobject *) h); break; case DUK_HTYPE_BUFFER: /* nothing to mark */ break; default: DUK_D(DUK_DPRINT("attempt to mark heaphdr %p with invalid htype %ld", (void *) h, (long) DUK_HEAPHDR_GET_TYPE(h))); DUK_UNREACHABLE(); } heap->mark_and_sweep_recursion_depth--; }
static void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) { duk_hstring *h; duk_uint_fast32_t i; #ifdef DUK_USE_DEBUG duk_size_t count_free = 0; #endif duk_size_t count_keep = 0; DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); for (i = 0; i < heap->st_size; i++) { h = heap->st[i]; if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { continue; } else if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); count_keep++; continue; } #ifdef DUK_USE_DEBUG count_free++; #endif #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen for unreachable strings, * because we refcount finalize all unreachable objects which * should have decreased unreachable string refcounts to zero * (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); #endif DUK_DDD(DUK_DDDPRINT("sweep string, not reachable: %p", (void *) h)); /* deal with weak references first */ duk_heap_strcache_string_remove(heap, (duk_hstring *) h); /* remove the string (mark DELETED), could also call * duk_heap_string_remove() but that would be slow and * pointless because we already know the slot. */ heap->st[i] = DUK_STRTAB_DELETED_MARKER(heap); /* then free */ #if 1 DUK_FREE(heap, (duk_heaphdr *) h); /* no inner refs/allocs, just free directly */ #else duk_heap_free_heaphdr_raw(heap, (duk_heaphdr *) h); /* this would be OK but unnecessary */ #endif } #ifdef DUK_USE_DEBUG DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %d freed, %d kept", (int) count_free, (int) count_keep)); #endif *out_count_keep = count_keep; }
static duk_hstring *duk__alloc_init_hstring(duk_heap *heap, duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_hstring *res = NULL; duk_uint8_t *data; duk_size_t alloc_size; duk_uarridx_t dummy; /* NUL terminate for convenient C access */ alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1); res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); if (!res) { goto error; } DUK_MEMZERO(res, sizeof(duk_hstring)); #ifdef DUK_USE_EXPLICIT_NULL_INIT DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) { DUK_HSTRING_SET_ARRIDX(res); } /* All strings beginning with 0xff are treated as "internal", * even strings interned by the user. This allows user code to * create internal properties too, and makes behavior consistent * in case user code happens to use a string also used by Duktape * (such as string has already been interned and has the 'internal' * flag set). */ if (blen > 0 && str[0] == (duk_uint8_t) 0xff) { DUK_HSTRING_SET_INTERNAL(res); } res->hash = strhash; res->blen = blen; res->clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); /* clen <= blen */ data = (duk_uint8_t *) (res + 1); DUK_MEMCPY(data, str, blen); data[blen] = (duk_uint8_t) 0; DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld", (unsigned long) DUK_HSTRING_GET_HASH(res), (long) DUK_HSTRING_GET_BYTELEN(res), (long) DUK_HSTRING_GET_CHARLEN(res), (long) DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0)); return res; error: DUK_FREE(heap, res); return NULL; }
DUK_LOCAL void duk__run_object_finalizers(duk_heap *heap, duk_small_uint_t flags) { duk_heaphdr *curr; duk_heaphdr *next; #if defined(DUK_USE_DEBUG) duk_size_t count = 0; #endif duk_hthread *thr; DUK_DD(DUK_DDPRINT("duk__run_object_finalizers: %p", (void *) heap)); thr = duk__get_temp_hthread(heap); DUK_ASSERT(thr != NULL); curr = heap->finalize_list; while (curr) { DUK_DDD(DUK_DDDPRINT("mark-and-sweep finalize: %p", (void *) curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* only objects have finalizers */ DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); /* flags have been already cleared */ DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); /* No finalizers for ROM objects */ if (DUK_LIKELY((flags & DUK_MS_FLAG_SKIP_FINALIZERS) == 0)) { /* Run the finalizer, duk_hobject_run_finalizer() sets FINALIZED. * Next mark-and-sweep will collect the object unless it has * become reachable (i.e. rescued). FINALIZED prevents the * finalizer from being executed again before that. */ duk_hobject_run_finalizer(thr, (duk_hobject *) curr); /* must never longjmp */ DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(curr)); } else { /* Used during heap destruction: don't actually run finalizers * because we're heading into forced finalization. Instead, * queue finalizable objects back to the heap_allocated list. */ DUK_D(DUK_DPRINT("skip finalizers flag set, queue object to heap_allocated without finalizing")); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); } /* queue back to heap_allocated */ next = DUK_HEAPHDR_GET_NEXT(heap, curr); DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr); curr = next; #if defined(DUK_USE_DEBUG) count++; #endif } /* finalize_list will always be processed completely */ heap->finalize_list = NULL; #if defined(DUK_USE_DEBUG) DUK_D(DUK_DPRINT("mark-and-sweep finalize objects: %ld finalizers called", (long) count)); #endif }
DUK_LOCAL void duk__mark_tval(duk_heap *heap, duk_tval *tv) { DUK_DDD(DUK_DDDPRINT("duk__mark_tval %p", (void *) tv)); if (!tv) { return; } if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { duk__mark_heaphdr(heap, DUK_TVAL_GET_HEAPHDR(tv)); } }
DUK_LOCAL void duk__mark_hstring(duk_heap *heap, duk_hstring *h) { DUK_UNREF(heap); DUK_UNREF(h); DUK_DDD(DUK_DDDPRINT("duk__mark_hstring: %p", (void *) h)); DUK_ASSERT(h); /* nothing to process */ }
DUK_INTERNAL void duk_free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); if (DUK_HBUFFER_HAS_DYNAMIC(h)) { duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(g))); DUK_FREE(heap, DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(g)); } }
static void duk__free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); if (DUK_HBUFFER_HAS_DYNAMIC(h)) { duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", g->curr_alloc)); DUK_FREE(heap, g->curr_alloc); } }
static void duk__free_markandsweep_finalize_list(duk_heap *heap) { duk_heaphdr *curr; duk_heaphdr *next; curr = heap->finalize_list; while (curr) { DUK_DDD(DUK_DDDPRINT("FINALFREE (finalize_list): %!iO", curr)); next = DUK_HEAPHDR_GET_NEXT(curr); duk_heap_free_heaphdr_raw(heap, curr); curr = next; } }
static void duk__free_refzero_list(duk_heap *heap) { duk_heaphdr *curr; duk_heaphdr *next; curr = heap->refzero_list; while (curr) { DUK_DDD(DUK_DDDPRINT("FINALFREE (refzero_list): %!iO", curr)); next = DUK_HEAPHDR_GET_NEXT(curr); duk_heap_free_heaphdr_raw(heap, curr); curr = next; } }
DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start, duk_size_t *p_count_check, duk_size_t *p_count_compact, duk_size_t *p_count_bytes_saved) { #else DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start) { #endif duk_heaphdr *curr; #if defined(DUK_USE_DEBUG) duk_size_t old_size, new_size; #endif duk_hobject *obj; DUK_UNREF(heap); curr = start; while (curr) { DUK_DDD(DUK_DDDPRINT("mark-and-sweep compact: %p", (void *) curr)); if (DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_OBJECT) { goto next; } obj = (duk_hobject *) curr; #if defined(DUK_USE_DEBUG) old_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj), DUK_HOBJECT_GET_ASIZE(obj), DUK_HOBJECT_GET_HSIZE(obj)); #endif DUK_DD(DUK_DDPRINT("compact object: %p", (void *) obj)); duk_push_hobject((duk_context *) thr, obj); /* XXX: disable error handlers for duration of compaction? */ duk_safe_call((duk_context *) thr, duk__protected_compact_object, NULL, 1, 0); #if defined(DUK_USE_DEBUG) new_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj), DUK_HOBJECT_GET_ASIZE(obj), DUK_HOBJECT_GET_HSIZE(obj)); #endif #if defined(DUK_USE_DEBUG) (*p_count_compact)++; (*p_count_bytes_saved) += (duk_size_t) (old_size - new_size); #endif next: curr = DUK_HEAPHDR_GET_NEXT(heap, curr); #if defined(DUK_USE_DEBUG) (*p_count_check)++; #endif } }
DUK_INTERNAL void duk_free_hstring_inner(duk_heap *heap, duk_hstring *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); DUK_UNREF(heap); DUK_UNREF(h); #if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_FREE) if (DUK_HSTRING_HAS_EXTDATA(h)) { DUK_DDD(DUK_DDDPRINT("free extstr: hstring %!O, extdata: %p", h, DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h))); DUK_USE_EXTSTR_FREE((const void *) DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h)); } #endif }
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx) { duk_idx_t len; duk_idx_t i; DUK_ASSERT_TOP(ctx, 2); /* not a vararg function */ duk_push_this(ctx); if (!duk_is_callable(ctx, -1)) { DUK_DDD(DUK_DDDPRINT("func is not callable")); goto type_error; } duk_insert(ctx, 0); DUK_ASSERT_TOP(ctx, 3); DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT", (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 1), (duk_tval *) duk_get_tval(ctx, 2))); /* [ func thisArg argArray ] */ if (duk_is_null_or_undefined(ctx, 2)) { DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args")); len = 0; } else if (!duk_is_object(ctx, 2)) { goto type_error; } else { DUK_DDD(DUK_DDDPRINT("argArray is an object")); /* XXX: make this an internal helper */ duk_get_prop_stridx(ctx, 2, DUK_STRIDX_LENGTH); len = (duk_idx_t) duk_to_uint32(ctx, -1); /* ToUint32() coercion required */ duk_pop(ctx); duk_require_stack(ctx, len); DUK_DDD(DUK_DDDPRINT("argArray length is %ld", (long) len)); for (i = 0; i < len; i++) { duk_get_prop_index(ctx, 2, i); } } duk_remove(ctx, 2); DUK_ASSERT_TOP(ctx, 2 + len); /* [ func thisArg arg1 ... argN ] */ DUK_DDD(DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%ld", (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 1), (long) len)); duk_call_method(ctx, len); return 1; type_error: return DUK_RET_TYPE_ERROR; }