DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) {
	duk_context *ctx = (duk_context *) thr;
	duk_hobject *h;
	duk_hstring *h_bc;
	duk_small_int_t re_flags;

	/* [ ... escape_source bytecode ] */

	h_bc = duk_require_hstring(ctx, -1);
	DUK_ASSERT(h_bc != NULL);
	DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h_bc) >= 1);          /* always at least the header */
	DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h_bc) >= 1);
	DUK_ASSERT((duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0] < 0x80);  /* flags always encodes to 1 byte */
	re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0];

	/* [ ... escaped_source bytecode ] */

	duk_push_object(ctx);
	h = duk_known_hobject(ctx, -1);
	duk_insert(ctx, -3);

	/* [ ... regexp_object escaped_source bytecode ] */

	DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP);
	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]);

	duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE);

	/* [ ... regexp_object escaped_source ] */

	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_SOURCE, DUK_PROPDESC_FLAGS_NONE);

	/* [ ... regexp_object ] */

	duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_GLOBAL));
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_GLOBAL, DUK_PROPDESC_FLAGS_NONE);

	duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_IGNORE_CASE));
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_IGNORE_CASE, DUK_PROPDESC_FLAGS_NONE);

	duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_MULTILINE));
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MULTILINE, DUK_PROPDESC_FLAGS_NONE);

	duk_push_int(ctx, 0);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W);

	/* [ ... regexp_object ] */
}
DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hstring *h_str;

	DUK_UNREF(thr);

	/* Vararg function: must be careful to check/require arguments.
	 * The JSON helpers accept invalid indices and treat them like
	 * non-existent optional parameters.
	 */

	h_str = duk_require_hstring(ctx, 0);
	duk_require_valid_index(ctx, 1);

	if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
		duk_set_top(ctx, 2);
		duk_hex_encode(ctx, 1);
		DUK_ASSERT_TOP(ctx, 2);
	} else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
		duk_set_top(ctx, 2);
		duk_base64_encode(ctx, 1);
		DUK_ASSERT_TOP(ctx, 2);
#ifdef DUK_USE_JX
	} else if (h_str == DUK_HTHREAD_STRING_JX(thr)) {
		duk_bi_json_stringify_helper(ctx,
		                             1 /*idx_value*/,
		                             2 /*idx_replacer*/,
		                             3 /*idx_space*/,
		                             DUK_JSON_FLAG_EXT_CUSTOM |
		                             DUK_JSON_FLAG_ASCII_ONLY |
		                             DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);
#endif
#ifdef DUK_USE_JC
	} else if (h_str == DUK_HTHREAD_STRING_JC(thr)) {
		duk_bi_json_stringify_helper(ctx,
		                             1 /*idx_value*/,
		                             2 /*idx_replacer*/,
		                             3 /*idx_space*/,
		                             DUK_JSON_FLAG_EXT_COMPATIBLE |
		                             DUK_JSON_FLAG_ASCII_ONLY /*flags*/);
#endif
	} else {
		return DUK_RET_TYPE_ERROR;
	}
	return 1;
}
Beispiel #3
0
duk_ret_t duk_bi_duk_object_dec(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hstring *h_str;

	/* Vararg function: must be careful to check/require arguments.
	 * The JSON helpers accept invalid indices and treat them like
	 * non-existent optional parameters.
	 */

	h_str = duk_require_hstring(ctx, 0);
	duk_require_valid_index(ctx, 1);

	if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
		duk_set_top(ctx, 2);
		duk_hex_decode(ctx, 1);
		DUK_ASSERT_TOP(ctx, 2);
	} else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
		duk_set_top(ctx, 2);
		duk_base64_decode(ctx, 1);
		DUK_ASSERT_TOP(ctx, 2);
