You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by al...@apache.org on 2017/02/22 20:52:04 UTC

[09/51] [partial] nifi-minifi-cpp git commit: MINIFI-183 Added civet 1.9.1 sources Added onScheduled hook Added initial two-way TLS-enabled ListenHTTP implementation

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;
+}