]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_bi_string.c
import quincy beta 17.1.0
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.8.0 / src-separate / duk_bi_string.c
diff --git a/ceph/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_bi_string.c b/ceph/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_bi_string.c
deleted file mode 100644 (file)
index bf43750..0000000
+++ /dev/null
@@ -1,1314 +0,0 @@
-/*
- *  String built-ins
- */
-
-/* XXX: There are several limitations in the current implementation for
- * strings with >= 0x80000000UL characters.  In some cases one would need
- * to be able to represent the range [-0xffffffff,0xffffffff] and so on.
- * Generally character and byte length are assumed to fit into signed 32
- * bits (< 0x80000000UL).  Places with issues are not marked explicitly
- * below in all cases, look for signed type usage (duk_int_t etc) for
- * offsets/lengths.
- */
-
-#include "duk_internal.h"
-
-/*
- *  Constructor
- */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_context *ctx) {
-       /* String constructor needs to distinguish between an argument not given at all
-        * vs. given as 'undefined'.  We're a vararg function to handle this properly.
-        */
-
-       if (duk_get_top(ctx) == 0) {
-               duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);
-       } else {
-               duk_to_string(ctx, 0);
-       }
-       DUK_ASSERT(duk_is_string(ctx, 0));
-       duk_set_top(ctx, 1);
-
-       if (duk_is_constructor_call(ctx)) {
-               duk_push_object_helper(ctx,
-                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
-                                      DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
-                                      DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING),
-                                      DUK_BIDX_STRING_PROTOTYPE);
-
-               /* String object internal value is immutable */
-               duk_dup(ctx, 0);
-               duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
-       }
-       /* Note: unbalanced stack on purpose */
-
-       return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_char_code(duk_context *ctx) {
-       duk_hthread *thr = (duk_hthread *) ctx;
-       duk_bufwriter_ctx bw_alloc;
-       duk_bufwriter_ctx *bw;
-       duk_idx_t i, n;
-       duk_ucodepoint_t cp;
-
-       /* XXX: It would be nice to build the string directly but ToUint16()
-        * coercion is needed so a generic helper would not be very
-        * helpful (perhaps coerce the value stack first here and then
-        * build a string from a duk_tval number sequence in one go?).
-        */
-
-       n = duk_get_top(ctx);
-
-       bw = &bw_alloc;
-       DUK_BW_INIT_PUSHBUF(thr, bw, n);  /* initial estimate for ASCII only codepoints */
-
-       for (i = 0; i < n; i++) {
-               /* XXX: could improve bufwriter handling to write multiple codepoints
-                * with one ensure call but the relative benefit would be quite small.
-                */
-
-#if defined(DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT)
-               /* ToUint16() coercion is mandatory in the E5.1 specification, but
-                * this non-compliant behavior makes more sense because we support
-                * non-BMP codepoints.  Don't use CESU-8 because that'd create
-                * surrogate pairs.
-                */
-
-               cp = (duk_ucodepoint_t) duk_to_uint32(ctx, i);
-               DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp);
-#else
-               cp = (duk_ucodepoint_t) duk_to_uint16(ctx, i);
-               DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp);
-#endif
-       }
-
-       DUK_BW_COMPACT(thr, bw);
-       duk_to_string(ctx, -1);
-       return 1;
-}
-
-/*
- *  toString(), valueOf()
- */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx) {
-       duk_tval *tv;
-
-       duk_push_this(ctx);
-       tv = duk_require_tval(ctx, -1);
-       DUK_ASSERT(tv != NULL);
-
-       if (DUK_TVAL_IS_STRING(tv)) {
-               /* return as is */
-               return 1;
-       } else if (DUK_TVAL_IS_OBJECT(tv)) {
-               duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
-               DUK_ASSERT(h != NULL);
-
-               /* Must be a "string object", i.e. class "String" */
-               if (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_STRING) {
-                       goto type_error;
-               }
-
-               duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE);
-               DUK_ASSERT(duk_is_string(ctx, -1));
-
-               return 1;
-       } else {
-               goto type_error;
-       }
-
-       /* never here, but fall through */
-
- type_error:
-       return DUK_RET_TYPE_ERROR;
-}
-
-/*
- *  Character and charcode access
- */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_at(duk_context *ctx) {
-       duk_int_t pos;
-
-       /* XXX: faster implementation */
-
-       (void) duk_push_this_coercible_to_string(ctx);
-       pos = duk_to_int(ctx, 0);
-       duk_substring(ctx, -1, pos, pos + 1);
-       return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) {
-       duk_hthread *thr = (duk_hthread *) ctx;
-       duk_int_t pos;
-       duk_hstring *h;
-       duk_bool_t clamped;
-
-       /* XXX: faster implementation */
-
-       DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(ctx, 0)));
-
-       h = duk_push_this_coercible_to_string(ctx);
-       DUK_ASSERT(h != NULL);
-
-       pos = duk_to_int_clamped_raw(ctx,
-                                    0 /*index*/,
-                                    0 /*min(incl)*/,
-                                    DUK_HSTRING_GET_CHARLEN(h) - 1 /*max(incl)*/,
-                                    &clamped /*out_clamped*/);
-       if (clamped) {
-               duk_push_number(ctx, DUK_DOUBLE_NAN);
-               return 1;
-       }
-
-       duk_push_u32(ctx, (duk_uint32_t) duk_hstring_char_code_at_raw(thr, h, pos));
-       return 1;
-}
-
-/*
- *  substring(), substr(), slice()
- */
-
-/* XXX: any chance of merging these three similar but still slightly
- * different algorithms so that footprint would be reduced?
- */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx) {
-       duk_hstring *h;
-       duk_int_t start_pos, end_pos;
-       duk_int_t len;
-
-       h = duk_push_this_coercible_to_string(ctx);
-       DUK_ASSERT(h != NULL);
-       len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
-
-       /* [ start end str ] */
-
-       start_pos = duk_to_int_clamped(ctx, 0, 0, len);
-       if (duk_is_undefined(ctx, 1)) {
-               end_pos = len;
-       } else {
-               end_pos = duk_to_int_clamped(ctx, 1, 0, len);
-       }
-       DUK_ASSERT(start_pos >= 0 && start_pos <= len);
-       DUK_ASSERT(end_pos >= 0 && end_pos <= len);
-
-       if (start_pos > end_pos) {
-               duk_int_t tmp = start_pos;
-               start_pos = end_pos;
-               end_pos = tmp;
-       }
-
-       DUK_ASSERT(end_pos >= start_pos);
-
-       duk_substring(ctx, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
-       return 1;
-}
-
-#ifdef DUK_USE_SECTION_B
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx) {
-       duk_hstring *h;
-       duk_int_t start_pos, end_pos;
-       duk_int_t len;
-
-       /* Unlike non-obsolete String calls, substr() algorithm in E5.1
-        * specification will happily coerce undefined and null to strings
-        * ("undefined" and "null").
-        */
-       duk_push_this(ctx);
-       h = duk_to_hstring(ctx, -1);
-       DUK_ASSERT(h != NULL);
-       len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
-
-       /* [ start length str ] */
-
-       /* The implementation for computing of start_pos and end_pos differs
-        * from the standard algorithm, but is intended to result in the exactly
-        * same behavior.  This is not always obvious.
-        */
-
-       /* combines steps 2 and 5; -len ensures max() not needed for step 5 */
-       start_pos = duk_to_int_clamped(ctx, 0, -len, len);
-       if (start_pos < 0) {
-               start_pos = len + start_pos;
-       }
-       DUK_ASSERT(start_pos >= 0 && start_pos <= len);
-
-       /* combines steps 3, 6; step 7 is not needed */
-       if (duk_is_undefined(ctx, 1)) {
-               end_pos = len;
-       } else {
-               DUK_ASSERT(start_pos <= len);
-               end_pos = start_pos + duk_to_int_clamped(ctx, 1, 0, len - start_pos);
-       }
-       DUK_ASSERT(start_pos >= 0 && start_pos <= len);
-       DUK_ASSERT(end_pos >= 0 && end_pos <= len);
-       DUK_ASSERT(end_pos >= start_pos);
-
-       duk_substring(ctx, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
-       return 1;
-}
-#else  /* DUK_USE_SECTION_B */
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx) {
-       DUK_UNREF(ctx);
-       return DUK_RET_UNSUPPORTED_ERROR;
-}
-#endif  /* DUK_USE_SECTION_B */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_slice(duk_context *ctx) {
-       duk_hstring *h;
-       duk_int_t start_pos, end_pos;
-       duk_int_t len;
-
-       h = duk_push_this_coercible_to_string(ctx);
-       DUK_ASSERT(h != NULL);
-       len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
-
-       /* [ start end str ] */
-
-       start_pos = duk_to_int_clamped(ctx, 0, -len, len);
-       if (start_pos < 0) {
-               start_pos = len + start_pos;
-       }
-       if (duk_is_undefined(ctx, 1)) {
-               end_pos = len;
-       } else {
-               end_pos = duk_to_int_clamped(ctx, 1, -len, len);
-               if (end_pos < 0) {
-                       end_pos = len + end_pos;
-               }
-       }
-       DUK_ASSERT(start_pos >= 0 && start_pos <= len);
-       DUK_ASSERT(end_pos >= 0 && end_pos <= len);
-
-       if (end_pos < start_pos) {
-               end_pos = start_pos;
-       }
-
-       DUK_ASSERT(end_pos >= start_pos);
-
-       duk_substring(ctx, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
-       return 1;
-}
-
-/*
- *  Case conversion
- */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx) {
-       duk_hthread *thr = (duk_hthread *) ctx;
-       duk_small_int_t uppercase = duk_get_current_magic(ctx);
-
-       (void) duk_push_this_coercible_to_string(ctx);
-       duk_unicode_case_convert_string(thr, (duk_bool_t) uppercase);
-       return 1;
-}
-
-/*
- *  indexOf() and lastIndexOf()
- */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx) {
-       duk_hthread *thr = (duk_hthread *) ctx;
-       duk_hstring *h_this;
-       duk_hstring *h_search;
-       duk_int_t clen_this;
-       duk_int_t cpos;
-       duk_int_t bpos;
-       const duk_uint8_t *p_start, *p_end, *p;
-       const duk_uint8_t *q_start;
-       duk_int_t q_blen;
-       duk_uint8_t firstbyte;
-       duk_uint8_t t;
-       duk_small_int_t is_lastindexof = duk_get_current_magic(ctx);  /* 0=indexOf, 1=lastIndexOf */
-
-       h_this = duk_push_this_coercible_to_string(ctx);
-       DUK_ASSERT(h_this != NULL);
-       clen_this = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h_this);
-
-       h_search = duk_to_hstring(ctx, 0);
-       DUK_ASSERT(h_search != NULL);
-       q_start = DUK_HSTRING_GET_DATA(h_search);
-       q_blen = (duk_int_t) DUK_HSTRING_GET_BYTELEN(h_search);
-
-       duk_to_number(ctx, 1);
-       if (duk_is_nan(ctx, 1) && is_lastindexof) {
-               /* indexOf: NaN should cause pos to be zero.
-                * lastIndexOf: NaN should cause pos to be +Infinity
-                * (and later be clamped to len).
-                */
-               cpos = clen_this;
-       } else {
-               cpos = duk_to_int_clamped(ctx, 1, 0, clen_this);
-       }
-
-       /* Empty searchstring always matches; cpos must be clamped here.
-        * (If q_blen were < 0 due to clamped coercion, it would also be
-        * caught here.)
-        */
-       if (q_blen <= 0) {
-               duk_push_int(ctx, cpos);
-               return 1;
-       }
-       DUK_ASSERT(q_blen > 0);
-
-       bpos = (duk_int_t) duk_heap_strcache_offset_char2byte(thr, h_this, (duk_uint32_t) cpos);
-
-       p_start = DUK_HSTRING_GET_DATA(h_this);
-       p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_this);
-       p = p_start + bpos;
-
-       /* This loop is optimized for size.  For speed, there should be
-        * two separate loops, and we should ensure that memcmp() can be
-        * used without an extra "will searchstring fit" check.  Doing
-        * the preconditioning for 'p' and 'p_end' is easy but cpos
-        * must be updated if 'p' is wound back (backward scanning).
-        */
-
-       firstbyte = q_start[0];  /* leading byte of match string */
-       while (p <= p_end && p >= p_start) {
-               t = *p;
-
-               /* For Ecmascript strings, this check can only match for
-                * initial UTF-8 bytes (not continuation bytes).  For other
-                * strings all bets are off.
-                */
-
-               if ((t == firstbyte) && ((duk_size_t) (p_end - p) >= (duk_size_t) q_blen)) {
-                       DUK_ASSERT(q_blen > 0);  /* no issues with memcmp() zero size, even if broken */
-                       if (DUK_MEMCMP((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) {
-                               duk_push_int(ctx, cpos);
-                               return 1;
-                       }
-               }
-
-               /* track cpos while scanning */
-               if (is_lastindexof) {
-                       /* when going backwards, we decrement cpos 'early';
-                        * 'p' may point to a continuation byte of the char
-                        * at offset 'cpos', but that's OK because we'll
-                        * backtrack all the way to the initial byte.
-                        */
-                       if ((t & 0xc0) != 0x80) {
-                               cpos--;
-                       }
-                       p--;
-               } else {
-                       if ((t & 0xc0) != 0x80) {
-                               cpos++;
-                       }
-                       p++;
-               }
-       }
-
-       /* Not found.  Empty string case is handled specially above. */
-       duk_push_int(ctx, -1);
-       return 1;
-}
-
-/*
- *  replace()
- */
-
-/* XXX: the current implementation works but is quite clunky; it compiles
- * to almost 1,4kB of x86 code so it needs to be simplified (better approach,
- * shared helpers, etc).  Some ideas for refactoring:
- *
- * - a primitive to convert a string into a regexp matcher (reduces matching
- *   code at the cost of making matching much slower)
- * - use replace() as a basic helper for match() and split(), which are both
- *   much simpler
- * - API call to get_prop and to_boolean
- */
-
-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((const void *) p, (const 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((const void *) p, (const void *) q_start, (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;
-}
-#endif  /* DUK_USE_REGEXP_SUPPORT */
-
-#ifdef DUK_USE_REGEXP_SUPPORT
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) {
-       duk_hthread *thr = (duk_hthread *) ctx;
-       duk_bool_t global;
-       duk_int_t prev_last_index;
-       duk_int_t this_index;
-       duk_int_t arr_idx;
-
-       DUK_ASSERT_TOP(ctx, 1);
-       (void) duk_push_this_coercible_to_string(ctx);
-       duk__to_regexp_helper(ctx, 0 /*index*/, 0 /*force_new*/);
-       global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL);
-       DUK_ASSERT_TOP(ctx, 2);
-
-       /* stack[0] = regexp
-        * stack[1] = string
-        */
-
-       if (!global) {
-               duk_regexp_match(thr);  /* -> [ res_obj ] */
-               return 1;  /* return 'res_obj' */
-       }
-
-       /* Global case is more complex. */
-
-       /* [ regexp string ] */
-
-       duk_push_int(ctx, 0);
-       duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX);
-       duk_push_array(ctx);
-
-       /* [ regexp string res_arr ] */
-
-       prev_last_index = 0;
-       arr_idx = 0;
-
-       for (;;) {
-               DUK_ASSERT_TOP(ctx, 3);
-
-               duk_dup(ctx, 0);
-               duk_dup(ctx, 1);
-               duk_regexp_match(thr);  /* -> [ ... regexp string ] -> [ ... res_obj ] */
-
-               if (!duk_is_object(ctx, -1)) {
-                       duk_pop(ctx);
-                       break;
-               }
-
-               duk_get_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX);
-               DUK_ASSERT(duk_is_number(ctx, -1));
-               this_index = duk_get_int(ctx, -1);
-               duk_pop(ctx);
-
-               if (this_index == prev_last_index) {
-                       this_index++;
-                       duk_push_int(ctx, this_index);
-                       duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LAST_INDEX);
-               }
-               prev_last_index = this_index;
-
-               duk_get_prop_index(ctx, -1, 0);  /* match string */
-               duk_put_prop_index(ctx, 2, arr_idx);
-               arr_idx++;
-               duk_pop(ctx);  /* res_obj */
-       }
-
-       if (arr_idx == 0) {
-               duk_push_null(ctx);
-       }
-
-       return 1;  /* return 'res_arr' or 'null' */
-}
-#else  /* DUK_USE_REGEXP_SUPPORT */
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) {
-       DUK_UNREF(ctx);
-       return DUK_RET_UNSUPPORTED_ERROR;
-}
-#endif  /* DUK_USE_REGEXP_SUPPORT */
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_concat(duk_context *ctx) {
-       /* duk_concat() coerces arguments with ToString() in correct order */
-       (void) duk_push_this_coercible_to_string(ctx);
-       duk_insert(ctx, 0);  /* this is relatively expensive */
-       duk_concat(ctx, duk_get_top(ctx));
-       return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_trim(duk_context *ctx) {
-       DUK_ASSERT_TOP(ctx, 0);
-       (void) duk_push_this_coercible_to_string(ctx);
-       duk_trim(ctx, 0);
-       DUK_ASSERT_TOP(ctx, 1);
-       return 1;
-}
-
-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 void *) DUK_HSTRING_GET_DATA(h1),
-                                         (const void *) DUK_HSTRING_GET_DATA(h2),
-                                         (size_t) 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;
-}