/* Overflow, relevant mainly when listlen is 16 bits. */ return 1; /* fail */ } new_lst = (duk_hstring **) DUK_REALLOC(heap, e->u.strlist, sizeof(duk_hstring *) * (e->listlen + 1)); if (new_lst == NULL) { return 1; /* fail */ } new_lst[e->listlen++] = h; e->u.strlist = new_lst; } return 0; } #endif /* DUK_USE_HEAPPTR16 */ #if defined(DUK_USE_HEAPPTR16) DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_uint16_t *lst; duk_size_t i, n; duk_uint16_t null16 = heap->heapptr_null16; DUK_ASSERT(heap != NULL); slotidx = strhash % DUK_STRTAB_CHAIN_SIZE; DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str16 != null16) { duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); DUK_ASSERT(h != NULL); if (DUK_HSTRING_GET_BYTELEN(h) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { return h; } } } else { DUK_ASSERT(e->u.strlist16 != null16); lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); DUK_ASSERT(lst != NULL); for (i = 0, n = e->listlen; i < n; i++) { if (lst[i] != null16) { duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[i]); DUK_ASSERT(h != NULL); if (DUK_HSTRING_GET_BYTELEN(h) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { return h; } } } } return NULL; } #else /* DUK_USE_HEAPPTR16 */ DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_hstring **lst; duk_size_t i, n; DUK_ASSERT(heap != NULL); slotidx = strhash % DUK_STRTAB_CHAIN_SIZE; DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str != NULL && DUK_HSTRING_GET_BYTELEN(e->u.str) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e->u.str), (size_t) blen) == 0) { return e->u.str; } } else { DUK_ASSERT(e->u.strlist != NULL); lst = e->u.strlist; for (i = 0, n = e->listlen; i < n; i++) { if (lst[i] != NULL && DUK_HSTRING_GET_BYTELEN(lst[i]) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(lst[i]), (size_t) blen) == 0) { return lst[i]; } } } return NULL; }
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; }
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx) { duk_hstring *h1; duk_hstring *h2; duk_size_t h1_len, h2_len, prefix_len; duk_small_int_t ret = 0; duk_small_int_t rc; /* The current implementation of localeCompare() is simply a codepoint * by codepoint comparison, implemented with a simple string compare * because UTF-8 should preserve codepoint ordering (assuming valid * shortest UTF-8 encoding). * * The specification requires that the return value must be related * to the sort order: e.g. negative means that 'this' comes before * 'that' in sort order. We assume an ascending sort order. */ /* XXX: could share code with duk_js_ops.c, duk_js_compare_helper */ h1 = duk_push_this_coercible_to_string(ctx); DUK_ASSERT(h1 != NULL); h2 = duk_to_hstring(ctx, 0); DUK_ASSERT(h2 != NULL); h1_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1); h2_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2); prefix_len = (h1_len <= h2_len ? h1_len : h2_len); /* Zero size compare not an issue with DUK_MEMCMP. */ rc = (duk_small_int_t) DUK_MEMCMP((const char *) DUK_HSTRING_GET_DATA(h1), (const char *) DUK_HSTRING_GET_DATA(h2), prefix_len); if (rc < 0) { ret = -1; goto done; } else if (rc > 0) { ret = 1; goto done; } /* prefix matches, lengths matter now */ if (h1_len > h2_len) { ret = 1; goto done; } else if (h1_len == h2_len) { DUK_ASSERT(ret == 0); goto done; } ret = -1; goto done; done: duk_push_int(ctx, (duk_int_t) ret); return 1; }
DUK_INTERNAL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2) { /* * String comparison (E5 Section 11.8.5, step 4), which * needs to compare codepoint by codepoint. * * However, UTF-8 allows us to use strcmp directly: the shared * prefix will be encoded identically (UTF-8 has unique encoding) * and the first differing character can be compared with a simple * unsigned byte comparison (which strcmp does). * * This will not work properly for non-xutf-8 strings, but this * is not an issue for compliance. */ duk_size_t h1_len, h2_len, prefix_len; duk_small_int_t rc; DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); h1_len = DUK_HSTRING_GET_BYTELEN(h1); h2_len = DUK_HSTRING_GET_BYTELEN(h2); prefix_len = (h1_len <= h2_len ? h1_len : h2_len); /* XXX: this special case can now be removed with DUK_MEMCMP */ /* memcmp() should return zero (equal) for zero length, but avoid * it because there are some platform specific bugs. Don't use * strncmp() because it stops comparing at a NUL. */ if (prefix_len == 0) { rc = 0; } else { rc = DUK_MEMCMP((const char *) DUK_HSTRING_GET_DATA(h1), (const char *) DUK_HSTRING_GET_DATA(h2), prefix_len); } if (rc < 0) { return -1; } else if (rc > 0) { return 1; } /* prefix matches, lengths matter now */ if (h1_len < h2_len) { /* e.g. "x" < "xx" */ return -1; } else if (h1_len > h2_len) { return 1; } return 0; }
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(); }
DUK_LOCAL void duk__print_hstring(duk__dprint_state *st, duk_hstring *h, duk_bool_t quotes) { duk_fixedbuffer *fb = st->fb; const duk_uint8_t *p; const duk_uint8_t *p_end; /* terminal type: no depth check */ if (duk_fb_is_full(fb)) { return; } duk__print_shared_heaphdr_string(st, &h->hdr); if (!h) { duk_fb_put_cstring(fb, "NULL"); return; } p = DUK_HSTRING_GET_DATA(h); p_end = p + DUK_HSTRING_GET_BYTELEN(h); if (p_end > p && p[0] == DUK_ASC_UNDERSCORE) { /* if property key begins with underscore, encode it with * forced quotes (e.g. "_Foo") to distinguish it from encoded * internal properties (e.g. \xffBar -> _Bar). */ quotes = 1; } if (quotes) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE); } while (p < p_end) { duk_uint8_t ch = *p++; /* two special escapes: '\' and '"', other printables as is */ if (ch == '\\') { duk_fb_sprintf(fb, "\\\\"); } else if (ch == '"') { duk_fb_sprintf(fb, "\\\""); } else if (ch >= 0x20 && ch <= 0x7e) { duk_fb_put_byte(fb, ch); } else if (ch == 0xff && !quotes) { /* encode \xffBar as _Bar if no quotes are applied, this is for * readable internal keys. */ duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_UNDERSCORE); } else { duk_fb_sprintf(fb, "\\x%02lx", (unsigned long) ch); } } if (quotes) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE); } #if defined(DUK_USE_REFERENCE_COUNTING) /* XXX: limit to quoted strings only, to save keys from being cluttered? */ duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr)); #endif }
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; }
size_t duk_hbuffer_append_hstring(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_hstring *str) { size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(str != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); len = DUK_HSTRING_GET_BYTELEN(str); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), (duk_uint8_t *) DUK_HSTRING_GET_DATA(str), len); return len; }
DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) { switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: return 0; case DUK_TAG_BOOLEAN: return DUK_TVAL_GET_BOOLEAN(tv); case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); return (DUK_HSTRING_GET_BYTELEN(h) > 0 ? 1 : 0); } case DUK_TAG_OBJECT: { return 1; } case DUK_TAG_BUFFER: { /* mimic semantics for strings */ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (DUK_HBUFFER_GET_SIZE(h) > 0 ? 1 : 0); } case DUK_TAG_POINTER: { void *p = DUK_TVAL_GET_POINTER(tv); return (p != NULL ? 1 : 0); } case DUK_TAG_LIGHTFUNC: { return 1; } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: if (DUK_TVAL_GET_FASTINT(tv) != 0) { return 1; } else { return 0; } #endif default: { /* number */ duk_double_t d; int c; DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); d = DUK_TVAL_GET_DOUBLE(tv); c = DUK_FPCLASSIFY((double) d); if (c == DUK_FP_ZERO || c == DUK_FP_NAN) { return 0; } else { return 1; } } } DUK_UNREACHABLE(); }
DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_uint16_t *lst; duk_size_t i, n; duk_uint16_t null16 = heap->heapptr_null16; DUK_ASSERT(heap != NULL); slotidx = strhash % DUK_STRTAB_CHAIN_SIZE; DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str16 != null16) { duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); DUK_ASSERT(h != NULL); if (DUK_HSTRING_GET_BYTELEN(h) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { return h; } } } else { DUK_ASSERT(e->u.strlist16 != null16); lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); DUK_ASSERT(lst != NULL); for (i = 0, n = e->listlen; i < n; i++) { if (lst[i] != null16) { duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[i]); DUK_ASSERT(h != NULL); if (DUK_HSTRING_GET_BYTELEN(h) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { return h; } } } } return NULL; }
DUK_INTERNAL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2) { /* * String comparison (E5 Section 11.8.5, step 4), which * needs to compare codepoint by codepoint. * * However, UTF-8 allows us to use strcmp directly: the shared * prefix will be encoded identically (UTF-8 has unique encoding) * and the first differing character can be compared with a simple * unsigned byte comparison (which strcmp does). * * This will not work properly for non-xutf-8 strings, but this * is not an issue for compliance. */ DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); return duk_js_data_compare((const duk_uint8_t *) DUK_HSTRING_GET_DATA(h1), (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h2), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2)); }
DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_hobject *h; duk_hstring *h_bc; duk_small_int_t re_flags; /* [ ... escape_source bytecode ] */ h_bc = duk_get_hstring(ctx, -1); DUK_ASSERT(h_bc != NULL); DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h_bc) >= 1); /* always at least the header */ DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h_bc) >= 1); DUK_ASSERT((duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0] < 0x80); /* flags always encodes to 1 byte */ re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0]; /* [ ... escaped_source bytecode ] */ duk_push_object(ctx); h = duk_get_hobject(ctx, -1); DUK_ASSERT(h != NULL); duk_insert(ctx, -3); /* [ ... regexp_object escaped_source bytecode ] */ DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]); duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE); /* [ ... regexp_object escaped_source ] */ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_SOURCE, DUK_PROPDESC_FLAGS_NONE); /* [ ... regexp_object ] */ duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_GLOBAL)); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_GLOBAL, DUK_PROPDESC_FLAGS_NONE); duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_IGNORE_CASE)); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_IGNORE_CASE, DUK_PROPDESC_FLAGS_NONE); duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_MULTILINE)); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MULTILINE, DUK_PROPDESC_FLAGS_NONE); duk_push_int(ctx, 0); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W); /* [ ... regexp_object ] */ }
/* Called by duk_hstring.h macros */ DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h) { duk_uarridx_t res; duk_small_int_t rc; if (!DUK_HSTRING_HAS_ARRIDX(h)) { return DUK_HSTRING_NO_ARRAY_INDEX; } rc = duk_js_to_arrayindex_raw_string(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h), &res); DUK_UNREF(rc); DUK_ASSERT(rc != 0); return res; }
DUK_LOCAL duk_uint8_t *duk__dump_hstring_raw(duk_uint8_t *p, duk_hstring *h) { duk_size_t len; duk_uint32_t tmp32; DUK_ASSERT(h != NULL); len = DUK_HSTRING_GET_BYTELEN(h); DUK_ASSERT(len <= 0xffffffffUL); /* string limits */ tmp32 = (duk_uint32_t) len; DUK_RAW_WRITE_U32_BE(p, tmp32); DUK_MEMCPY((void *) p, (const void *) DUK_HSTRING_GET_DATA(h), len); p += len; return p; }
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_uint32_t alloc_size; duk_uint32_t dummy; /* NUL terminate for convenient C access */ alloc_size = 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); } 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%08x, blen=%d, clen=%d, arridx=%d", DUK_HSTRING_GET_HASH(res), DUK_HSTRING_GET_BYTELEN(res), DUK_HSTRING_GET_CHARLEN(res), DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0)); return res; error: DUK_FREE(heap, res); return NULL; }
DUK_LOCAL duk_hstring *duk__do_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t *out_strhash) { duk_hstring *res; DUK_ASSERT(out_strhash); *out_strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen); #if defined(DUK_USE_ROM_STRINGS) { duk_small_uint_t i; /* XXX: This is VERY inefficient now, and should be e.g. a * binary search or perfect hash, to be fixed. */ for (i = 0; i < (duk_small_uint_t) (sizeof(duk_rom_strings) / sizeof(duk_hstring *)); i++) { duk_hstring *romstr; romstr = (duk_hstring *) duk_rom_strings[i]; if (blen == DUK_HSTRING_GET_BYTELEN(romstr) && DUK_MEMCMP(str, (void *) DUK_HSTRING_GET_DATA(romstr), blen) == 0) { DUK_DD(DUK_DDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx", romstr, (unsigned long) *out_strhash, (unsigned long) DUK_HSTRING_GET_HASH(romstr))); DUK_ASSERT(*out_strhash == DUK_HSTRING_GET_HASH(romstr)); *out_strhash = DUK_HSTRING_GET_HASH(romstr); return romstr; } } } #endif /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_STRTAB_CHAIN) res = duk__find_matching_string_chain(heap, str, blen, *out_strhash); #elif defined(DUK_USE_STRTAB_PROBE) res = duk__find_matching_string_probe(heap, #if defined(DUK_USE_HEAPPTR16) heap->strtable16, #else heap->strtable, #endif heap->st_size, str, blen, *out_strhash); #else #error internal error, invalid strtab options #endif return res; }
DUK_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) { duk_context *ctx = (duk_context *) thr; duk_hstring *h; const duk_uint8_t *p; duk_bufwriter_ctx bw_alloc; duk_bufwriter_ctx *bw; duk_uint8_t *q; duk_size_t i, n; duk_uint_fast8_t c_prev, c; h = duk_get_hstring(ctx, idx_pattern); DUK_ASSERT(h != NULL); p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); n = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h); if (n == 0) { /* return '(?:)' */ duk_push_hstring_stridx(ctx, DUK_STRIDX_ESCAPED_EMPTY_REGEXP); return; } bw = &bw_alloc; DUK_BW_INIT_PUSHBUF(thr, bw, n); q = DUK_BW_GET_PTR(thr, bw); c_prev = (duk_uint_fast8_t) 0; for (i = 0; i < n; i++) { c = p[i]; q = DUK_BW_ENSURE_RAW(thr, bw, 2, q); if (c == (duk_uint_fast8_t) '/' && c_prev != (duk_uint_fast8_t) '\\') { /* Unescaped '/' ANYWHERE in the regexp (in disjunction, * inside a character class, ...) => same escape works. */ *q++ = DUK_ASC_BACKSLASH; } *q++ = (duk_uint8_t) c; c_prev = c; } DUK_BW_SETPTR_AND_COMPACT(thr, bw, q); duk_to_string(ctx, -1); /* -> [ ... escaped_source ] */ }
DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) { duk_hstring *h_str; duk_tval *tv; tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx)); if (tv != NULL && DUK_TVAL_IS_STRING(tv)) { h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); } else { h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr); DUK_ASSERT(h_str != NULL); } DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(h_str), p); p = duk__dump_hstring_raw(p, h_str); return p; }
static duk_uint32_t parse_regexp_flags(duk_hthread *thr, duk_hstring *h) { duk_uint8_t *p; duk_uint8_t *p_end; duk_uint32_t flags = 0; p = DUK_HSTRING_GET_DATA(h); p_end = p + DUK_HSTRING_GET_BYTELEN(h); /* Note: can be safely scanned as bytes (undecoded) */ while (p < p_end) { duk_uint8_t c = *p++; switch ((int) c) { case (int) 'g': { if (flags & DUK_RE_FLAG_GLOBAL) { goto error; } flags |= DUK_RE_FLAG_GLOBAL; break; } case (int) 'i': { if (flags & DUK_RE_FLAG_IGNORE_CASE) { goto error; } flags |= DUK_RE_FLAG_IGNORE_CASE; break; } case (int) 'm': { if (flags & DUK_RE_FLAG_MULTILINE) { goto error; } flags |= DUK_RE_FLAG_MULTILINE; break; } default: { goto error; } } } return flags; error: DUK_ERROR(thr, DUK_ERR_SYNTAX_ERROR, "invalid regexp flags"); return 0; /* never here */ }
DUK_LOCAL duk_uint32_t duk__parse_regexp_flags(duk_hthread *thr, duk_hstring *h) { const duk_uint8_t *p; const duk_uint8_t *p_end; duk_uint32_t flags = 0; p = DUK_HSTRING_GET_DATA(h); p_end = p + DUK_HSTRING_GET_BYTELEN(h); /* Note: can be safely scanned as bytes (undecoded) */ while (p < p_end) { duk_uint8_t c = *p++; switch (c) { case (duk_uint8_t) 'g': { if (flags & DUK_RE_FLAG_GLOBAL) { goto error; } flags |= DUK_RE_FLAG_GLOBAL; break; } case (duk_uint8_t) 'i': { if (flags & DUK_RE_FLAG_IGNORE_CASE) { goto error; } flags |= DUK_RE_FLAG_IGNORE_CASE; break; } case (duk_uint8_t) 'm': { if (flags & DUK_RE_FLAG_MULTILINE) { goto error; } flags |= DUK_RE_FLAG_MULTILINE; break; } default: { goto error; } } } return flags; error: DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_REGEXP_FLAGS); return 0; /* never here */ }
DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { duk_tval *tv; tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_VARMAP(thr)); if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *h; duk_uint_fast32_t i; h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); /* We know _Varmap only has own properties so walk property * table directly. We also know _Varmap is dense and all * values are numbers; assert for these. GC and finalizers * shouldn't affect _Varmap so side effects should be fine. */ for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { duk_hstring *key; duk_tval *tv_val; duk_uint32_t val; key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i); DUK_ASSERT(key != NULL); /* _Varmap is dense */ DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i)); tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i); DUK_ASSERT(tv_val != NULL); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_val)); /* known to be number; in fact an integer */ #if defined(DUK_USE_FASTINT) DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_val)); DUK_ASSERT(DUK_TVAL_GET_FASTINT(tv_val) == (duk_int64_t) DUK_TVAL_GET_FASTINT_U32(tv_val)); /* known to be 32-bit */ val = DUK_TVAL_GET_FASTINT_U32(tv_val); #else val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv_val); #endif DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(key) + 4, p); p = duk__dump_hstring_raw(p, key); DUK_RAW_WRITE_U32_BE(p, val); } } p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Varmap */ return p; }
static void create_escaped_source(duk_hthread *thr, int idx_pattern) { duk_context *ctx = (duk_context *) thr; duk_hstring *h; duk_hbuffer_dynamic *buf; const duk_uint8_t *p; duk_size_t i, n; duk_uint_fast8_t c_prev, c; h = duk_get_hstring(ctx, idx_pattern); DUK_ASSERT(h != NULL); p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); n = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h); if (n == 0) { /* return '(?:)' */ duk_push_hstring_stridx(ctx, DUK_STRIDX_ESCAPED_EMPTY_REGEXP); return; } duk_push_dynamic_buffer(ctx, 0); buf = (duk_hbuffer_dynamic *) duk_get_hbuffer(ctx, -1); DUK_ASSERT(buf != NULL); c_prev = (duk_uint_fast8_t) 0; for (i = 0; i < n; i++) { c = p[i]; if (c == (duk_uint_fast8_t) '/' && c_prev != (duk_uint_fast8_t) '\\') { /* Unescaped '/' ANYWHERE in the regexp (in disjunction, * inside a character class, ...) => same escape works. */ duk_hbuffer_append_byte(thr, buf, (duk_uint8_t) '\\'); } duk_hbuffer_append_byte(thr, buf, (duk_uint8_t) c); c_prev = c; } duk_to_string(ctx, -1); /* -> [ ... escaped_source ] */ }
DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { duk_tval *tv; tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_FORMALS(thr)); if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *h; duk_uint_fast32_t i; h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); /* We know _Formals is dense and all entries will be in the * array part. GC and finalizers shouldn't affect _Formals * so side effects should be fine. */ for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { duk_tval *tv_val; duk_hstring *varname; tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i); DUK_ASSERT(tv_val != NULL); if (DUK_TVAL_IS_STRING(tv_val)) { /* Array is dense and contains only strings, but ASIZE may * be larger than used part and there are UNUSED entries. */ varname = DUK_TVAL_GET_STRING(tv_val); DUK_ASSERT(varname != NULL); DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(varname), p); p = duk__dump_hstring_raw(p, varname); } } } p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Formals */ return p; }
DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { #if defined(DUK_USE_ASSERTIONS) duk_small_uint_t i; #endif /* With ROM-based strings, heap->strs[] and thr->strs[] are omitted * so nothing to initialize for strs[]. */ #if defined(DUK_USE_ASSERTIONS) for (i = 0; i < sizeof(duk_rom_strings) / sizeof(const duk_hstring *); i++) { duk_uint32_t hash; const duk_hstring *h; h = duk_rom_strings[i]; DUK_ASSERT(h != NULL); hash = duk_heap_hashstring(heap, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); DUK_DD(DUK_DDPRINT("duk_rom_strings[%d] -> hash 0x%08lx, computed 0x%08lx", (int) i, (unsigned long) DUK_HSTRING_GET_HASH(h), (unsigned long) hash)); DUK_ASSERT(hash == (duk_uint32_t) DUK_HSTRING_GET_HASH(h)); } #endif return 1; }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_int(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_int(ctx, (int) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_int_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hthread); } else { hdr_size = (duk_int_t) sizeof(duk_hobject); } duk_push_int(ctx, (int) hdr_size); duk_push_int(ctx, (int) DUK_HOBJECT_E_ALLOC_SIZE(h_obj)); duk_push_int(ctx, (int) h_obj->e_size); duk_push_int(ctx, (int) h_obj->e_used); duk_push_int(ctx, (int) h_obj->a_size); duk_push_int(ctx, (int) h_obj->h_size); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = ((duk_hcompiledfunction *) h_obj)->data; if (h_data) { duk_push_int(ctx, DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_int(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { /* XXX: when usable_size == 0, dynamic buf ptr may now be NULL, in which case * the second allocation does not exist. */ duk_hbuffer_dynamic *h_dyn = (duk_hbuffer_dynamic *) h; duk_push_int(ctx, (int) (sizeof(duk_hbuffer_dynamic))); duk_push_int(ctx, (int) (DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h_dyn))); } else { duk_push_int(ctx, (int) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* FIXME: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }
DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_sourcecode; duk_idx_t nargs; duk_idx_t i; duk_small_uint_t comp_flags; duk_hcompiledfunction *func; duk_hobject *outer_lex_env; duk_hobject *outer_var_env; /* normal and constructor calls have identical semantics */ nargs = duk_get_top(ctx); for (i = 0; i < nargs; i++) { duk_to_string(ctx, i); } if (nargs == 0) { duk_push_string(ctx, ""); duk_push_string(ctx, ""); } else if (nargs == 1) { /* XXX: cover this with the generic >1 case? */ duk_push_string(ctx, ""); } else { duk_insert(ctx, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ duk_push_string(ctx, ","); duk_insert(ctx, 1); duk_join(ctx, nargs - 1); } /* [ body formals ], formals is comma separated list that needs to be parsed */ DUK_ASSERT_TOP(ctx, 2); /* XXX: this placeholder is not always correct, but use for now. * It will fail in corner cases; see test-dev-func-cons-args.js. */ duk_push_string(ctx, "function("); duk_dup(ctx, 1); duk_push_string(ctx, "){"); duk_dup(ctx, 0); duk_push_string(ctx, "}"); duk_concat(ctx, 5); /* [ body formals source ] */ DUK_ASSERT_TOP(ctx, 3); /* strictness is not inherited, intentional */ comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ h_sourcecode = duk_require_hstring(ctx, -2); duk_js_compile(thr, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode), comp_flags); func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); DUK_ASSERT(func != NULL); DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); /* [ body formals source template ] */ /* only outer_lex_env matters, as functions always get a new * variable declaration environment. */ outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/); /* [ body formals source template closure ] */ return 1; }
/* Helper which can be called both directly and with duk_safe_call(). */ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk__compile_raw_args *comp_args; duk_uint_t flags; duk_small_uint_t comp_flags; duk_hcompiledfunction *h_templ; /* Note: strictness is not inherited from the current Duktape/C * context. Otherwise it would not be possible to compile * non-strict code inside a Duktape/C activation (which is * always strict now). See api-testcases/test-eval-strictness.c * for discussion. */ /* [ ... source? filename &comp_args ] (depends on flags) */ comp_args = (duk__compile_raw_args *) duk_require_pointer(ctx, -1); flags = comp_args->flags; duk_pop(ctx); /* [ ... source? filename ] */ if (!comp_args->src_buffer) { duk_hstring *h_sourcecode; h_sourcecode = duk_get_hstring(ctx, -2); if ((flags & DUK_COMPILE_NOSOURCE) || /* args incorrect */ (h_sourcecode == NULL)) { /* e.g. duk_push_file_string_raw() pushed undefined */ /* XXX: when this error is caused by a nonexistent * file given to duk_peval_file() or similar, the * error message is not the best possible. */ DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_NO_SOURCECODE); } DUK_ASSERT(h_sourcecode != NULL); comp_args->src_buffer = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode); comp_args->src_length = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode); } DUK_ASSERT(comp_args->src_buffer != NULL); /* XXX: unnecessary translation of flags */ comp_flags = 0; if (flags & DUK_COMPILE_EVAL) { comp_flags |= DUK_JS_COMPILE_FLAG_EVAL; } if (flags & DUK_COMPILE_FUNCTION) { comp_flags |= DUK_JS_COMPILE_FLAG_EVAL | DUK_JS_COMPILE_FLAG_FUNCEXPR; } if (flags & DUK_COMPILE_STRICT) { comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; } /* [ ... source? filename ] */ duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, comp_flags); /* [ ... source? func_template ] */ if (flags & DUK_COMPILE_NOSOURCE) { ; } else { duk_remove(ctx, -2); } /* [ ... func_template ] */ h_templ = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); DUK_ASSERT(h_templ != NULL); duk_js_push_closure(thr, h_templ, thr->builtins[DUK_BIDX_GLOBAL_ENV], thr->builtins[DUK_BIDX_GLOBAL_ENV]); duk_remove(ctx, -2); /* -> [ ... closure ] */ /* [ ... closure ] */ return 1; }
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) { duk_strtab_entry *e; duk_small_uint_t i; duk_size_t j, n, used; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t *lst; duk_uint16_t null16 = heap->heapptr_null16; #else duk_hstring **lst; #endif DUK_ASSERT(heap != NULL); for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { e = heap->strtable + i; if (e->listlen == 0) { #if defined(DUK_USE_HEAPPTR16) DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str16 != null16 ? 1 : 0))); #else DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str ? 1 : 0))); #endif } else { used = 0; #if defined(DUK_USE_HEAPPTR16) lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); #else lst = e->u.strlist; #endif DUK_ASSERT(lst != NULL); for (j = 0, n = e->listlen; j < n; j++) { #if defined(DUK_USE_HEAPPTR16) if (lst[j] != null16) { #else if (lst[j] != NULL) { #endif used++; } } DUK_DD(DUK_DDPRINT("[%03d] -> array %d/%d", (int) i, (int) used, (int) e->listlen)); } } } #endif /* DUK_USE_DEBUG */ #endif /* DUK_USE_STRTAB_CHAIN */ /* * String table algorithm: closed hashing with a probe sequence * * This is the default algorithm and works fine for environments with * minimal memory constraints. */ #if defined(DUK_USE_STRTAB_PROBE) /* Count actually used (non-NULL, non-DELETED) entries. */ DUK_LOCAL duk_int_t duk__count_used_probe(duk_heap *heap) { duk_int_t res = 0; duk_uint_fast32_t i, n; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t null16 = heap->heapptr_null16; duk_uint16_t deleted16 = heap->heapptr_deleted16; #endif n = (duk_uint_fast32_t) heap->st_size; for (i = 0; i < n; i++) { #if defined(DUK_USE_HEAPPTR16) if (heap->strtable16[i] != null16 && heap->strtable16[i] != deleted16) { #else if (heap->strtable[i] != NULL && heap->strtable[i] != DUK__DELETED_MARKER(heap)) { #endif res++; } } return res; } #if defined(DUK_USE_HEAPPTR16) DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { #else DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { #endif duk_uint32_t i; duk_uint32_t step; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t null16 = heap->heapptr_null16; duk_uint16_t deleted16 = heap->heapptr_deleted16; #endif 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 (;;) { #if defined(DUK_USE_HEAPPTR16) duk_uint16_t e16 = entries16[i]; #else duk_hstring *e = entries[i]; #endif #if defined(DUK_USE_HEAPPTR16) /* XXX: could check for e16 == 0 because NULL is guaranteed to * encode to zero. */ if (e16 == null16) { #else if (e == NULL) { #endif DUK_DDD(DUK_DDDPRINT("insert hit (null): %ld", (long) i)); #if defined(DUK_USE_HEAPPTR16) entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); #else entries[i] = h; #endif (*p_used)++; break; #if defined(DUK_USE_HEAPPTR16) } else if (e16 == deleted16) { #else } else if (e == DUK__DELETED_MARKER(heap)) { #endif /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %ld", (long) i)); #if defined(DUK_USE_HEAPPTR16) entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); #else entries[i] = h; #endif break; } DUK_DDD(DUK_DDDPRINT("insert miss: %ld", (long) i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size)); } } #if defined(DUK_USE_HEAPPTR16) DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { #else DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { #endif 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; #if defined(DUK_USE_HEAPPTR16) e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, entries16[i]); #else e = entries[i]; #endif if (!e) { return NULL; } if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) { if (DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e), (size_t) blen) == 0) { DUK_DDD(DUK_DDDPRINT("find matching hit: %ld (step %ld, size %ld)", (long) i, (long) step, (long) size)); return e; } } DUK_DDD(DUK_DDDPRINT("find matching miss: %ld (step %ld, size %ld)", (long) i, (long) step, (long) size)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size)); } DUK_UNREACHABLE(); }
DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_re_compiler_ctx re_ctx; duk_lexer_point lex_point; duk_hstring *h_pattern; duk_hstring *h_flags; duk__re_disjunction_info ign_disj; DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); /* * Args validation */ /* TypeError if fails */ h_pattern = duk_require_hstring(ctx, -2); h_flags = duk_require_hstring(ctx, -1); /* * Create normalized 'source' property (E5 Section 15.10.3). */ /* [ ... pattern flags ] */ duk__create_escaped_source(thr, -2); /* [ ... pattern flags escaped_source ] */ /* * Init compilation context */ /* [ ... pattern flags escaped_source buffer ] */ DUK_MEMZERO(&re_ctx, sizeof(re_ctx)); DUK_LEXER_INITCTX(&re_ctx.lex); /* duplicate zeroing, expect for (possible) NULL inits */ re_ctx.thr = thr; re_ctx.lex.thr = thr; re_ctx.lex.input = DUK_HSTRING_GET_DATA(h_pattern); re_ctx.lex.input_length = DUK_HSTRING_GET_BYTELEN(h_pattern); re_ctx.lex.token_limit = DUK_RE_COMPILE_TOKEN_LIMIT; re_ctx.recursion_limit = DUK_USE_REGEXP_COMPILER_RECLIMIT; re_ctx.re_flags = duk__parse_regexp_flags(thr, h_flags); DUK_BW_INIT_PUSHBUF(thr, &re_ctx.bw, DUK__RE_INITIAL_BUFSIZE); DUK_DD(DUK_DDPRINT("regexp compiler ctx initialized, flags=0x%08lx, recursion_limit=%ld", (unsigned long) re_ctx.re_flags, (long) re_ctx.recursion_limit)); /* * Init lexer */ lex_point.offset = 0; /* expensive init, just want to fill window */ lex_point.line = 1; DUK_LEXER_SETPOINT(&re_ctx.lex, &lex_point); /* * Compilation */ DUK_DD(DUK_DDPRINT("starting regexp compilation")); duk__append_u32(&re_ctx, DUK_REOP_SAVE); duk__append_u32(&re_ctx, 0); duk__parse_disjunction(&re_ctx, 1 /*expect_eof*/, &ign_disj); duk__append_u32(&re_ctx, DUK_REOP_SAVE); duk__append_u32(&re_ctx, 1); duk__append_u32(&re_ctx, DUK_REOP_MATCH); /* * Check for invalid backreferences; note that it is NOT an error * to back-reference a capture group which has not yet been introduced * in the pattern (as in /\1(foo)/); in fact, the backreference will * always match! It IS an error to back-reference a capture group * which will never be introduced in the pattern. Thus, we can check * for such references only after parsing is complete. */ if (re_ctx.highest_backref > re_ctx.captures) { DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BACKREFS); } /* * Emit compiled regexp header: flags, ncaptures * (insertion order inverted on purpose) */ duk__insert_u32(&re_ctx, 0, (re_ctx.captures + 1) * 2); duk__insert_u32(&re_ctx, 0, re_ctx.re_flags); /* [ ... pattern flags escaped_source buffer ] */ DUK_BW_COMPACT(thr, &re_ctx.bw); duk_to_string(ctx, -1); /* coerce to string */ /* [ ... pattern flags escaped_source bytecode ] */ /* * Finalize stack */ duk_remove(ctx, -4); /* -> [ ... flags escaped_source bytecode ] */ duk_remove(ctx, -3); /* -> [ ... escaped_source bytecode ] */ DUK_DD(DUK_DDPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T", (duk_tval *) duk_get_tval(ctx, -1), (duk_tval *) duk_get_tval(ctx, -2))); }
DUK_LOCAL duk_hstring *duk__alloc_init_hstring(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash, const duk_uint8_t *extdata) { duk_hstring *res = NULL; duk_uint8_t *data; duk_size_t alloc_size; duk_uarridx_t dummy; duk_uint32_t clen; #if defined(DUK_USE_STRLEN16) /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */ if (blen > 0xffffUL) { DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern")); return NULL; } #endif if (extdata) { alloc_size = (duk_size_t) sizeof(duk_hstring_external); res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); if (!res) { goto alloc_error; } DUK_MEMZERO(res, sizeof(duk_hstring_external)); #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, DUK_HSTRING_FLAG_EXTDATA); ((duk_hstring_external *) res)->extdata = extdata; } else { /* 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 alloc_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); data = (duk_uint8_t *) (res + 1); DUK_MEMCPY(data, str, blen); data[blen] = (duk_uint8_t) 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); } DUK_HSTRING_SET_HASH(res, strhash); DUK_HSTRING_SET_BYTELEN(res, blen); clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); DUK_ASSERT(clen <= blen); DUK_HSTRING_SET_CHARLEN(res, clen); DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld, has_extdata=%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), (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0))); return res; alloc_error: DUK_FREE(heap, res); return NULL; }