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 ] */ }
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 ] */ }
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_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t output_type) { duk_hthread *thr = (duk_hthread *) ctx; duk_idx_t idx_td; duk_small_int_t i; /* traceback depth fits into 16 bits */ duk_small_int_t t; /* stack type fits into 16 bits */ duk_small_int_t count_func = 0; /* traceback depth ensures fits into 16 bits */ const char *str_tailcall = " tailcall"; const char *str_strict = " strict"; const char *str_construct = " construct"; const char *str_prevyield = " preventsyield"; const char *str_directeval = " directeval"; const char *str_empty = ""; DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ DUK_UNREF(thr); duk_push_this(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TRACEDATA); idx_td = duk_get_top_index(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_4SPACE); duk_push_this(ctx); /* [ ... this tracedata sep this ] */ /* XXX: skip null filename? */ if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { /* Current tracedata contains 2 entries per callstack entry. */ for (i = 0; ; i += 2) { duk_int_t pc; duk_int_t line; duk_int_t flags; duk_double_t d; const char *funcname; const char *filename; duk_hobject *h_func; duk_hstring *h_name; duk_require_stack(ctx, 5); duk_get_prop_index(ctx, idx_td, i); duk_get_prop_index(ctx, idx_td, i + 1); d = duk_to_number(ctx, -1); pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32); flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32); t = (duk_small_int_t) duk_get_type(ctx, -2); if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) { /* * Ecmascript/native function call or lightfunc call */ count_func++; /* [ ... v1(func) v2(pc+flags) ] */ h_func = duk_get_hobject(ctx, -2); /* NULL for lightfunc */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME); #if defined(DUK_USE_PC2LINE) line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc); #else line = 0; #endif /* [ ... v1 v2 name filename ] */ /* When looking for .fileName/.lineNumber, blame first * function which has a .fileName. */ if (duk_is_string(ctx, -1)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, line); return 1; } } /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */ /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */ h_name = duk_get_hstring(ctx, -2); /* may be NULL */ funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name); filename = duk_get_string(ctx, -1); filename = filename ? filename : ""; DUK_ASSERT(funcname != NULL); DUK_ASSERT(filename != NULL); if (h_func == NULL) { duk_push_sprintf(ctx, "at %s light%s%s%s%s%s", (const char *) funcname, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) { duk_push_sprintf(ctx, "at %s (%s) native%s%s%s%s%s", (const char *) funcname, (const char *) filename, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else { duk_push_sprintf(ctx, "at %s (%s:%ld)%s%s%s%s%s", (const char *) funcname, (const char *) filename, (long) line, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ duk_pop_n(ctx, 3); /* -> [ ... str ] */ } else if (t == DUK_TYPE_STRING) { /* * __FILE__ / __LINE__ entry, here 'pc' is line number directly. * Sometimes __FILE__ / __LINE__ is reported as the source for * the error (fileName, lineNumber), sometimes not. */ /* [ ... v1(filename) v2(line+flags) ] */ /* When looking for .fileName/.lineNumber, blame compilation * or C call site unless flagged not to do so. */ if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { duk_pop(ctx); return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, pc); return 1; } } duk_push_sprintf(ctx, "at [anon] (%s:%ld) internal", (const char *) duk_get_string(ctx, -2), (long) pc); duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ duk_pop(ctx); /* -> [ ... str ] */ } else { /* unknown, ignore */ duk_pop_2(ctx); break; } } if (count_func >= DUK_USE_TRACEBACK_DEPTH) { /* Possibly truncated; there is no explicit truncation * marker so this is the best we can do. */ duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); } } /* [ ... this tracedata sep this str1 ... strN ] */ if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { return 0; } else { /* The 'this' after 'sep' will get ToString() coerced by * duk_join() automatically. We don't want to do that * coercion when providing .fileName or .lineNumber (GH-254). */ duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); 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 duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_input; duk_hstring *h_match; duk_hstring *h_search; duk_hobject *h_re; duk_bufwriter_ctx bw_alloc; duk_bufwriter_ctx *bw; #ifdef DUK_USE_REGEXP_SUPPORT duk_bool_t is_regexp; duk_bool_t is_global; #endif duk_bool_t is_repl_func; duk_uint32_t match_start_coff, match_start_boff; #ifdef DUK_USE_REGEXP_SUPPORT duk_int_t match_caps; #endif duk_uint32_t prev_match_end_boff; const duk_uint8_t *r_start, *r_end, *r; /* repl string scan */ duk_size_t tmp_sz; DUK_ASSERT_TOP(ctx, 2); h_input = duk_push_this_coercible_to_string(ctx); DUK_ASSERT(h_input != NULL); bw = &bw_alloc; DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); /* input size is good output starting point */ DUK_ASSERT_TOP(ctx, 4); /* stack[0] = search value * stack[1] = replace value * stack[2] = input string * stack[3] = result buffer */ h_re = duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_REGEXP); if (h_re) { #ifdef DUK_USE_REGEXP_SUPPORT is_regexp = 1; is_global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL); if (is_global) { /* start match from beginning */ duk_push_int(ctx, 0); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); } #else /* DUK_USE_REGEXP_SUPPORT */ return DUK_RET_UNSUPPORTED_ERROR; #endif /* DUK_USE_REGEXP_SUPPORT */ } else { duk_to_string(ctx, 0); #ifdef DUK_USE_REGEXP_SUPPORT is_regexp = 0; is_global = 0; #endif } if (duk_is_function(ctx, 1)) { is_repl_func = 1; r_start = NULL; r_end = NULL; } else { duk_hstring *h_repl; is_repl_func = 0; h_repl = duk_to_hstring(ctx, 1); DUK_ASSERT(h_repl != NULL); r_start = DUK_HSTRING_GET_DATA(h_repl); r_end = r_start + DUK_HSTRING_GET_BYTELEN(h_repl); } prev_match_end_boff = 0; for (;;) { /* * If matching with a regexp: * - non-global RegExp: lastIndex not touched on a match, zeroed * on a non-match * - global RegExp: on match, lastIndex will be updated by regexp * executor to point to next char after the matching part (so that * characters in the matching part are not matched again) * * If matching with a string: * - always non-global match, find first occurrence * * We need: * - The character offset of start-of-match for the replacer function * - The byte offsets for start-of-match and end-of-match to implement * the replacement values $&, $`, and $', and to copy non-matching * input string portions (including header and trailer) verbatim. * * NOTE: the E5.1 specification is a bit vague how the RegExp should * behave in the replacement process; e.g. is matching done first for * all matches (in the global RegExp case) before any replacer calls * are made? See: test-bi-string-proto-replace.js for discussion. */ DUK_ASSERT_TOP(ctx, 4); #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_dup(ctx, 0); duk_dup(ctx, 2); duk_regexp_match(thr); /* [ ... regexp input ] -> [ res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_pop(ctx); break; } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); match_start_coff = duk_get_int(ctx, -1); duk_pop(ctx); duk_get_prop_index(ctx, -1, 0); DUK_ASSERT(duk_is_string(ctx, -1)); h_match = duk_get_hstring(ctx, -1); DUK_ASSERT(h_match != NULL); duk_pop(ctx); /* h_match is borrowed, remains reachable through match_obj */ if (DUK_HSTRING_GET_BYTELEN(h_match) == 0) { /* This should be equivalent to match() algorithm step 8.f.iii.2: * detect an empty match and allow it, but don't allow it twice. */ duk_uint32_t last_index; duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); last_index = (duk_uint32_t) duk_get_uint(ctx, -1); DUK_DDD(DUK_DDDPRINT("empty match, bump lastIndex: %ld -> %ld", (long) last_index, (long) (last_index + 1))); duk_pop(ctx); duk_push_int(ctx, last_index + 1); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); } DUK_ASSERT(duk_get_length(ctx, -1) <= DUK_INT_MAX); /* string limits */ match_caps = (duk_int_t) duk_get_length(ctx, -1); } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ const duk_uint8_t *p_start, *p_end, *p; /* input string scan */ const duk_uint8_t *q_start; /* match string */ duk_size_t q_blen; #ifdef DUK_USE_REGEXP_SUPPORT DUK_ASSERT(!is_global); /* single match always */ #endif p_start = DUK_HSTRING_GET_DATA(h_input); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); p = p_start; h_search = duk_get_hstring(ctx, 0); DUK_ASSERT(h_search != NULL); q_start = DUK_HSTRING_GET_DATA(h_search); q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_search); p_end -= q_blen; /* ensure full memcmp() fits in while */ match_start_coff = 0; while (p <= p_end) { DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input)); if (DUK_MEMCMP((void *) p, (void *) q_start, (size_t) q_blen) == 0) { duk_dup(ctx, 0); h_match = duk_get_hstring(ctx, -1); DUK_ASSERT(h_match != NULL); #ifdef DUK_USE_REGEXP_SUPPORT match_caps = 0; #endif goto found; } /* track utf-8 non-continuation bytes */ if ((p[0] & 0xc0) != 0x80) { match_start_coff++; } p++; } /* not found */ break; } found: /* stack[0] = search value * stack[1] = replace value * stack[2] = input string * stack[3] = result buffer * stack[4] = regexp match OR match string */ match_start_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff); tmp_sz = (duk_size_t) (match_start_boff - prev_match_end_boff); DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz); prev_match_end_boff = match_start_boff + DUK_HSTRING_GET_BYTELEN(h_match); if (is_repl_func) { duk_idx_t idx_args; duk_hstring *h_repl; /* regexp res_obj is at index 4 */ duk_dup(ctx, 1); idx_args = duk_get_top(ctx); #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_int_t idx; duk_require_stack(ctx, match_caps + 2); for (idx = 0; idx < match_caps; idx++) { /* match followed by capture(s) */ duk_get_prop_index(ctx, 4, idx); } } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ /* match == search string, by definition */ duk_dup(ctx, 0); } duk_push_int(ctx, match_start_coff); duk_dup(ctx, 2); /* [ ... replacer match [captures] match_char_offset input ] */ duk_call(ctx, duk_get_top(ctx) - idx_args); h_repl = duk_to_hstring(ctx, -1); /* -> [ ... repl_value ] */ DUK_ASSERT(h_repl != NULL); DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_repl); duk_pop(ctx); /* repl_value */ } else { r = r_start; while (r < r_end) { duk_int_t ch1; duk_int_t ch2; #ifdef DUK_USE_REGEXP_SUPPORT duk_int_t ch3; #endif duk_size_t left; ch1 = *r++; if (ch1 != DUK_ASC_DOLLAR) { goto repl_write; } left = r_end - r; if (left <= 0) { goto repl_write; } ch2 = r[0]; switch ((int) ch2) { case DUK_ASC_DOLLAR: { ch1 = (1 << 8) + DUK_ASC_DOLLAR; goto repl_write; } case DUK_ASC_AMP: { DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_match); r++; continue; } case DUK_ASC_GRAVE: { tmp_sz = (duk_size_t) match_start_boff; DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input), tmp_sz); r++; continue; } case DUK_ASC_SINGLEQUOTE: { duk_uint32_t match_end_boff; /* Use match charlen instead of bytelen, just in case the input and * match codepoint encodings would have different lengths. */ match_end_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff + DUK_HSTRING_GET_CHARLEN(h_match)); tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - match_end_boff); DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + match_end_boff, tmp_sz); r++; continue; } default: { #ifdef DUK_USE_REGEXP_SUPPORT duk_int_t capnum, captmp, capadv; /* XXX: optional check, match_caps is zero if no regexp, * so dollar will be interpreted literally anyway. */ if (!is_regexp) { goto repl_write; } if (!(ch2 >= DUK_ASC_0 && ch2 <= DUK_ASC_9)) { goto repl_write; } capnum = ch2 - DUK_ASC_0; capadv = 1; if (left >= 2) { ch3 = r[1]; if (ch3 >= DUK_ASC_0 && ch3 <= DUK_ASC_9) { captmp = capnum * 10 + (ch3 - DUK_ASC_0); if (captmp < match_caps) { capnum = captmp; capadv = 2; } } } if (capnum > 0 && capnum < match_caps) { DUK_ASSERT(is_regexp != 0); /* match_caps == 0 without regexps */ /* regexp res_obj is at offset 4 */ duk_get_prop_index(ctx, 4, (duk_uarridx_t) capnum); if (duk_is_string(ctx, -1)) { duk_hstring *h_tmp_str; h_tmp_str = duk_get_hstring(ctx, -1); DUK_ASSERT(h_tmp_str != NULL); DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_tmp_str); } else { /* undefined -> skip (replaced with empty) */ } duk_pop(ctx); r += capadv; continue; } else { goto repl_write; } #else /* DUK_USE_REGEXP_SUPPORT */ goto repl_write; /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ } /* default case */ } /* switch (ch2) */ repl_write: /* ch1 = (r_increment << 8) + byte */ DUK_BW_WRITE_ENSURE_U8(thr, bw, (duk_uint8_t) (ch1 & 0xff)); r += ch1 >> 8; } /* while repl */ } /* if (is_repl_func) */ duk_pop(ctx); /* pop regexp res_obj or match string */ #ifdef DUK_USE_REGEXP_SUPPORT if (!is_global) { #else { /* unconditionally; is_global==0 */ #endif break; } } /* trailer */ tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff); DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz); DUK_ASSERT_TOP(ctx, 4); DUK_BW_COMPACT(thr, bw); duk_to_string(ctx, -1); return 1; } /* * split() */ /* XXX: very messy now, but works; clean up, remove unused variables (nomimally * used so compiler doesn't complain). */ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_input; duk_hstring *h_sep; duk_uint32_t limit; duk_uint32_t arr_idx; #ifdef DUK_USE_REGEXP_SUPPORT duk_bool_t is_regexp; #endif duk_bool_t matched; /* set to 1 if any match exists (needed for empty input special case) */ duk_uint32_t prev_match_end_coff, prev_match_end_boff; duk_uint32_t match_start_boff, match_start_coff; duk_uint32_t match_end_boff, match_end_coff; DUK_UNREF(thr); h_input = duk_push_this_coercible_to_string(ctx); DUK_ASSERT(h_input != NULL); duk_push_array(ctx); if (duk_is_undefined(ctx, 1)) { limit = 0xffffffffUL; } else { limit = duk_to_uint32(ctx, 1); } if (limit == 0) { return 1; } /* If the separator is a RegExp, make a "clone" of it. The specification * algorithm calls [[Match]] directly for specific indices; we emulate this * by tweaking lastIndex and using a "force global" variant of duk_regexp_match() * which will use global-style matching even when the RegExp itself is non-global. */ if (duk_is_undefined(ctx, 0)) { /* The spec algorithm first does "R = ToString(separator)" before checking * whether separator is undefined. Since this is side effect free, we can * skip the ToString() here. */ duk_dup(ctx, 2); duk_put_prop_index(ctx, 3, 0); return 1; } else if (duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_REGEXP) != NULL) { #ifdef DUK_USE_REGEXP_SUPPORT duk_push_hobject_bidx(ctx, DUK_BIDX_REGEXP_CONSTRUCTOR); duk_dup(ctx, 0); duk_new(ctx, 1); /* [ ... RegExp val ] -> [ ... res ] */ duk_replace(ctx, 0); /* lastIndex is initialized to zero by new RegExp() */ is_regexp = 1; #else return DUK_RET_UNSUPPORTED_ERROR; #endif } else { duk_to_string(ctx, 0); #ifdef DUK_USE_REGEXP_SUPPORT is_regexp = 0; #endif } /* stack[0] = separator (string or regexp) * stack[1] = limit * stack[2] = input string * stack[3] = result array */ prev_match_end_boff = 0; prev_match_end_coff = 0; arr_idx = 0; matched = 0; for (;;) { /* * The specification uses RegExp [[Match]] to attempt match at specific * offsets. We don't have such a primitive, so we use an actual RegExp * and tweak lastIndex. Since the RegExp may be non-global, we use a * special variant which forces global-like behavior for matching. */ DUK_ASSERT_TOP(ctx, 4); #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_dup(ctx, 0); duk_dup(ctx, 2); duk_regexp_match_force_global(thr); /* [ ... regexp input ] -> [ res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_pop(ctx); break; } matched = 1; duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); match_start_coff = duk_get_int(ctx, -1); match_start_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff); duk_pop(ctx); if (match_start_coff == DUK_HSTRING_GET_CHARLEN(h_input)) { /* don't allow an empty match at the end of the string */ duk_pop(ctx); break; } duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); match_end_coff = duk_get_int(ctx, -1); match_end_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_end_coff); duk_pop(ctx); /* empty match -> bump and continue */ if (prev_match_end_boff == match_end_boff) { duk_push_int(ctx, match_end_coff + 1); duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX); duk_pop(ctx); continue; } } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ const duk_uint8_t *p_start, *p_end, *p; /* input string scan */ const duk_uint8_t *q_start; /* match string */ duk_size_t q_blen, q_clen; p_start = DUK_HSTRING_GET_DATA(h_input); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); p = p_start + prev_match_end_boff; h_sep = duk_get_hstring(ctx, 0); DUK_ASSERT(h_sep != NULL); q_start = DUK_HSTRING_GET_DATA(h_sep); q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sep); q_clen = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_sep); p_end -= q_blen; /* ensure full memcmp() fits in while */ match_start_coff = prev_match_end_coff; if (q_blen == 0) { /* Handle empty separator case: it will always match, and always * triggers the check in step 13.c.iii initially. Note that we * must skip to either end of string or start of first codepoint, * skipping over any continuation bytes! * * Don't allow an empty string to match at the end of the input. */ matched = 1; /* empty separator can always match */ match_start_coff++; p++; while (p < p_end) { if ((p[0] & 0xc0) != 0x80) { goto found; } p++; } goto not_found; } DUK_ASSERT(q_blen > 0 && q_clen > 0); while (p <= p_end) { DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input)); DUK_ASSERT(q_blen > 0); /* no issues with empty memcmp() */ if (DUK_MEMCMP((void *) p, (void *) q_start, (duk_size_t) q_blen) == 0) { /* never an empty match, so step 13.c.iii can't be triggered */ goto found; } /* track utf-8 non-continuation bytes */ if ((p[0] & 0xc0) != 0x80) { match_start_coff++; } p++; } not_found: /* not found */ break; found: matched = 1; match_start_boff = (duk_uint32_t) (p - p_start); match_end_coff = (duk_uint32_t) (match_start_coff + q_clen); /* constrained by string length */ match_end_boff = (duk_uint32_t) (match_start_boff + q_blen); /* ditto */ /* empty match (may happen with empty separator) -> bump and continue */ if (prev_match_end_boff == match_end_boff) { prev_match_end_boff++; prev_match_end_coff++; continue; } } /* if (is_regexp) */ /* stack[0] = separator (string or regexp) * stack[1] = limit * stack[2] = input string * stack[3] = result array * stack[4] = regexp res_obj (if is_regexp) */ DUK_DDD(DUK_DDDPRINT("split; match_start b=%ld,c=%ld, match_end b=%ld,c=%ld, prev_end b=%ld,c=%ld", (long) match_start_boff, (long) match_start_coff, (long) match_end_boff, (long) match_end_coff, (long) prev_match_end_boff, (long) prev_match_end_coff)); duk_push_lstring(ctx, (const char *) (DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff), (duk_size_t) (match_start_boff - prev_match_end_boff)); duk_put_prop_index(ctx, 3, arr_idx); arr_idx++; if (arr_idx >= limit) { goto hit_limit; } #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_size_t i, len; len = duk_get_length(ctx, 4); for (i = 1; i < len; i++) { DUK_ASSERT(i <= DUK_UARRIDX_MAX); /* cannot have >4G captures */ duk_get_prop_index(ctx, 4, (duk_uarridx_t) i); duk_put_prop_index(ctx, 3, arr_idx); arr_idx++; if (arr_idx >= limit) { goto hit_limit; } } duk_pop(ctx); /* lastIndex already set up for next match */ } else { #else /* DUK_USE_REGEXP_SUPPORT */ { /* unconditionally */ #endif /* DUK_USE_REGEXP_SUPPORT */ /* no action */ } prev_match_end_boff = match_end_boff; prev_match_end_coff = match_end_coff; continue; } /* for */ /* Combined step 11 (empty string special case) and 14-15. */ DUK_DDD(DUK_DDDPRINT("split trailer; prev_end b=%ld,c=%ld", (long) prev_match_end_boff, (long) prev_match_end_coff)); if (DUK_HSTRING_GET_CHARLEN(h_input) > 0 || !matched) { /* Add trailer if: * a) non-empty input * b) empty input and no (zero size) match found (step 11) */ duk_push_lstring(ctx, (const char *) DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff)); duk_put_prop_index(ctx, 3, arr_idx); /* No arr_idx update or limit check */ } return 1; hit_limit: #ifdef DUK_USE_REGEXP_SUPPORT if (is_regexp) { duk_pop(ctx); } #endif return 1; } /* * Various */ #ifdef DUK_USE_REGEXP_SUPPORT DUK_LOCAL void duk__to_regexp_helper(duk_context *ctx, duk_idx_t index, duk_bool_t force_new) { duk_hobject *h; /* Shared helper for match() steps 3-4, search() steps 3-4. */ DUK_ASSERT(index >= 0); if (force_new) { goto do_new; } h = duk_get_hobject_with_class(ctx, index, DUK_HOBJECT_CLASS_REGEXP); if (!h) { goto do_new; } return; do_new: duk_push_hobject_bidx(ctx, DUK_BIDX_REGEXP_CONSTRUCTOR); duk_dup(ctx, index); duk_new(ctx, 1); /* [ ... RegExp val ] -> [ ... res ] */ duk_replace(ctx, index); } #endif /* DUK_USE_REGEXP_SUPPORT */ #ifdef DUK_USE_REGEXP_SUPPORT DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; /* Easiest way to implement the search required by the specification * is to do a RegExp test() with lastIndex forced to zero. To avoid * side effects on the argument, "clone" the RegExp if a RegExp was * given as input. * * The global flag of the RegExp should be ignored; setting lastIndex * to zero (which happens when "cloning" the RegExp) should have an * equivalent effect. */ DUK_ASSERT_TOP(ctx, 1); (void) duk_push_this_coercible_to_string(ctx); /* at index 1 */ duk__to_regexp_helper(ctx, 0 /*index*/, 1 /*force_new*/); /* stack[0] = regexp * stack[1] = string */ /* Avoid using RegExp.prototype methods, as they're writable and * configurable and may have been changed. */ duk_dup(ctx, 0); duk_dup(ctx, 1); /* [ ... re_obj input ] */ duk_regexp_match(thr); /* -> [ ... res_obj ] */ if (!duk_is_object(ctx, -1)) { duk_push_int(ctx, -1); return 1; } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INDEX); DUK_ASSERT(duk_is_number(ctx, -1)); return 1; } #else /* DUK_USE_REGEXP_SUPPORT */ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx) { DUK_UNREF(ctx); return DUK_RET_UNSUPPORTED_ERROR; }
DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_to_string(duk_context *ctx) { duk_hstring *h_bc; duk_small_int_t re_flags; #if 0 /* A little tricky string approach to provide the flags string. * This depends on the specific flag values in duk_regexp.h, * which needs to be asserted for. In practice this doesn't * produce more compact code than the easier approach in use. */ const char *flag_strings = "gim\0gi\0gm\0g\0"; duk_uint8_t flag_offsets[8] = { (duk_uint8_t) 3, /* flags: "" */ (duk_uint8_t) 10, /* flags: "g" */ (duk_uint8_t) 5, /* flags: "i" */ (duk_uint8_t) 4, /* flags: "gi" */ (duk_uint8_t) 2, /* flags: "m" */ (duk_uint8_t) 7, /* flags: "gm" */ (duk_uint8_t) 1, /* flags: "im" */ (duk_uint8_t) 0, /* flags: "gim" */ }; DUK_ASSERT(DUK_RE_FLAG_GLOBAL == 1); DUK_ASSERT(DUK_RE_FLAG_IGNORE_CASE == 2); DUK_ASSERT(DUK_RE_FLAG_MULTILINE == 4); #endif duk__get_this_regexp(ctx); /* [ regexp ] */ duk_get_prop_stridx(ctx, 0, DUK_STRIDX_SOURCE); duk_get_prop_stridx(ctx, 0, DUK_STRIDX_INT_BYTECODE); h_bc = duk_get_hstring(ctx, -1); DUK_ASSERT(h_bc != NULL); DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h_bc) >= 1); DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h_bc) >= 1); DUK_ASSERT(DUK_HSTRING_GET_DATA(h_bc)[0] < 0x80); re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0]; /* [ regexp source bytecode ] */ #if 1 /* This is a cleaner approach and also produces smaller code than * the other alternative. Use duk_require_string() for format * safety (although the source property should always exist). */ duk_push_sprintf(ctx, "/%s/%s%s%s", (const char *) duk_require_string(ctx, -2), /* require to be safe */ (re_flags & DUK_RE_FLAG_GLOBAL) ? "g" : "", (re_flags & DUK_RE_FLAG_IGNORE_CASE) ? "i" : "", (re_flags & DUK_RE_FLAG_MULTILINE) ? "m" : ""); #else /* This should not be necessary because no-one should tamper with the * regexp bytecode, but is prudent to avoid potential segfaults if that * were to happen for some reason. */ re_flags &= 0x07; DUK_ASSERT(re_flags >= 0 && re_flags <= 7); /* three flags */ duk_push_sprintf(ctx, "/%s/%s", (const char *) duk_require_string(ctx, -2), (const char *) (flag_strings + flag_offsets[re_flags])); #endif 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, void *udata) { duk_hthread *thr = (duk_hthread *) ctx; duk__compile_raw_args *comp_args; duk_uint_t flags; duk_small_uint_t comp_flags; duk_hcompfunc *h_templ; DUK_ASSERT_CTX_VALID(ctx); DUK_ASSERT(udata != NULL); /* 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 tests/api/test-eval-strictness.c * for discussion. */ /* [ ... source? filename? ] (depends on flags) */ comp_args = (duk__compile_raw_args *) udata; flags = comp_args->flags; if (flags & DUK_COMPILE_NOFILENAME) { /* Automatic filename: 'eval' or 'input'. */ duk_push_hstring_stridx(ctx, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT); } /* [ ... 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_string_file_raw() pushed undefined */ DUK_ERROR_TYPE(thr, 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_m2(ctx); } /* [ ... func_template ] */ h_templ = (duk_hcompfunc *) duk_known_hobject(ctx, -1); duk_js_push_closure(thr, h_templ, thr->builtins[DUK_BIDX_GLOBAL_ENV], thr->builtins[DUK_BIDX_GLOBAL_ENV], 1 /*add_auto_proto*/); duk_remove_m2(ctx); /* -> [ ... closure ] */ /* [ ... closure ] */ return 1; }
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx) { duk_small_uint_t pass; duk_uint_t defprop_flags; duk_hobject *obj; duk_idx_t idx_value; duk_hobject *get; duk_hobject *set; /* Lightfunc handling by ToObject() coercion. */ obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); /* target */ DUK_ASSERT(obj != NULL); duk_to_object(ctx, 1); /* properties object */ DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT", (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 1))); /* * Two pass approach to processing the property descriptors. * On first pass validate and normalize all descriptors before * any changes are made to the target object. On second pass * make the actual modifications to the target object. * * Right now we'll just use the same normalize/validate helper * on both passes, ignoring its outputs on the first pass. */ for (pass = 0; pass < 2; pass++) { duk_set_top(ctx, 2); /* -> [ hobject props ] */ duk_enum(ctx, 1, DUK_ENUM_OWN_PROPERTIES_ONLY /*enum_flags*/); for (;;) { duk_hstring *key; /* [ hobject props enum(props) ] */ duk_set_top(ctx, 3); if (!duk_next(ctx, 2, 1 /*get_value*/)) { break; } DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); /* [ hobject props enum(props) key desc ] */ duk_hobject_prepare_property_descriptor(ctx, 4 /*idx_desc*/, &defprop_flags, &idx_value, &get, &set); /* [ hobject props enum(props) key desc value? getter? setter? ] */ if (pass == 0) { continue; } key = duk_get_hstring(ctx, 3); DUK_ASSERT(key != NULL); duk_hobject_define_property_helper(ctx, defprop_flags, obj, key, idx_value, get, set); } } /* * Return target object */ duk_dup(ctx, 0); return 1; }
DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_bitdecoder_ctx bd_ctx; duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ duk_hobject *h; duk_small_uint_t i, j; DUK_D(DUK_DPRINT("INITBUILTINS BEGIN: DUK_NUM_BUILTINS=%d, DUK_NUM_BUILTINS_ALL=%d", (int) DUK_NUM_BUILTINS, (int) DUK_NUM_ALL_BUILTINS)); DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); bd->data = (const duk_uint8_t *) duk_builtins_data; bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH; /* * First create all built-in bare objects on the empty valstack. * * Built-ins in the index range [0,DUK_NUM_BUILTINS-1] have value * stack indices matching their eventual thr->builtins[] index. * * Built-ins in the index range [DUK_NUM_BUILTINS,DUK_NUM_ALL_BUILTINS] * will exist on the value stack during init but won't be placed * into thr->builtins[]. These are objects referenced in some way * from thr->builtins[] roots but which don't need to be indexed by * Duktape through thr->builtins[] (e.g. user custom objects). */ duk_require_stack(ctx, DUK_NUM_ALL_BUILTINS); DUK_DD(DUK_DDPRINT("create empty built-ins")); DUK_ASSERT_TOP(ctx, 0); for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { duk_small_uint_t class_num; duk_small_int_t len = -1; /* must be signed */ class_num = (duk_small_uint_t) duk_bd_decode(bd, DUK__CLASS_BITS); len = (duk_small_int_t) duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { duk_small_uint_t natidx; duk_int_t c_nargs; /* must hold DUK_VARARGS */ duk_c_function c_func; duk_int16_t magic; DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len)); DUK_ASSERT(len >= 0); natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); c_func = duk_bi_native_functions[natidx]; c_nargs = (duk_small_uint_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } /* XXX: set magic directly here? (it could share the c_nargs arg) */ duk_push_c_function_noexotic(ctx, c_func, c_nargs); h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); /* Currently all built-in native functions are strict. * duk_push_c_function() now sets strict flag, so * assert for it. */ DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h)); /* XXX: function properties */ /* Built-in 'name' is not writable by default. Function '.name' * is writable to allow user code to set a '.name' on a native * function. */ duk__push_stridx_or_string(ctx, bd); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, (i == DUK_BIDX_FUNCTION_PROTOTYPE) ? DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE); /* Almost all global level Function objects are constructable * but not all: Function.prototype is a non-constructable, * callable Function. */ if (duk_bd_decode_flag(bd)) { DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h)); } else { DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h); } /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/); ((duk_hnatfunc *) h)->magic = magic; } else if (class_num == DUK_HOBJECT_CLASS_ARRAY) { duk_push_array(ctx); } else { duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE, -1); /* no prototype or class yet */ } h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num); if (i < DUK_NUM_BUILTINS) { thr->builtins[i] = h; DUK_HOBJECT_INCREF(thr, &h->hdr); } if (len >= 0) { /* * For top-level objects, 'length' property has the following * default attributes: non-writable, non-enumerable, non-configurable * (E5 Section 15). * * However, 'length' property for Array.prototype has attributes * expected of an Array instance which are different: writable, * non-enumerable, non-configurable (E5 Section 15.4.5.2). * * Because Array .length is virtual, it's not encoded in the init * data separately. */ DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY); /* .length is virtual */ duk_push_int(ctx, len); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); } /* enable exotic behaviors last */ if (class_num == DUK_HOBJECT_CLASS_ARRAY) { DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)); /* set by duk_push_array() */ } if (class_num == DUK_HOBJECT_CLASS_STRING) { DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h); } /* some assertions */ DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h)); /* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h)); /* DUK_HOBJECT_FLAG_NATFUNC varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY); /* DUK_HOBJECT_FLAG_STRICT varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(h) || /* all native functions have NEWENV */ DUK_HOBJECT_HAS_NEWENV(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h)); /* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */ /* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)); DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len)); } /* * Then decode the builtins init data (see genbuiltins.py) to * init objects */ DUK_DD(DUK_DDPRINT("initialize built-in object properties")); for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { duk_small_uint_t t; duk_small_uint_t num; DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i)); h = duk_require_hobject(ctx, i); DUK_ASSERT(h != NULL); t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t)); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, duk_require_hobject(ctx, t)); } t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'prototype' property for all built-in objects (which have it) has attributes: * [[Writable]] = false, * [[Enumerable]] = false, * [[Configurable]] = false */ DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t)); duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE); } t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'constructor' property for all built-in objects (which have it) has attributes: * [[Writable]] = true, * [[Enumerable]] = false, * [[Configurable]] = true */ DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t)); duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC); } /* normal valued properties */ num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS); DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num)); for (j = 0; j < num; j++) { duk_small_uint_t prop_flags; duk__push_stridx_or_string(ctx, bd); /* * Property attribute defaults are defined in E5 Section 15 (first * few pages); there is a default for all properties and a special * default for 'length' properties. Variation from the defaults is * signaled using a single flag bit in the bitstream. */ if (duk_bd_decode_flag(bd)) { prop_flags = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_FLAGS_BITS); } else { prop_flags = DUK_PROPDESC_FLAGS_WC; } t = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS); DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, key %!T, flags 0x%02lx, type %ld", (long) i, (long) j, duk_get_tval(ctx, -1), (unsigned long) prop_flags, (long) t)); switch (t) { case DUK__PROP_TYPE_DOUBLE: { duk__push_double(ctx, bd); break; } case DUK__PROP_TYPE_STRING: { duk__push_string(ctx, bd); break; } case DUK__PROP_TYPE_STRIDX: { duk__push_stridx(ctx, bd); break; } case DUK__PROP_TYPE_BUILTIN: { duk_small_uint_t bidx; bidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER); duk_dup(ctx, (duk_idx_t) bidx); break; } case DUK__PROP_TYPE_UNDEFINED: { duk_push_undefined(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_TRUE: { duk_push_true(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_FALSE: { duk_push_false(ctx); break; } case DUK__PROP_TYPE_ACCESSOR: { duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); duk_c_function c_func_getter; duk_c_function c_func_setter; /* XXX: this is a bit awkward because there is no exposed helper * in the API style, only this internal helper. */ DUK_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, key=%!T, getteridx=%ld, setteridx=%ld, flags=0x%04lx", (long) i, duk_get_tval(ctx, -1), (long) natidx_getter, (long) natidx_setter, (unsigned long) prop_flags)); c_func_getter = duk_bi_native_functions[natidx_getter]; c_func_setter = duk_bi_native_functions[natidx_setter]; duk_push_c_function_noconstruct_noexotic(ctx, c_func_getter, 0); /* always 0 args */ duk_push_c_function_noconstruct_noexotic(ctx, c_func_setter, 1); /* always 1 arg */ /* XXX: magic for getter/setter? use duk_def_prop()? */ DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_WRITABLE) == 0); /* genbuiltins.py ensures */ prop_flags |= DUK_PROPDESC_FLAG_ACCESSOR; /* accessor flag not encoded explicitly */ duk_hobject_define_accessor_internal(thr, duk_require_hobject(ctx, i), duk_get_hstring(ctx, -3), duk_require_hobject(ctx, -2), duk_require_hobject(ctx, -1), prop_flags); duk_pop_3(ctx); /* key, getter and setter, now reachable through object */ goto skip_value; } default: { /* exhaustive */ DUK_UNREACHABLE(); } } DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0); duk_xdef_prop(ctx, i, prop_flags); skip_value: continue; /* avoid empty label at the end of a compound statement */ } /* native function properties */ num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS); DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num)); for (j = 0; j < num; j++) { duk_hstring *h_key; duk_small_uint_t natidx; duk_int_t c_nargs; /* must hold DUK_VARARGS */ duk_small_uint_t c_length; duk_int16_t magic; duk_c_function c_func; duk_hnatfunc *h_func; #if defined(DUK_USE_LIGHTFUNC_BUILTINS) duk_small_int_t lightfunc_eligible; #endif duk__push_stridx_or_string(ctx, bd); h_key = duk_get_hstring(ctx, -1); DUK_ASSERT(h_key != NULL); DUK_UNREF(h_key); natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS); c_nargs = (duk_int_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } c_func = duk_bi_native_functions[natidx]; DUK_DDD(DUK_DDDPRINT("built-in %ld, function-valued property %ld, key %!O, natidx %ld, length %ld, nargs %ld", (long) i, (long) j, (duk_heaphdr *) h_key, (long) natidx, (long) c_length, (c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs))); /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0); #if defined(DUK_USE_LIGHTFUNC_BUILTINS) lightfunc_eligible = ((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) && (c_length <= DUK_LFUNC_LENGTH_MAX) && (magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX); if (h_key == DUK_HTHREAD_STRING_EVAL(thr) || h_key == DUK_HTHREAD_STRING_YIELD(thr) || h_key == DUK_HTHREAD_STRING_RESUME(thr)) { /* These functions have trouble working as lightfuncs. * Some of them have specific asserts and some may have * additional properties (e.g. 'require.id' may be written). */ DUK_D(DUK_DPRINT("reject as lightfunc: key=%!O, i=%d, j=%d", (duk_heaphdr *) h_key, (int) i, (int) j)); lightfunc_eligible = 0; } if (lightfunc_eligible) { duk_tval tv_lfunc; duk_small_uint_t lf_nargs = (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs); duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs); DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags); duk_push_tval(ctx, &tv_lfunc); DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld -> %!iT", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic, duk_get_tval(ctx, -1))); goto lightfunc_skip; } DUK_D(DUK_DPRINT("built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic)); #endif /* DUK_USE_LIGHTFUNC_BUILTINS */ /* [ (builtin objects) name ] */ duk_push_c_function_noconstruct_noexotic(ctx, c_func, c_nargs); h_func = duk_require_hnatfunc(ctx, -1); DUK_UNREF(h_func); /* Currently all built-in native functions are strict. * This doesn't matter for many functions, but e.g. * String.prototype.charAt (and other string functions) * rely on being strict so that their 'this' binding is * not automatically coerced. */ DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func); /* No built-in functions are constructable except the top * level ones (Number, etc). */ DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func)); /* XXX: any way to avoid decoding magic bit; there are quite * many function properties and relatively few with magic values. */ h_func->magic = magic; /* [ (builtin objects) name func ] */ duk_push_int(ctx, c_length); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); duk_dup(ctx, -2); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* XXX: other properties of function instances; 'arguments', 'caller'. */ DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T", (long) i, (long) j, (duk_tval *) duk_get_tval(ctx, -1))); /* [ (builtin objects) name func ] */ /* * The default property attributes are correct for all * function valued properties of built-in objects now. */ #if defined(DUK_USE_LIGHTFUNC_BUILTINS) lightfunc_skip: #endif duk_xdef_prop(ctx, i, DUK_PROPDESC_FLAGS_WC); /* [ (builtin objects) ] */ } } /* * Special post-tweaks, for cases not covered by the init data format. * * - Set Date.prototype.toGMTString to Date.prototype.toUTCString. * toGMTString is required to have the same Function object as * toUTCString in E5 Section B.2.6. Note that while Smjs respects * this, V8 does not (the Function objects are distinct). * * - Make DoubleError non-extensible. * * - Add info about most important effective compile options to Duktape. * * - Possibly remove some properties (values or methods) which are not * desirable with current feature options but are not currently * conditional in init data. */ duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING); duk_xdef_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC); h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR); DUK_ASSERT(h != NULL); DUK_HOBJECT_CLEAR_EXTENSIBLE(h); #if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY) DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features")); (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], DUK_HTHREAD_STRING___PROTO__(thr), DUK_DELPROP_FLAG_THROW); #endif #if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF) DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features")); (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), DUK_DELPROP_FLAG_THROW); #endif /* XXX: relocate */ duk_push_string(ctx, /* Endianness indicator */ #if defined(DUK_USE_INTEGER_LE) "l" #elif defined(DUK_USE_INTEGER_BE) "b" #elif defined(DUK_USE_INTEGER_ME) /* integer mixed endian not really used now */ "m" #else "?" #endif #if defined(DUK_USE_DOUBLE_LE) "l" #elif defined(DUK_USE_DOUBLE_BE) "b" #elif defined(DUK_USE_DOUBLE_ME) "m" #else "?" #endif " " /* Packed or unpacked tval */ #if defined(DUK_USE_PACKED_TVAL) "p" #else "u" #endif #if defined(DUK_USE_FASTINT) "f" #endif " " /* Low memory options */ #if defined(DUK_USE_STRTAB_CHAIN) "c" /* chain */ #elif defined(DUK_USE_STRTAB_PROBE) "p" /* probe */ #else "?" #endif #if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16) "n" #endif #if defined(DUK_USE_HEAPPTR16) "h" #endif #if defined(DUK_USE_DATAPTR16) "d" #endif #if defined(DUK_USE_FUNCPTR16) "f" #endif #if defined(DUK_USE_REFCOUNT16) "R" #endif #if defined(DUK_USE_STRHASH16) "H" #endif #if defined(DUK_USE_STRLEN16) "S" #endif #if defined(DUK_USE_BUFLEN16) "B" #endif #if defined(DUK_USE_OBJSIZES16) "O" #endif #if defined(DUK_USE_LIGHTFUNC_BUILTINS) "L" #endif #if defined(DUK_USE_ROM_STRINGS) || defined(DUK_USE_ROM_OBJECTS) /* XXX: This won't be shown in practice now * because this code is not run when builtins * are in ROM. */ "Z" #endif " " /* Object property allocation layout */ #if defined(DUK_USE_HOBJECT_LAYOUT_1) "p1" #elif defined(DUK_USE_HOBJECT_LAYOUT_2) "p2" #elif defined(DUK_USE_HOBJECT_LAYOUT_3) "p3" #else "p?" #endif " " /* Alignment guarantee */ #if (DUK_USE_ALIGN_BY == 4) "a4" #elif (DUK_USE_ALIGN_BY == 8) "a8" #elif (DUK_USE_ALIGN_BY == 1) "a1" #else #error invalid DUK_USE_ALIGN_BY #endif " " /* Architecture, OS, and compiler strings */ DUK_USE_ARCH_STRING " " DUK_USE_OS_STRING " " DUK_USE_COMPILER_STRING); duk_xdef_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC); /* * InitJS code - Ecmascript code evaluated from a built-in source * which provides e.g. backward compatibility. User can also provide * JS code to be evaluated at startup. */ #ifdef DUK_USE_BUILTIN_INITJS /* XXX: compression */ DUK_DD(DUK_DDPRINT("running built-in initjs")); duk_eval_string(ctx, (const char *) duk_initjs_data); /* initjs data is NUL terminated */ duk_pop(ctx); #endif /* DUK_USE_BUILTIN_INITJS */ #ifdef DUK_USE_USER_INITJS /* XXX: compression (as an option) */ DUK_DD(DUK_DDPRINT("running user initjs")); duk_eval_string_noresult(ctx, (const char *) DUK_USE_USER_INITJS); #endif /* DUK_USE_USER_INITJS */ /* * Since built-ins are not often extended, compact them. */ DUK_DD(DUK_DDPRINT("compact built-ins")); for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { duk_hobject_compact_props(thr, duk_require_hobject(ctx, i)); } DUK_D(DUK_DPRINT("INITBUILTINS END")); #if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO", (long) i, (duk_heaphdr *) duk_require_hobject(ctx, i))); } #endif /* * Pop built-ins from stack: they are now INCREF'd and * reachable from the builtins[] array or indirectly * through builtins[]. */ duk_set_top(ctx, 0); DUK_ASSERT_TOP(ctx, 0); }
static int duk__traceback_getter_helper(duk_context *ctx, int output_type) { duk_hthread *thr = (duk_hthread *) ctx; int idx_td; int i; const char *str_tailcalled = " tailcalled"; const char *str_strict = " strict"; const char *str_construct = " construct"; const char *str_prevyield = " preventsyield"; const char *str_directeval = " directeval"; const char *str_empty = ""; DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ duk_push_this(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TRACEDATA); idx_td = duk_get_top_index(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_TAB); duk_push_this(ctx); duk_to_string(ctx, -1); /* [ ... this tracedata sep ToString(this) ] */ /* FIXME: skip null filename? */ if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { int t; /* Current tracedata contains 2 entries per callstack entry. */ for (i = 0; ; i += 2) { int pc; int line; int flags; double d; const char *funcname; duk_hobject *h_func; duk_hstring *h_name; duk_hbuffer_fixed *pc2line; duk_require_stack(ctx, 5); duk_get_prop_index(ctx, idx_td, i); duk_get_prop_index(ctx, idx_td, i + 1); d = duk_to_number(ctx, -1); pc = (int) fmod(d, DUK_DOUBLE_2TO32); flags = (int) floor(d / DUK_DOUBLE_2TO32); t = duk_get_type(ctx, -2); if (t == DUK_TYPE_OBJECT) { /* * Ecmascript/native function call */ /* [ ... v1(func) v2(pc+flags) ] */ h_func = duk_get_hobject(ctx, -2); DUK_ASSERT(h_func != NULL); duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME); duk_get_prop_stridx(ctx, -4, DUK_STRIDX_INT_PC2LINE); if (duk_is_buffer(ctx, -1)) { pc2line = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line)); line = duk_hobject_pc2line_query(pc2line, (duk_uint_fast32_t) pc); } else { line = 0; } duk_pop(ctx); /* [ ... v1 v2 name filename ] */ if (output_type == DUK__OUTPUT_TYPE_FILENAME) { return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, line); return 1; } h_name = duk_get_hstring(ctx, -2); /* may be NULL */ funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? "anon" : (const char *) DUK_HSTRING_GET_DATA(h_name); if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func)) { duk_push_sprintf(ctx, "%s %s native%s%s%s%s%s", funcname, duk_get_string(ctx, -1), (flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty, (flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty, (flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty, (flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty, (flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty); } else { duk_push_sprintf(ctx, "%s %s:%d%s%s%s%s%s", funcname, duk_get_string(ctx, -1), line, (flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty, (flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty, (flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty, (flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty, (flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty); } duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ duk_pop_n(ctx, 3); /* -> [ ... str ] */ } else if (t == DUK_TYPE_STRING) { /* * __FILE__ / __LINE__ entry, here 'pc' is line number directly. * Sometimes __FILE__ / __LINE__ is reported as the source for * the error (fileName, lineNumber), sometimes not. */ /* [ ... v1(filename) v2(line+flags) ] */ if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { duk_pop(ctx); return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, pc); return 1; } } duk_push_sprintf(ctx, "%s:%d", duk_get_string(ctx, -2), pc); duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ duk_pop(ctx); /* -> [ ... str ] */ } else { /* unknown, ignore */ duk_pop_2(ctx); break; } } if (i >= DUK_USE_TRACEBACK_DEPTH * 2) { /* Possibly truncated; there is no explicit truncation * marker so this is the best we can do. */ duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); } } /* [ ... this tracedata sep ToString(this) str1 ... strN ] */ if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { return 0; } else { duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); return 1; } }