#ifdef DUK_USE_JSONX
	} else if (h_str == DUK_HTHREAD_STRING_JSONX(thr)) {
		duk_bi_json_parse_helper(ctx,
		                         1 /*idx_value*/,
		                         2 /*idx_replacer*/,
		                         DUK_JSON_FLAG_EXT_CUSTOM /*flags*/);
#endif
#ifdef DUK_USE_JSONC
	} else if (h_str == DUK_HTHREAD_STRING_JSONC(thr)) {
		duk_bi_json_parse_helper(ctx,
		                         1 /*idx_value*/,
		                         2 /*idx_replacer*/,
		                         DUK_JSON_FLAG_EXT_COMPATIBLE /*flags*/);
#endif
	} else {
		return DUK_RET_TYPE_ERROR;
	}
	return 1;
}
DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hstring *h_sourcecode;
	duk_idx_t nargs;
	duk_idx_t i;
	duk_small_uint_t comp_flags;
	duk_hcompiledfunction *func;
	duk_hobject *outer_lex_env;
	duk_hobject *outer_var_env;

	/* normal and constructor calls have identical semantics */

	nargs = duk_get_top(ctx);
	for (i = 0; i < nargs; i++) {
		duk_to_string(ctx, i);
	}

	if (nargs == 0) {
		duk_push_string(ctx, "");
		duk_push_string(ctx, "");
	} else if (nargs == 1) {
		/* XXX: cover this with the generic >1 case? */
		duk_push_string(ctx, "");
	} else {
		duk_insert(ctx, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
		duk_push_string(ctx, ",");
		duk_insert(ctx, 1);
		duk_join(ctx, nargs - 1);
	}

	/* [ body formals ], formals is comma separated list that needs to be parsed */

	DUK_ASSERT_TOP(ctx, 2);

	/* XXX: this placeholder is not always correct, but use for now.
	 * It will fail in corner cases; see test-dev-func-cons-args.js.
	 */
	duk_push_string(ctx, "function(");
	duk_dup(ctx, 1);
	duk_push_string(ctx, "){");
	duk_dup(ctx, 0);
	duk_push_string(ctx, "}");
	duk_concat(ctx, 5);

	/* [ body formals source ] */

	DUK_ASSERT_TOP(ctx, 3);

	/* strictness is not inherited, intentional */
	comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR;

	duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */  /* XXX: ignored now */
	h_sourcecode = duk_require_hstring(ctx, -2);
	duk_js_compile(thr,
	               (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode),
	               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode),
	               comp_flags);
	func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);
	DUK_ASSERT(func != NULL);
	DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func));

	/* [ body formals source template ] */

	/* only outer_lex_env matters, as functions always get a new
	 * variable declaration environment.
	 */

	outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
	outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];

	duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/);

	/* [ body formals source template closure ] */

	return 1;
}
DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) {
    duk_context *ctx = (duk_context *) thr;
    duk_re_compiler_ctx re_ctx;
    duk_lexer_point lex_point;
    duk_hstring *h_pattern;
    duk_hstring *h_flags;
    duk__re_disjunction_info ign_disj;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(ctx != NULL);

    /*
     *  Args validation
     */

    /* TypeError if fails */
    h_pattern = duk_require_hstring(ctx, -2);
    h_flags = duk_require_hstring(ctx, -1);

    /*
     *  Create normalized 'source' property (E5 Section 15.10.3).
     */

    /* [ ... pattern flags ] */

    duk__create_escaped_source(thr, -2);

    /* [ ... pattern flags escaped_source ] */

    /*
     *  Init compilation context
     */

    /* [ ... pattern flags escaped_source buffer ] */

    DUK_MEMZERO(&re_ctx, sizeof(re_ctx));
    DUK_LEXER_INITCTX(&re_ctx.lex);  /* duplicate zeroing, expect for (possible) NULL inits */
    re_ctx.thr = thr;
    re_ctx.lex.thr = thr;
    re_ctx.lex.input = DUK_HSTRING_GET_DATA(h_pattern);
    re_ctx.lex.input_length = DUK_HSTRING_GET_BYTELEN(h_pattern);
    re_ctx.lex.token_limit = DUK_RE_COMPILE_TOKEN_LIMIT;
    re_ctx.recursion_limit = DUK_USE_REGEXP_COMPILER_RECLIMIT;
    re_ctx.re_flags = duk__parse_regexp_flags(thr, h_flags);

    DUK_BW_INIT_PUSHBUF(thr, &re_ctx.bw, DUK__RE_INITIAL_BUFSIZE);

    DUK_DD(DUK_DDPRINT("regexp compiler ctx initialized, flags=0x%08lx, recursion_limit=%ld",
                       (unsigned long) re_ctx.re_flags, (long) re_ctx.recursion_limit));

    /*
     *  Init lexer
     */

    lex_point.offset = 0;  /* expensive init, just want to fill window */
    lex_point.line = 1;
    DUK_LEXER_SETPOINT(&re_ctx.lex, &lex_point);

    /*
     *  Compilation
     */

    DUK_DD(DUK_DDPRINT("starting regexp compilation"));

    duk__append_u32(&re_ctx, DUK_REOP_SAVE);
    duk__append_u32(&re_ctx, 0);
    duk__parse_disjunction(&re_ctx, 1 /*expect_eof*/, &ign_disj);
    duk__append_u32(&re_ctx, DUK_REOP_SAVE);
    duk__append_u32(&re_ctx, 1);
    duk__append_u32(&re_ctx, DUK_REOP_MATCH);

    /*
     *  Check for invalid backreferences; note that it is NOT an error
     *  to back-reference a capture group which has not yet been introduced
     *  in the pattern (as in /\1(foo)/); in fact, the backreference will
     *  always match!  It IS an error to back-reference a capture group
     *  which will never be introduced in the pattern.  Thus, we can check
     *  for such references only after parsing is complete.
     */

    if (re_ctx.highest_backref > re_ctx.captures) {
        DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BACKREFS);
    }

    /*
     *  Emit compiled regexp header: flags, ncaptures
     *  (insertion order inverted on purpose)
     */

    duk__insert_u32(&re_ctx, 0, (re_ctx.captures + 1) * 2);
    duk__insert_u32(&re_ctx, 0, re_ctx.re_flags);

    /* [ ... pattern flags escaped_source buffer ] */

    DUK_BW_COMPACT(thr, &re_ctx.bw);
    duk_to_string(ctx, -1);  /* coerce to string */

    /* [ ... pattern flags escaped_source bytecode ] */

    /*
     *  Finalize stack
     */

    duk_remove(ctx, -4);     /* -> [ ... flags escaped_source bytecode ] */
    duk_remove(ctx, -3);     /* -> [ ... escaped_source bytecode ] */

    DUK_DD(DUK_DDPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T",
                       (duk_tval *) duk_get_tval(ctx, -1), (duk_tval *) duk_get_tval(ctx, -2)));
}
Beispiel #6
0
/* Shared helper for providing .source, .global, .multiline, etc getters. */
DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_hthread *thr) {
	duk_hstring *h_bc;
	duk_small_uint_t re_flags;
	duk_hobject *h;
	duk_int_t magic;

	DUK_ASSERT_TOP(thr, 0);

	duk_push_this(thr);
	h = duk_require_hobject(thr, -1);
	magic = duk_get_current_magic(thr);

	if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_REGEXP) {
		duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_INT_SOURCE);
		duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_INT_BYTECODE);
		h_bc = duk_require_hstring(thr, -1);
		re_flags = (duk_small_uint_t) DUK_HSTRING_GET_DATA(h_bc)[0];  /* Safe even if h_bc length is 0 (= NUL) */
		duk_pop(thr);
	} else if (h == thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]) {
		/* In ES2015 and ES2016 a TypeError would be thrown here.
		 * However, this had real world issues so ES2017 draft
		 * allows RegExp.prototype specifically, returning '(?:)'
		 * for .source and undefined for all flags.
		 */
		if (magic != 16 /* .source */) {
			return 0;
		}
		duk_push_literal(thr, "(?:)");  /* .source handled by switch-case */
		re_flags = 0;
	} else {
		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
	}

	/* [ regexp source ] */

	switch (magic) {
	case 0: {  /* global */
		duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_GLOBAL));
		break;
	}
	case 1: {  /* ignoreCase */
		duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_IGNORE_CASE));
		break;
	}
	case 2: {  /* multiline */
		duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_MULTILINE));
		break;
	}
