cosmopolitan/third_party/duktape/duk_bi_regexp.c

227 lines
5.8 KiB
C

/*
* RegExp built-ins
*/
#include "third_party/duktape/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 */