http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_string.c
----------------------------------------------------------------------
diff --git 
a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_string.c
 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_string.c
new file mode 100644
index 0000000..bf43750
--- /dev/null
+++ 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_string.c
@@ -0,0 +1,1314 @@
+/*
+ *  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;
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thread.c
----------------------------------------------------------------------
diff --git 
a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thread.c
 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thread.c
new file mode 100644
index 0000000..813c8a1
--- /dev/null
+++ 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thread.c
@@ -0,0 +1,305 @@
+/*
+ *  Thread builtins
+ */
+
+#include "duk_internal.h"
+
+/*
+ *  Constructor
+ */
+
+DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_context *ctx) {
+       duk_hthread *new_thr;
+       duk_hobject *func;
+
+       /* XXX: need a duk_require_func_or_lfunc_coerce() */
+       if (!duk_is_callable(ctx, 0)) {
+               return DUK_RET_TYPE_ERROR;
+       }
+       func = duk_require_hobject_or_lfunc_coerce(ctx, 0);
+       DUK_ASSERT(func != NULL);
+
+       duk_push_thread(ctx);
+       new_thr = (duk_hthread *) duk_get_hobject(ctx, -1);
+       DUK_ASSERT(new_thr != NULL);
+       new_thr->state = DUK_HTHREAD_STATE_INACTIVE;
+
+       /* push initial function call to new thread stack; this is
+        * picked up by resume().
+        */
+       duk_push_hobject((duk_context *) new_thr, func);
+
+       return 1;  /* return thread */
+}
+
+/*
+ *  Resume a thread.
+ *
+ *  The thread must be in resumable state, either (a) new thread which hasn't
+ *  yet started, or (b) a thread which has previously yielded.  This method
+ *  must be called from an Ecmascript function.
+ *
+ *  Args:
+ *    - thread
+ *    - value
+ *    - isError (defaults to false)
+ *
+ *  Note: yield and resume handling is currently asymmetric.
+ */
+
+DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
+       duk_hthread *thr = (duk_hthread *) ctx;
+       duk_hthread *thr_resume;
+       duk_tval *tv;
+       duk_hobject *func;
+       duk_hobject *caller_func;
+       duk_small_int_t is_error;
+
+       DUK_DDD(DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, 
is_error=%!T",
+                            (duk_tval *) duk_get_tval(ctx, 0),
+                            (duk_tval *) duk_get_tval(ctx, 1),
+                            (duk_tval *) duk_get_tval(ctx, 2)));
+
+       DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
+       DUK_ASSERT(thr->heap->curr_thread == thr);
+
+       thr_resume = duk_require_hthread(ctx, 0);
+       is_error = (duk_small_int_t) duk_to_boolean(ctx, 2);
+       duk_set_top(ctx, 2);
+
+       /* [ thread value ] */
+
+       /*
+        *  Thread state and calling context checks
+        */
+
+       if (thr->callstack_top < 2) {
+               DUK_DD(DUK_DDPRINT("resume state invalid: callstack should 
contain at least 2 entries (caller and Duktape.Thread.resume)"));
+               goto state_error;
+       }
+       DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != 
NULL);  /* us */
+       
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + 
thr->callstack_top - 1)));
+       DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != 
NULL);  /* caller */
+
+       caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2);
+       if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(caller_func)) {
+               DUK_DD(DUK_DDPRINT("resume state invalid: caller must be 
Ecmascript code"));
+               goto state_error;
+       }
+
+       /* Note: there is no requirement that: 'thr->callstack_preventcount == 
1'
+        * like for yield.
+        */
+
+       if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE &&
+           thr_resume->state != DUK_HTHREAD_STATE_YIELDED) {
+               DUK_DD(DUK_DDPRINT("resume state invalid: target thread must be 
INACTIVE or YIELDED"));
+               goto state_error;
+       }
+
+       DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE ||
+                  thr_resume->state == DUK_HTHREAD_STATE_YIELDED);
+
+       /* Further state-dependent pre-checks */
+
+       if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
+               /* no pre-checks now, assume a previous yield() has left things 
in
+                * tip-top shape (longjmp handler will assert for these).
+                */
+       } else {
+               DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE);
+
+               if ((thr_resume->callstack_top != 0) ||
+                   (thr_resume->valstack_top - thr_resume->valstack != 1)) {
+                       goto state_invalid_initial;
+               }
+               tv = &thr_resume->valstack_top[-1];
+               DUK_ASSERT(tv >= thr_resume->valstack && tv < 
thr_resume->valstack_top);
+               if (!DUK_TVAL_IS_OBJECT(tv)) {
+                       goto state_invalid_initial;
+               }
+               func = DUK_TVAL_GET_OBJECT(tv);
+               DUK_ASSERT(func != NULL);
+               if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
+                       /* Note: cannot be a bound function either right now,
+                        * this would be easy to relax though.
+                        */
+                       goto state_invalid_initial;
+               }
+
+       }
+
+       /*
+        *  The error object has been augmented with a traceback and other
+        *  info from its creation point -- usually another thread.  The
+        *  error handler is called here right before throwing, but it also
+        *  runs in the resumer's thread.  It might be nice to get a traceback
+        *  from the resumee but this is not the case now.
+        */
+
+#if defined(DUK_USE_AUGMENT_ERROR_THROW)
+       if (is_error) {
+               DUK_ASSERT_TOP(ctx, 2);  /* value (error) is at stack top */
+               duk_err_augment_error_throw(thr);  /* in resumer's context */
+       }
+#endif
+
+#ifdef DUK_USE_DEBUG
+       if (is_error) {
+               DUK_DDD(DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T",
+                                    (duk_tval *) duk_get_tval(ctx, 0),
+                                    (duk_tval *) duk_get_tval(ctx, 1)));
+       } else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
+               DUK_DDD(DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T",
+                                    (duk_tval *) duk_get_tval(ctx, 0),
+                                    (duk_tval *) duk_get_tval(ctx, 1)));
+       } else {
+               DUK_DDD(DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T",
+                                    (duk_tval *) duk_get_tval(ctx, 0),
+                                    (duk_tval *) duk_get_tval(ctx, 1)));
+       }
+#endif
+
+       thr->heap->lj.type = DUK_LJ_TYPE_RESUME;
+
+       /* lj value2: thread */
+       DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
+       DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value2, 
&thr->valstack_bottom[0]);  /* side effects */
+
+       /* lj value1: value */
+       DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top);
+       DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, 
&thr->valstack_bottom[1]);  /* side effects */
+       DUK_TVAL_CHKFAST_INPLACE(&thr->heap->lj.value1);
+
+       thr->heap->lj.iserror = is_error;
+
+       DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* call is from 
executor, so we know we have a jmpbuf */
+       duk_err_longjmp(thr);  /* execution resumes in bytecode executor */
+       return 0;  /* never here */
+
+ state_invalid_initial:
+       DUK_ERROR_TYPE(thr, "invalid initial thread state/stack");
+       return 0;  /* never here */
+
+ state_error:
+       DUK_ERROR_TYPE(thr, "invalid state");
+       return 0;  /* never here */
+}
+
+/*
+ *  Yield the current thread.
+ *
+ *  The thread must be in yieldable state: it must have a resumer, and there
+ *  must not be any yield-preventing calls (native calls and constructor calls,
+ *  currently) in the thread's call stack (otherwise a resume would not be
+ *  possible later).  This method must be called from an Ecmascript function.
+ *
+ *  Args:
+ *    - value
+ *    - isError (defaults to false)
+ *
+ *  Note: yield and resume handling is currently asymmetric.
+ */
+
+DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) {
+       duk_hthread *thr = (duk_hthread *) ctx;
+       duk_hobject *caller_func;
+       duk_small_int_t is_error;
+
+       DUK_DDD(DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T",
+                            (duk_tval *) duk_get_tval(ctx, 0),
+                            (duk_tval *) duk_get_tval(ctx, 1)));
+
+       DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
+       DUK_ASSERT(thr->heap->curr_thread == thr);
+
+       is_error = (duk_small_int_t) duk_to_boolean(ctx, 1);
+       duk_set_top(ctx, 1);
+
+       /* [ value ] */
+
+       /*
+        *  Thread state and calling context checks
+        */
+
+       if (!thr->resumer) {
+               DUK_DD(DUK_DDPRINT("yield state invalid: current thread must 
have a resumer"));
+               goto state_error;
+       }
+       DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
+
+       if (thr->callstack_top < 2) {
+               DUK_DD(DUK_DDPRINT("yield state invalid: callstack should 
contain at least 2 entries (caller and Duktape.Thread.yield)"));
+               goto state_error;
+       }
+       DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != 
NULL);  /* us */
+       
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + 
thr->callstack_top - 1)));
+       DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != 
NULL);  /* caller */
+
+       caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2);
+       if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(caller_func)) {
+               DUK_DD(DUK_DDPRINT("yield state invalid: caller must be 
Ecmascript code"));
+               goto state_error;
+       }
+
+       DUK_ASSERT(thr->callstack_preventcount >= 1);  /* should never be zero, 
because we (Duktape.Thread.yield) are on the stack */
+       if (thr->callstack_preventcount != 1) {
+               /* Note: the only yield-preventing call is 
Duktape.Thread.yield(), hence check for 1, not 0 */
+               DUK_DD(DUK_DDPRINT("yield state invalid: there must be no 
yield-preventing calls in current thread callstack (preventcount is %ld)",
+                                  (long) thr->callstack_preventcount));
+               goto state_error;
+       }
+
+       /*
+        *  The error object has been augmented with a traceback and other
+        *  info from its creation point -- usually the current thread.
+        *  The error handler, however, is called right before throwing
+        *  and runs in the yielder's thread.
+        */
+
+#if defined(DUK_USE_AUGMENT_ERROR_THROW)
+       if (is_error) {
+               DUK_ASSERT_TOP(ctx, 1);  /* value (error) is at stack top */
+               duk_err_augment_error_throw(thr);  /* in yielder's context */
+       }
+#endif
+
+#ifdef DUK_USE_DEBUG
+       if (is_error) {
+               DUK_DDD(DUK_DDDPRINT("YIELD ERROR: value=%!T",
+                                    (duk_tval *) duk_get_tval(ctx, 0)));
+       } else {
+               DUK_DDD(DUK_DDDPRINT("YIELD NORMAL: value=%!T",
+                                    (duk_tval *) duk_get_tval(ctx, 0)));
+       }
+#endif
+
+       /*
+        *  Process yield
+        *
+        *  After longjmp(), processing continues in bytecode executor longjmp
+        *  handler, which will e.g. update thr->resumer to NULL.
+        */
+
+       thr->heap->lj.type = DUK_LJ_TYPE_YIELD;
+
+       /* lj value1: value */
+       DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
+       DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, 
&thr->valstack_bottom[0]);  /* side effects */
+       DUK_TVAL_CHKFAST_INPLACE(&thr->heap->lj.value1);
+
+       thr->heap->lj.iserror = is_error;
+
+       DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* call is from 
executor, so we know we have a jmpbuf */
+       duk_err_longjmp(thr);  /* execution resumes in bytecode executor */
+       return 0;  /* never here */
+
+ state_error:
+       DUK_ERROR_TYPE(thr, "invalid state");
+       return 0;  /* never here */
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_context *ctx) {
+       duk_push_current_thread(ctx);
+       return 1;
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thrower.c
----------------------------------------------------------------------
diff --git 
a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thrower.c
 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thrower.c
new file mode 100644
index 0000000..a71512a
--- /dev/null
+++ 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_thrower.c
@@ -0,0 +1,10 @@
+/*
+ *  Type error thrower, E5 Section 13.2.3.
+ */
+
+#include "duk_internal.h"
+
+DUK_INTERNAL duk_ret_t duk_bi_type_error_thrower(duk_context *ctx) {
+       DUK_UNREF(ctx);
+       return DUK_RET_TYPE_ERROR;
+}

Reply via email to