#if 0
	/* Don't provide until implemented to avoid interfering with feature
	 * detection in user code.
	 */
	case 3:    /* sticky */
	case 4: {  /* unicode */
		duk_push_false(thr);
		break;
	}
#endif
	default: {  /* source */
		/* leave 'source' on top */
		break;
	}
	}

	return 1;
}
Beispiel #7
0
/* Object.defineProperty() equivalent C binding. */
DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t flags) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_idx_t idx_base;
	duk_hobject *obj;
	duk_hstring *key;
	duk_idx_t idx_value;
	duk_hobject *get;
	duk_hobject *set;
	duk_uint_t is_data_desc;
	duk_uint_t is_acc_desc;

	DUK_ASSERT_CTX_VALID(ctx);

	obj = duk_require_hobject(ctx, obj_index);

	is_data_desc = flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE);
	is_acc_desc = flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
	if (is_data_desc && is_acc_desc) {
		/* "Have" flags must not be conflicting so that they would
		 * apply to both a plain property and an accessor at the same
		 * time.
		 */
		goto fail_invalid_desc;
	}

	idx_base = duk_get_top_index(ctx);
	if (flags & DUK_DEFPROP_HAVE_SETTER) {
		duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED |
		                                     DUK_TYPE_MASK_OBJECT |
		                                     DUK_TYPE_MASK_LIGHTFUNC);
		set = duk_get_hobject_or_lfunc_coerce(ctx, idx_base);
		if (set != NULL && !DUK_HOBJECT_IS_CALLABLE(set)) {
			goto fail_not_callable;
		}
		idx_base--;
	} else {
		set = NULL;
	}
	if (flags & DUK_DEFPROP_HAVE_GETTER) {
		duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED |
		                                     DUK_TYPE_MASK_OBJECT |
		                                     DUK_TYPE_MASK_LIGHTFUNC);
		get = duk_get_hobject_or_lfunc_coerce(ctx, idx_base);
		if (get != NULL && !DUK_HOBJECT_IS_CALLABLE(get)) {
			goto fail_not_callable;
		}
		idx_base--;
	} else {
		get = NULL;
	}
	if (flags & DUK_DEFPROP_HAVE_VALUE) {
		idx_value = idx_base;
		idx_base--;
	} else {
		idx_value = (duk_idx_t) -1;
	}
	key = duk_require_hstring(ctx, idx_base);

	duk_require_valid_index(ctx, idx_base);

	duk_hobject_define_property_helper(ctx,
	                                   flags /*defprop_flags*/,
	                                   obj,
	                                   key,
	                                   idx_value,
	                                   get,
	                                   set);

	/* Clean up stack */

	duk_set_top(ctx, idx_base);

	/* [ ... obj ... ] */

	return;

 fail_invalid_desc:
	DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_DESCRIPTOR);
	return;

 fail_not_callable:
	DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_CALLABLE);
	return;
}
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx) {
	duk_hobject *obj;
	duk_hstring *key;
	duk_hobject *get;
	duk_hobject *set;
	duk_idx_t idx_value;
	duk_uint_t defprop_flags;

	DUK_ASSERT(ctx != NULL);

	DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T",
	                     (void *) ctx,
	                     (duk_tval *) duk_get_tval(ctx, 0),
	                     (duk_tval *) duk_get_tval(ctx, 1),
	                     (duk_tval *) duk_get_tval(ctx, 2)));

	/* [ obj key desc ] */

	/* Lightfuncs are currently supported by coercing to a temporary
	 * Function object; changes will be allowed (the coerced value is
	 * extensible) but will be lost.
	 */
	obj = duk_require_hobject_or_lfunc_coerce(ctx, 0);
	(void) duk_to_string(ctx, 1);
	key = duk_require_hstring(ctx, 1);
	(void) duk_require_hobject(ctx, 2);

	DUK_ASSERT(obj != NULL);
	DUK_ASSERT(key != NULL);
	DUK_ASSERT(duk_get_hobject(ctx, 2) != NULL);

	/*
	 *  Validate and convert argument property descriptor (an Ecmascript
	 *  object) into a set of defprop_flags and possibly property value,
	 *  getter, and/or setter values on the value stack.
	 *
	 *  Lightfunc set/get values are coerced to full Functions.
	 */

	duk_hobject_prepare_property_descriptor(ctx,
	                                        2 /*idx_desc*/,
	                                        &defprop_flags,
	                                        &idx_value,
	                                        &get,
	                                        &set);

	/*
	 *  Use Object.defineProperty() helper for the actual operation.
	 */

	duk_hobject_define_property_helper(ctx,
	                                   defprop_flags,
	                                   obj,
	                                   key,
	                                   idx_value,
	                                   get,
	                                   set);

	/* Ignore the normalize/validate helper outputs on the value stack,
	 * they're popped automatically.
	 */

	/*
	 *  Return target object.
	 */

	duk_push_hobject(ctx, obj);
	return 1;
}
void duk_regexp_compile(duk_hthread *thr) {
	duk_context *ctx = (duk_context *) thr;
	duk_re_compiler_ctx re_ctx;
	duk_lexer_point lex_point;
	duk_hstring *h_pattern;
	duk_hstring *h_flags;
	duk_hbuffer_dynamic *h_buffer;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(ctx != NULL);

	/*
	 *  Args validation
	 */

	/* TypeError if fails */
	h_pattern = duk_require_hstring(ctx, -2);
	h_flags = duk_require_hstring(ctx, -1);

	/*
	 *  Create normalized 'source' property (E5 Section 15.10.3).
	 */

	/* [ ... pattern flags ] */

	create_escaped_source(thr, -2);

	/* [ ... pattern flags escaped_source ] */

	/*
	 *  Init compilation context
	 */

	duk_push_dynamic_buffer(ctx, 0);
	h_buffer = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, -1);
	DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h_buffer));

	/* [ ... pattern flags escaped_source buffer ] */

	DUK_MEMSET(&re_ctx, 0, sizeof(re_ctx));
	DUK_LEXER_INITCTX(&re_ctx.lex);  /* duplicate zeroing, expect for (possible) NULL inits */
	re_ctx.thr = thr;
	re_ctx.lex.thr = thr;
	re_ctx.lex.input = DUK_HSTRING_GET_DATA(h_pattern);
	re_ctx.lex.input_length = DUK_HSTRING_GET_BYTELEN(h_pattern);
	re_ctx.buf = h_buffer;
	re_ctx.recursion_limit = DUK_RE_COMPILE_RECURSION_LIMIT;
	re_ctx.re_flags = parse_regexp_flags(thr, h_flags);

	DUK_DDPRINT("regexp compiler ctx initialized, flags=0x%08x, recursion_limit=%d",
	            (unsigned int) re_ctx.re_flags, (int) re_ctx.recursion_limit);

	/*
	 *  Init lexer
	 */

	lex_point.offset = 0;		/* expensive init, just want to fill window */
	lex_point.line = 1;
	DUK_LEXER_SETPOINT(&re_ctx.lex, &lex_point);

	/*
	 *  Compilation
	 */

	DUK_DPRINT("starting regexp compilation");

	append_u32(&re_ctx, DUK_REOP_SAVE);
	append_u32(&re_ctx, 0);
	(void) parse_disjunction(&re_ctx, 1);  /* 1 = expect eof */
	append_u32(&re_ctx, DUK_REOP_SAVE);
	append_u32(&re_ctx, 1);
	append_u32(&re_ctx, DUK_REOP_MATCH);

	DUK_DPRINT("regexp bytecode size (before header) is %d bytes",
	           (int) DUK_HBUFFER_GET_SIZE(re_ctx.buf));

	/*
	 *  Check for invalid backreferences; note that it is NOT an error
	 *  to back-reference a capture group which has not yet been introduced
	 *  in the pattern (as in /\1(foo)/); in fact, the backreference will
	 *  always match!  It IS an error to back-reference a capture group
	 *  which will never be introduced in the pattern.  Thus, we can check
	 *  for such references only after parsing is complete.
	 */

	if (re_ctx.highest_backref > re_ctx.captures) {
		DUK_ERROR(thr, DUK_ERR_SYNTAX_ERROR, "invalid backreference(s)");
	}

	/*
	 *  Emit compiled regexp header: flags, ncaptures
	 *  (insertion order inverted on purpose)
	 */

	insert_u32(&re_ctx, 0, (re_ctx.captures + 1) * 2);
	insert_u32(&re_ctx, 0, re_ctx.re_flags);

	DUK_DPRINT("regexp bytecode size (after header) is %d bytes",
	           (int) DUK_HBUFFER_GET_SIZE(re_ctx.buf));
	DUK_DDDPRINT("compiled regexp: %!xO", re_ctx.buf);

	/* [ ... pattern flags escaped_source buffer ] */

	duk_to_string(ctx, -1);  /* coerce to string */

	/* [ ... pattern flags escaped_source bytecode ] */

	/*
	 *  Finalize stack
	 */

	duk_remove(ctx, -4);     /* -> [ ... flags escaped_source bytecode ] */
	duk_remove(ctx, -3);     /* -> [ ... escaped_source bytecode ] */

	DUK_DPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T",
	           duk_get_tval(ctx, -1), duk_get_tval(ctx, -2));
}