/* * RegExp built-ins */ #include "duk_internal.h" #if defined(DUK_USE_REGEXP_SUPPORT) DUK_LOCAL void duk__get_this_regexp(duk_hthread *thr) { duk_hobject *h; duk_push_this(thr); h = duk_require_hobject_with_class(thr, -1, DUK_HOBJECT_CLASS_REGEXP); DUK_ASSERT(h != NULL); DUK_UNREF(h); duk_insert(thr, 0); /* prepend regexp to valstack 0 index */ } /* XXX: much to improve (code size) */ DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_hthread *thr) { duk_hobject *h_pattern; DUK_ASSERT_TOP(thr, 2); h_pattern = duk_get_hobject(thr, 0); if (!duk_is_constructor_call(thr) && h_pattern != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP && duk_is_undefined(thr, 1)) { /* Called as a function, pattern has [[Class]] "RegExp" and * flags is undefined -> return object as is. */ /* XXX: ES2015 has a NewTarget SameValue() check which is not * yet implemented. */ duk_dup_0(thr); return 1; } /* Else functionality is identical for function call and constructor * call. */ if (h_pattern != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP) { duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_SOURCE); if (duk_is_undefined(thr, 1)) { /* In ES5 one would need to read the flags individually; * in ES2015 just read .flags. */ duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS); } else { /* In ES2015 allowed; overrides argument RegExp flags. */ duk_dup_1(thr); } } else { if (duk_is_undefined(thr, 0)) { duk_push_hstring_empty(thr); } else { duk_dup_0(thr); duk_to_string(thr, -1); /* Rejects Symbols. */ } if (duk_is_undefined(thr, 1)) { duk_push_hstring_empty(thr); } else { duk_dup_1(thr); duk_to_string(thr, -1); /* Rejects Symbols. */ } /* [ ... pattern flags ] */ } DUK_DDD(DUK_DDDPRINT("RegExp constructor/function call, pattern=%!T, flags=%!T", (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1))); /* [ ... pattern flags ] (both uncoerced) */ duk_to_string(thr, -2); duk_to_string(thr, -1); duk_regexp_compile(thr); /* [ ... bytecode escaped_source ] */ duk_regexp_create_instance(thr); /* [ ... RegExp ] */ return 1; } DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_hthread *thr) { duk__get_this_regexp(thr); /* [ regexp input ] */ duk_regexp_match(thr); /* [ result ] */ return 1; } DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_hthread *thr) { duk__get_this_regexp(thr); /* [ regexp input ] */ /* result object is created and discarded; wasteful but saves code space */ duk_regexp_match(thr); /* [ result ] */ duk_push_boolean(thr, (duk_is_null(thr, -1) ? 0 : 1)); return 1; } DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_tostring(duk_hthread *thr) { /* This must be generic in ES2015 and later. */ DUK_ASSERT_TOP(thr, 0); duk_push_this(thr); duk_push_literal(thr, "/"); duk_get_prop_stridx(thr, 0, DUK_STRIDX_SOURCE); duk_dup_m2(thr); /* another "/" */ duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS); duk_concat(thr, 4); return 1; } DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_hthread *thr) { /* .flags is ES2015 but present even when ES2015 bindings are * disabled because the constructor relies on it. */ duk_uint8_t buf[8]; /* enough for all flags + NUL */ duk_uint8_t *p = buf; /* .flags is generic and works on any object. */ duk_push_this(thr); (void) duk_require_hobject(thr, -1); if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL)) { *p++ = DUK_ASC_LC_G; } if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_IGNORE_CASE, NULL)) { *p++ = DUK_ASC_LC_I; } if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_MULTILINE, NULL)) { *p++ = DUK_ASC_LC_M; } /* .unicode: to be added */ /* .sticky: to be added */ *p++ = DUK_ASC_NUL; DUK_ASSERT((duk_size_t) (p - buf) <= sizeof(buf)); duk_push_string(thr, (const char *) buf); return 1; } /* 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_xget_owndataprop_stridx_short(thr, 0, DUK_STRIDX_INT_SOURCE); duk_xget_owndataprop_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; } #endif /* DUK_USE_REGEXP_SUPPORT */