From b74e9cd23f1f0dbfff52b0849e6794d282d7349f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20B=C3=A1tyai?= Date: Mon, 17 Aug 2015 15:16:44 +0200 Subject: [PATCH] Implement RegExp.prototype.compile() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai.u-szeged@partner.samsung.com --- .../ecma-builtin-regexp-prototype.cpp | 167 ++++++++++++++++++ .../ecma-builtin-regexp-prototype.inc.h | 3 + .../ecma/operations/ecma-regexp-object.cpp | 154 ++++++++++------ .../ecma/operations/ecma-regexp-object.h | 8 + jerry-core/lit/lit-magic-strings.inc.h | 1 + jerry-core/parser/regexp/re-compiler.cpp | 19 +- jerry-core/parser/regexp/re-compiler.h | 2 +- tests/jerry/regexp-routines.js | 45 +++++ 8 files changed, 341 insertions(+), 58 deletions(-) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp index 2afe5a8df9..79514776ed 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp @@ -45,6 +45,173 @@ * @{ */ +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ANNEXB_BUILTIN + +/** + * The RegExp.prototype object's 'compile' routine + * + * See also: + * ECMA-262 v5, B.2.5.1 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this argument */ + ecma_value_t pattern_arg, /**< pattern or RegExp object */ + ecma_value_t flags_arg) /**< flags */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + ecma_object_t *this_obj_p = ecma_get_object_from_value (this_arg); + + if (ecma_object_get_class_name (this_obj_p) != LIT_MAGIC_STRING_REGEXP_UL) + { + /* Compile can only be called on RegExp objects. */ + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + else + { + ecma_string_t *pattern_string_p = NULL; + uint8_t flags = 0; + + if (ecma_is_value_object (pattern_arg) + && ecma_object_get_class_name (ecma_get_object_from_value (pattern_arg)) == LIT_MAGIC_STRING_REGEXP_UL) + { + if (!ecma_is_value_undefined (flags_arg)) + { + ret_value = ecma_raise_type_error ("Invalid argument of RegExp compile."); + } + else + { + /* Compile from existing RegExp pbject. */ + ecma_object_t *target_p = ecma_get_object_from_value (pattern_arg); + + /* Get source. */ + ecma_string_t *magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_SOURCE); + ecma_property_t *prop_p = ecma_get_named_data_property (target_p, magic_string_p); + pattern_string_p = ecma_get_string_from_value (ecma_get_named_data_property_value (prop_p)); + ecma_deref_ecma_string (magic_string_p); + + /* Get flags. */ + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_GLOBAL); + prop_p = ecma_get_named_data_property (target_p, magic_string_p); + + if (ecma_is_value_true (ecma_get_named_data_property_value (prop_p))) + { + flags |= RE_FLAG_GLOBAL; + } + + ecma_deref_ecma_string (magic_string_p); + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_IGNORECASE_UL); + prop_p = ecma_get_named_data_property (target_p, magic_string_p); + + if (ecma_is_value_true (ecma_get_named_data_property_value (prop_p))) + { + flags |= RE_FLAG_IGNORE_CASE; + } + + ecma_deref_ecma_string (magic_string_p); + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_MULTILINE); + prop_p = ecma_get_named_data_property (target_p, magic_string_p); + + if (ecma_is_value_true (ecma_get_named_data_property_value (prop_p))) + { + flags |= RE_FLAG_MULTILINE; + } + + ecma_deref_ecma_string (magic_string_p); + + /* Get bytecode property. */ + ecma_property_t *bc_prop_p = ecma_get_internal_property (this_obj_p, + ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); + re_bytecode_t *old_bc_p = ECMA_GET_NON_NULL_POINTER (re_bytecode_t, + bc_prop_p->u.internal_property.value); + + FIXME ("We currently have to re-compile the bytecode, because we can't copy it without knowing its length.") + re_bytecode_t *new_bc_p = NULL; + ecma_completion_value_t bc_comp = re_compile_bytecode (&new_bc_p, pattern_string_p, flags); + /* Should always succeed, since we're compiling from a source that has been compiled previously. */ + JERRY_ASSERT (ecma_is_completion_value_empty (bc_comp)); + + mem_heap_free_block (old_bc_p); + ECMA_SET_POINTER (bc_prop_p->u.internal_property.value, new_bc_p); + + re_initialize_props (this_obj_p, pattern_string_p, flags); + + ret_value = ecma_make_normal_completion_value (ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); + } + } + else + { + /* Get source string. */ + if (!ecma_is_value_undefined (pattern_arg)) + { + ECMA_TRY_CATCH (regexp_str_value, + ecma_op_to_string (pattern_arg), + ret_value); + + if (ecma_string_get_length (ecma_get_string_from_value (regexp_str_value)) == 0) + { + pattern_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP); + } + else + { + pattern_string_p = ecma_copy_or_ref_ecma_string (ecma_get_string_from_value (regexp_str_value)); + } + + ECMA_FINALIZE (regexp_str_value); + } + else + { + pattern_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP); + } + + /* Parse flags. */ + if (ecma_is_completion_value_empty (ret_value) && !ecma_is_value_undefined (flags_arg)) + { + ECMA_TRY_CATCH (flags_str_value, + ecma_op_to_string (flags_arg), + ret_value); + + ECMA_TRY_CATCH (flags_dummy, + re_parse_regexp_flags (ecma_get_string_from_value (flags_str_value), &flags), + ret_value); + ECMA_FINALIZE (flags_dummy); + ECMA_FINALIZE (flags_str_value); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + ecma_property_t *bc_prop_p = ecma_get_internal_property (this_obj_p, + ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); + re_bytecode_t *old_bc_p = ECMA_GET_NON_NULL_POINTER (re_bytecode_t, + bc_prop_p->u.internal_property.value); + + /* Try to compile bytecode from new source. */ + re_bytecode_t *new_bc_p = NULL; + ECMA_TRY_CATCH (bc_dummy, re_compile_bytecode (&new_bc_p, pattern_string_p, flags), ret_value); + + /* Replace old bytecode with new one. */ + mem_heap_free_block (old_bc_p); + ECMA_SET_POINTER (bc_prop_p->u.internal_property.value, new_bc_p); + re_initialize_props (this_obj_p, pattern_string_p, flags); + ret_value = ecma_make_normal_completion_value (ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); + + ECMA_FINALIZE (bc_dummy); + } + + if (pattern_string_p != NULL) + { + ecma_deref_ecma_string (pattern_string_p); + } + } + } + + return ret_value; +} /* ecma_builtin_regexp_prototype_compile */ + +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ANNEXB_BUILTIN */ + /** * The RegExp.prototype object's 'exec' routine * diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h index 3190572db8..9d992bc90d 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h @@ -86,6 +86,9 @@ NUMBER_VALUE (LIT_MAGIC_STRING_LASTINDEX_UL, ECMA_PROPERTY_NOT_ENUMERABLE, ECMA_PROPERTY_NOT_CONFIGURABLE) +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ANNEXB_BUILTIN +ROUTINE (LIT_MAGIC_STRING_COMPILE, ecma_builtin_regexp_prototype_compile, 2, 1) +#endif ROUTINE (LIT_MAGIC_STRING_EXEC, ecma_builtin_regexp_prototype_exec, 1, 1) ROUTINE (LIT_MAGIC_STRING_TEST, ecma_builtin_regexp_prototype_test, 1, 1) ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ecma_builtin_regexp_prototype_to_string, 0, 0) diff --git a/jerry-core/ecma/operations/ecma-regexp-object.cpp b/jerry-core/ecma/operations/ecma-regexp-object.cpp index 78dd9ff2df..d2e84fab45 100644 --- a/jerry-core/ecma/operations/ecma-regexp-object.cpp +++ b/jerry-core/ecma/operations/ecma-regexp-object.cpp @@ -62,7 +62,7 @@ * @return completion value * Returned value must be freed with ecma_free_completion_value */ -static ecma_completion_value_t +ecma_completion_value_t re_parse_regexp_flags (ecma_string_t *flags_str_p, /**< Input string with flags */ uint8_t *flags_p) /**< Output: parsed flag bits */ { @@ -119,6 +119,101 @@ re_parse_regexp_flags (ecma_string_t *flags_str_p, /**< Input string with flags return ret_value; } /* re_parse_regexp_flags */ +/* + * Initializes the source, global, ignoreCase, multiline, and lastIndex properties of RegExp instance. + */ +void +re_initialize_props (ecma_object_t *re_obj_p, /**< RegExp obejct */ + ecma_string_t *source_p, /**< source string */ + uint8_t flags) /**< flags */ +{ + /* Set source property. ECMA-262 v5, 15.10.7.1 */ + ecma_string_t *magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_SOURCE); + ecma_property_t *prop_p = ecma_find_named_property (re_obj_p, magic_string_p); + + if (prop_p == NULL) + { + prop_p = ecma_create_named_data_property (re_obj_p, + magic_string_p, + false, false, false); + } + + ecma_deref_ecma_string (magic_string_p); + JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDDATA); + ecma_named_data_property_assign_value (re_obj_p, + prop_p, + ecma_make_string_value (source_p)); + + ecma_simple_value_t prop_value; + + /* Set global property. ECMA-262 v5, 15.10.7.2 */ + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_GLOBAL); + prop_p = ecma_find_named_property (re_obj_p, magic_string_p); + + if (prop_p == NULL) + { + prop_p = ecma_create_named_data_property (re_obj_p, + magic_string_p, + false, false, false); + } + + ecma_deref_ecma_string (magic_string_p); + prop_value = flags & RE_FLAG_GLOBAL ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; + JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDDATA); + ecma_set_named_data_property_value (prop_p, ecma_make_simple_value (prop_value)); + + /* Set ignoreCase property. ECMA-262 v5, 15.10.7.3 */ + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_IGNORECASE_UL); + prop_p = ecma_find_named_property (re_obj_p, magic_string_p); + + if (prop_p == NULL) + { + prop_p = ecma_create_named_data_property (re_obj_p, + magic_string_p, + false, false, false); + } + + ecma_deref_ecma_string (magic_string_p); + prop_value = flags & RE_FLAG_IGNORE_CASE ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; + JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDDATA); + ecma_set_named_data_property_value (prop_p, ecma_make_simple_value (prop_value)); + + /* Set multiline property. ECMA-262 v5, 15.10.7.4 */ + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_MULTILINE); + prop_p = ecma_find_named_property (re_obj_p, magic_string_p); + + if (prop_p == NULL) + { + prop_p = ecma_create_named_data_property (re_obj_p, + magic_string_p, + false, false, false); + } + + ecma_deref_ecma_string (magic_string_p); + prop_value = flags & RE_FLAG_MULTILINE ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; + JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDDATA); + ecma_set_named_data_property_value (prop_p, ecma_make_simple_value (prop_value)); + + /* Set lastIndex property. ECMA-262 v5, 15.10.7.5 */ + magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL); + prop_p = ecma_find_named_property (re_obj_p, magic_string_p); + + if (prop_p == NULL) + { + prop_p = ecma_create_named_data_property (re_obj_p, + magic_string_p, + true, false, false); + } + + ecma_deref_ecma_string (magic_string_p); + + ecma_number_t *lastindex_num_p = ecma_alloc_number (); + *lastindex_num_p = ECMA_NUMBER_ZERO; + JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDDATA); + ecma_named_data_property_assign_value (re_obj_p, prop_p, ecma_make_number_value (lastindex_num_p)); + ecma_dealloc_number (lastindex_num_p); +} /* re_initialize_props */ + /** * RegExp object creation operation. * @@ -155,64 +250,19 @@ ecma_op_create_regexp_object (ecma_string_t *pattern_p, /**< input pattern */ ecma_property_t *class_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CLASS); class_prop_p->u.internal_property.value = LIT_MAGIC_STRING_REGEXP_UL; - /* Set source property. ECMA-262 v5, 15.10.7.1 */ - ecma_string_t *magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_SOURCE); - ecma_property_t *source_prop_p = ecma_create_named_data_property (obj_p, - magic_string_p, - false, false, false); - ecma_deref_ecma_string (magic_string_p); - ecma_set_named_data_property_value (source_prop_p, - ecma_make_string_value (ecma_copy_or_ref_ecma_string (pattern_p))); - - ecma_simple_value_t prop_value; - - /* Set global property. ECMA-262 v5, 15.10.7.2*/ - magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_GLOBAL); - ecma_property_t *global_prop_p = ecma_create_named_data_property (obj_p, - magic_string_p, - false, false, false); - ecma_deref_ecma_string (magic_string_p); - prop_value = flags & RE_FLAG_GLOBAL ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; - ecma_set_named_data_property_value (global_prop_p, ecma_make_simple_value (prop_value)); - - /* Set ignoreCase property. ECMA-262 v5, 15.10.7.3*/ - magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_IGNORECASE_UL); - ecma_property_t *ignorecase_prop_p = ecma_create_named_data_property (obj_p, - magic_string_p, - false, false, false); - ecma_deref_ecma_string (magic_string_p); - prop_value = flags & RE_FLAG_IGNORE_CASE ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; - ecma_set_named_data_property_value (ignorecase_prop_p, ecma_make_simple_value (prop_value)); - - - /* Set multiline property. ECMA-262 v5, 15.10.7.4*/ - magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_MULTILINE); - ecma_property_t *multiline_prop_p = ecma_create_named_data_property (obj_p, - magic_string_p, - false, false, false); - ecma_deref_ecma_string (magic_string_p); - prop_value = flags & RE_FLAG_MULTILINE ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; - ecma_set_named_data_property_value (multiline_prop_p, ecma_make_simple_value (prop_value)); - - /* Set lastIndex property. ECMA-262 v5, 15.10.7.5*/ - magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL); - ecma_property_t *lastindex_prop_p = ecma_create_named_data_property (obj_p, - magic_string_p, - true, false, false); - ecma_deref_ecma_string (magic_string_p); - - ecma_number_t *lastindex_num_p = ecma_alloc_number (); - *lastindex_num_p = ECMA_NUMBER_ZERO; - ecma_named_data_property_assign_value (obj_p, lastindex_prop_p, ecma_make_number_value (lastindex_num_p)); - ecma_dealloc_number (lastindex_num_p); + re_initialize_props (obj_p, pattern_p, flags); /* Set bytecode internal property. */ ecma_property_t *bytecode_prop_p; bytecode_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); /* Compile bytecode. */ - ECMA_TRY_CATCH (empty, re_compile_bytecode (bytecode_prop_p, pattern_p, flags), ret_value); + re_bytecode_t *bc_p = NULL; + ECMA_TRY_CATCH (empty, re_compile_bytecode (&bc_p, pattern_p, flags), ret_value); + + ECMA_SET_POINTER (bytecode_prop_p->u.internal_property.value, bc_p); ret_value = ecma_make_normal_completion_value (ecma_make_object_value (obj_p)); + ECMA_FINALIZE (empty); if (ecma_is_completion_value_throw (ret_value)) diff --git a/jerry-core/ecma/operations/ecma-regexp-object.h b/jerry-core/ecma/operations/ecma-regexp-object.h index c06ca83514..5598ace06a 100644 --- a/jerry-core/ecma/operations/ecma-regexp-object.h +++ b/jerry-core/ecma/operations/ecma-regexp-object.h @@ -65,6 +65,14 @@ re_set_result_array_properties (ecma_object_t *array_obj_p, uint32_t num_of_elements, int32_t index); +extern ecma_completion_value_t +re_parse_regexp_flags (ecma_string_t *flags_str_p, uint8_t *flags_p); + +extern void +re_initialize_props (ecma_object_t *re_obj_p, + ecma_string_t *source_p, + uint8_t flags); + /** * @} * @} diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 397897a185..5f9f940622 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -211,6 +211,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INVALID_DATE_UL, "Invalid Date") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_APPLY, "apply") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CALL, "call") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIND, "bind") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMPILE, "compile") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EXEC, "exec") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TEST, "test") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NAME, "name") diff --git a/jerry-core/parser/regexp/re-compiler.cpp b/jerry-core/parser/regexp/re-compiler.cpp index 9585f1dbc9..473710fa07 100644 --- a/jerry-core/parser/regexp/re-compiler.cpp +++ b/jerry-core/parser/regexp/re-compiler.cpp @@ -633,7 +633,7 @@ re_parse_alternative (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -re_compile_bytecode (ecma_property_t *bytecode_p, /**< bytecode */ +re_compile_bytecode (re_bytecode_t **out_bytecode_p, /**< out:pointer to bytecode */ ecma_string_t *pattern_str_p, /**< pattern */ uint8_t flags) /**< flags */ { @@ -684,12 +684,21 @@ re_compile_bytecode (ecma_property_t *bytecode_p, /**< bytecode */ } ECMA_FINALIZE (empty); - /* The RegExp bytecode contains at least a RE_OP_SAVE_AT_START opdoce, so it cannot be NULL. */ - JERRY_ASSERT (bc_ctx.block_start_p != NULL); - ECMA_SET_POINTER (bytecode_p->u.internal_property.value, bc_ctx.block_start_p); - MEM_FINALIZE_LOCAL_ARRAY (pattern_start_p); + if (!ecma_is_completion_value_empty (ret_value)) + { + /* Compilation failed, free bytecode. */ + mem_heap_free_block (bc_ctx.block_start_p); + *out_bytecode_p = NULL; + } + else + { + /* The RegExp bytecode contains at least a RE_OP_SAVE_AT_START opdoce, so it cannot be NULL. */ + JERRY_ASSERT (bc_ctx.block_start_p != NULL); + *out_bytecode_p = bc_ctx.block_start_p; + } + #ifdef JERRY_ENABLE_LOG re_dump_bytecode (&bc_ctx); #endif diff --git a/jerry-core/parser/regexp/re-compiler.h b/jerry-core/parser/regexp/re-compiler.h index ab599db435..6351c4f3cc 100644 --- a/jerry-core/parser/regexp/re-compiler.h +++ b/jerry-core/parser/regexp/re-compiler.h @@ -96,7 +96,7 @@ typedef struct } re_compiler_ctx_t; ecma_completion_value_t -re_compile_bytecode (ecma_property_t *bytecode_p, ecma_string_t *pattern_str_p, uint8_t flags); +re_compile_bytecode (re_bytecode_t **out_bytecode_p, ecma_string_t *pattern_str_p, uint8_t flags); re_opcode_t re_get_opcode (re_bytecode_t **bc_p); diff --git a/tests/jerry/regexp-routines.js b/tests/jerry/regexp-routines.js index 0c1a6df284..47ec31c68a 100644 --- a/tests/jerry/regexp-routines.js +++ b/tests/jerry/regexp-routines.js @@ -66,3 +66,48 @@ result = re.exec("a"); assert (result.length === 1); assert (result[0] === "a"); assert (re.lastIndex === 1); + +var re1 = /foo/gim; +var re2 = /bar/g; + +try { + re2.compile("asd", "ggim"); + assert (false); +} catch (e) { + assert (e instanceof SyntaxError); + assert (re2 == "/bar/g"); +} + +try { + re2.compile("[", undefined); + assert (false); +} catch (e) { + assert (e instanceof SyntaxError); + assert (re2 == "/bar/g"); +} + +try { + re2.compile(re1, "im"); + assert (false); +} catch (e) { + assert (e instanceof TypeError); + assert (re2 == "/bar/g"); +} + +re2.lastIndex = 2; +re2.compile("asd", "im"); + +assert (re2 == "/asd/im"); +assert (re2.global === false); +assert (re2.ignoreCase === true); +assert (re2.multiline === true); +assert (re2.source === "asd"); +assert (re2.lastIndex === 0); + +re2.compile(re1); +assert (re2.toString() === re1.toString()); +assert (re2.global === re1.global); +assert (re2.ignoreCase === re1.ignoreCase); +assert (re2.multiline === re1.multiline); +assert (re2.source === re1.source); +assert (re2.lastIndex === 0);