cosmopolitan/third_party/quickjs/regexp.c

1411 lines
47 KiB
C
Raw Normal View History

/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "libc/str/str.h"
#include "third_party/quickjs/internal.h"
#include "third_party/quickjs/libregexp.h"
#include "third_party/quickjs/quickjs.h"
asm(".ident\t\"\\n\\n\
QuickJS (MIT License)\\n\
Copyright (c) 2017-2021 Fabrice Bellard\\n\
Copyright (c) 2017-2021 Charlie Gordon\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
void js_regexp_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSRegExp *re = &p->u.regexp;
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
}
/* create a string containing the RegExp bytecode */
JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
JSValueConst flags)
{
const char *str;
int re_flags, mask;
uint8_t *re_bytecode_buf;
size_t i, len;
int re_bytecode_len;
JSValue ret;
char error_msg[64];
re_flags = 0;
if (!JS_IsUndefined(flags)) {
str = JS_ToCStringLen(ctx, &len, flags);
if (!str)
return JS_EXCEPTION;
/* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
for (i = 0; i < len; i++) {
switch(str[i]) {
case 'g':
mask = LRE_FLAG_GLOBAL;
break;
case 'i':
mask = LRE_FLAG_IGNORECASE;
break;
case 'm':
mask = LRE_FLAG_MULTILINE;
break;
case 's':
mask = LRE_FLAG_DOTALL;
break;
case 'u':
mask = LRE_FLAG_UTF16;
break;
case 'y':
mask = LRE_FLAG_STICKY;
break;
default:
goto bad_flags;
}
if ((re_flags & mask) != 0) {
bad_flags:
JS_FreeCString(ctx, str);
return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
}
re_flags |= mask;
}
JS_FreeCString(ctx, str);
}
str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));
if (!str)
return JS_EXCEPTION;
re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
sizeof(error_msg), str, len, re_flags, ctx);
JS_FreeCString(ctx, str);
if (!re_bytecode_buf) {
JS_ThrowSyntaxError(ctx, "%s", error_msg);
return JS_EXCEPTION;
}
ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);
js_free(ctx, re_bytecode_buf);
return ret;
}
/* create a RegExp object from a string containing the RegExp bytecode
and the source pattern */
JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
JSValue pattern, JSValue bc)
{
JSValue obj;
JSObject *p;
JSRegExp *re;
/* sanity check */
if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
JS_ThrowTypeError(ctx, "string expected");
fail:
JS_FreeValue(ctx, bc);
JS_FreeValue(ctx, pattern);
return JS_EXCEPTION;
}
obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
if (JS_IsException(obj))
goto fail;
p = JS_VALUE_GET_OBJ(obj);
re = &p->u.regexp;
re->pattern = JS_VALUE_GET_STRING(pattern);
re->bytecode = JS_VALUE_GET_STRING(bc);
JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
JS_PROP_WRITABLE);
return obj;
}
JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
{
if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(obj);
if (p->class_id == JS_CLASS_REGEXP)
return &p->u.regexp;
}
if (throw_error) {
JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
}
return NULL;
}
/* return < 0 if exception or TRUE/FALSE */
int js_is_regexp(JSContext *ctx, JSValueConst obj)
{
JSValue m;
if (!JS_IsObject(obj))
return FALSE;
m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
if (JS_IsException(m))
return -1;
if (!JS_IsUndefined(m))
return JS_ToBoolFree(ctx, m);
return js_get_regexp(ctx, obj, FALSE) != NULL;
}
JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValue pattern, flags, bc, val;
JSValueConst pat, flags1;
JSRegExp *re;
int pat_is_regexp;
pat = argv[0];
flags1 = argv[1];
pat_is_regexp = js_is_regexp(ctx, pat);
if (pat_is_regexp < 0)
return JS_EXCEPTION;
if (JS_IsUndefined(new_target)) {
/* called as a function */
new_target = JS_GetActiveFunction(ctx);
if (pat_is_regexp && JS_IsUndefined(flags1)) {
JSValue ctor;
BOOL res;
ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
if (JS_IsException(ctor))
return ctor;
res = js_same_value(ctx, ctor, new_target);
JS_FreeValue(ctx, ctor);
if (res)
return JS_DupValue(ctx, pat);
}
}
re = js_get_regexp(ctx, pat, FALSE);
if (re) {
pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
if (JS_IsUndefined(flags1)) {
bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
goto no_compilation;
} else {
flags = JS_ToString(ctx, flags1);
if (JS_IsException(flags))
goto fail;
}
} else {
flags = JS_UNDEFINED;
if (pat_is_regexp) {
pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
if (JS_IsException(pattern))
goto fail;
if (JS_IsUndefined(flags1)) {
flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
if (JS_IsException(flags))
goto fail;
} else {
flags = JS_DupValue(ctx, flags1);
}
} else {
pattern = JS_DupValue(ctx, pat);
flags = JS_DupValue(ctx, flags1);
}
if (JS_IsUndefined(pattern)) {
pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
} else {
val = pattern;
pattern = JS_ToString(ctx, val);
JS_FreeValue(ctx, val);
if (JS_IsException(pattern))
goto fail;
}
}
bc = js_compile_regexp(ctx, pattern, flags);
if (JS_IsException(bc))
goto fail;
JS_FreeValue(ctx, flags);
no_compilation:
return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
fail:
JS_FreeValue(ctx, pattern);
JS_FreeValue(ctx, flags);
return JS_EXCEPTION;
}
JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSRegExp *re1, *re;
JSValueConst pattern1, flags1;
JSValue bc, pattern;
re = js_get_regexp(ctx, this_val, TRUE);
if (!re)
return JS_EXCEPTION;
pattern1 = argv[0];
flags1 = argv[1];
re1 = js_get_regexp(ctx, pattern1, FALSE);
if (re1) {
if (!JS_IsUndefined(flags1))
return JS_ThrowTypeError(ctx, "flags must be undefined");
pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
} else {
bc = JS_UNDEFINED;
if (JS_IsUndefined(pattern1))
pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
else
pattern = JS_ToString(ctx, pattern1);
if (JS_IsException(pattern))
goto fail;
bc = js_compile_regexp(ctx, pattern, flags1);
if (JS_IsException(bc))
goto fail;
}
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
re->pattern = JS_VALUE_GET_STRING(pattern);
re->bytecode = JS_VALUE_GET_STRING(bc);
if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
JS_NewInt32(ctx, 0)) < 0)
return JS_EXCEPTION;
return JS_DupValue(ctx, this_val);
fail:
JS_FreeValue(ctx, pattern);
JS_FreeValue(ctx, bc);
return JS_EXCEPTION;
}
#if 0
static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
{
JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
if (!re)
return JS_EXCEPTION;
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
}
static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
{
JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
int flags;
if (!re)
return JS_EXCEPTION;
flags = lre_get_flags(re->bytecode->u.str8);
return JS_NewInt32(ctx, flags);
}
#endif
JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
{
JSRegExp *re;
JSString *p;
StringBuffer b_s, *b = &b_s;
int i, n, c, c2, bra;
if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
goto empty_regex;
re = js_get_regexp(ctx, this_val, TRUE);
if (!re)
return JS_EXCEPTION;
p = re->pattern;
if (p->len == 0) {
empty_regex:
return JS_NewString(ctx, "(?:)");
}
string_buffer_init2(ctx, b, p->len, p->is_wide_char);
/* Escape '/' and newline sequences as needed */
bra = 0;
for (i = 0, n = p->len; i < n;) {
c2 = -1;
switch (c = string_get(p, i++)) {
case '\\':
if (i < n)
c2 = string_get(p, i++);
break;
case ']':
bra = 0;
break;
case '[':
if (!bra) {
if (i < n && string_get(p, i) == ']')
c2 = string_get(p, i++);
bra = 1;
}
break;
case '\n':
c = '\\';
c2 = 'n';
break;
case '\r':
c = '\\';
c2 = 'r';
break;
case '/':
if (!bra) {
c = '\\';
c2 = '/';
}
break;
}
string_buffer_putc16(b, c);
if (c2 >= 0)
string_buffer_putc16(b, c2);
}
return string_buffer_end(b);
}
JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
{
JSRegExp *re;
int flags;
if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
re = js_get_regexp(ctx, this_val, FALSE);
if (!re) {
if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
return JS_UNDEFINED;
else
return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
}
flags = lre_get_flags(re->bytecode->u.str8);
return JS_NewBool(ctx, (flags & mask) != 0);
}
JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
{
char str[8], *p = str;
int res;
if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
if (res < 0)
goto exception;
if (res)
*p++ = 'g';
res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
if (res < 0)
goto exception;
if (res)
*p++ = 'i';
res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
if (res < 0)
goto exception;
if (res)
*p++ = 'm';
res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
if (res < 0)
goto exception;
if (res)
*p++ = 's';
res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
if (res < 0)
goto exception;
if (res)
*p++ = 'u';
res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
if (res < 0)
goto exception;
if (res)
*p++ = 'y';
return JS_NewStringLen(ctx, str, p - str);
exception:
return JS_EXCEPTION;
}
JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue pattern, flags;
StringBuffer b_s, *b = &b_s;
if (!JS_IsObject(this_val))
return JS_ThrowTypeErrorNotAnObject(ctx);
string_buffer_init(ctx, b, 0);
string_buffer_putc8(b, '/');
pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
if (string_buffer_concat_value_free(b, pattern))
goto fail;
string_buffer_putc8(b, '/');
flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
if (string_buffer_concat_value_free(b, flags))
goto fail;
return string_buffer_end(b);
fail:
string_buffer_free(b);
return JS_EXCEPTION;
}
void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
if (it) {
JS_FreeValueRT(rt, it->iterating_regexp);
JS_FreeValueRT(rt, it->iterated_string);
js_free_rt(rt, it);
}
}
void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
if (it) {
JS_MarkValue(rt, it->iterating_regexp, mark_func);
JS_MarkValue(rt, it->iterated_string, mark_func);
}
}
JSValue js_regexp_string_iterator_next(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv,
BOOL *pdone, int magic)
{
JSRegExpStringIteratorData *it;
JSValueConst R, S;
JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
JSString *sp;
it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
if (!it)
goto exception;
if (it->done) {
*pdone = TRUE;
return JS_UNDEFINED;
}
R = it->iterating_regexp;
S = it->iterated_string;
match = JS_RegExpExec(ctx, R, S);
if (JS_IsException(match))
goto exception;
if (JS_IsNull(match)) {
it->done = TRUE;
*pdone = TRUE;
return JS_UNDEFINED;
} else if (it->global) {
matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
if (JS_IsException(matchStr))
goto exception;
if (JS_IsEmptyString(matchStr)) {
int64_t thisIndex, nextIndex;
if (JS_ToLengthFree(ctx, &thisIndex,
JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
goto exception;
sp = JS_VALUE_GET_STRING(S);
nextIndex = string_advance_index(sp, thisIndex, it->unicode);
if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
JS_NewInt64(ctx, nextIndex)) < 0)
goto exception;
}
JS_FreeValue(ctx, matchStr);
} else {
it->done = TRUE;
}
*pdone = FALSE;
return match;
exception:
JS_FreeValue(ctx, match);
JS_FreeValue(ctx, matchStr);
*pdone = FALSE;
return JS_EXCEPTION;
}
JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// [Symbol.matchAll](str)
JSValueConst R = this_val;
JSValue S, C, flags, matcher, iter;
JSValueConst args[2];
JSString *strp;
int64_t lastIndex;
JSRegExpStringIteratorData *it;
if (!JS_IsObject(R))
return JS_ThrowTypeErrorNotAnObject(ctx);
C = JS_UNDEFINED;
flags = JS_UNDEFINED;
matcher = JS_UNDEFINED;
iter = JS_UNDEFINED;
S = JS_ToString(ctx, argv[0]);
if (JS_IsException(S))
goto exception;
C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
if (JS_IsException(C))
goto exception;
flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
if (JS_IsException(flags))
goto exception;
args[0] = R;
args[1] = flags;
matcher = JS_CallConstructor(ctx, C, 2, args);
if (JS_IsException(matcher))
goto exception;
if (JS_ToLengthFree(ctx, &lastIndex,
JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
goto exception;
if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
JS_NewInt64(ctx, lastIndex)) < 0)
goto exception;
iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
if (JS_IsException(iter))
goto exception;
it = js_malloc(ctx, sizeof(*it));
if (!it)
goto exception;
it->iterating_regexp = matcher;
it->iterated_string = S;
strp = JS_VALUE_GET_STRING(flags);
it->global = string_indexof_char(strp, 'g', 0) >= 0;
it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
it->done = FALSE;
JS_SetOpaque(iter, it);
JS_FreeValue(ctx, C);
JS_FreeValue(ctx, flags);
return iter;
exception:
JS_FreeValue(ctx, S);
JS_FreeValue(ctx, C);
JS_FreeValue(ctx, flags);
JS_FreeValue(ctx, matcher);
JS_FreeValue(ctx, iter);
return JS_EXCEPTION;
}
JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
JSString *str;
JSValue str_val, obj, val, groups = JS_UNDEFINED;
uint8_t *re_bytecode;
int ret;
uint8_t **capture, *str_buf;
int capture_count, shift, i, re_flags;
int64_t last_index;
const char *group_name_ptr;
if (!re)
return JS_EXCEPTION;
str_val = JS_ToString(ctx, argv[0]);
if (JS_IsException(str_val))
return str_val;
val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
if (JS_IsException(val) ||
JS_ToLengthFree(ctx, &last_index, val)) {
JS_FreeValue(ctx, str_val);
return JS_EXCEPTION;
}
re_bytecode = re->bytecode->u.str8;
re_flags = lre_get_flags(re_bytecode);
if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
last_index = 0;
}
str = JS_VALUE_GET_STRING(str_val);
capture_count = lre_get_capture_count(re_bytecode);
capture = NULL;
if (capture_count > 0) {
capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
if (!capture) {
JS_FreeValue(ctx, str_val);
return JS_EXCEPTION;
}
}
shift = str->is_wide_char;
str_buf = str->u.str8;
if (last_index > str->len) {
ret = 2;
} else {
ret = lre_exec(capture, re_bytecode,
str_buf, last_index, str->len,
shift, ctx);
}
obj = JS_NULL;
if (ret != 1) {
if (ret >= 0) {
if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
JS_NewInt32(ctx, 0)) < 0)
goto fail;
}
} else {
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
goto fail;
}
JS_FreeValue(ctx, str_val);
} else {
int prop_flags;
if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
goto fail;
}
obj = JS_NewArray(ctx);
if (JS_IsException(obj))
goto fail;
prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
group_name_ptr = lre_get_groupnames(re_bytecode);
if (group_name_ptr) {
groups = JS_NewObjectProto(ctx, JS_NULL);
if (JS_IsException(groups))
goto fail;
}
for(i = 0; i < capture_count; i++) {
int start, end;
JSValue val;
if (capture[2 * i] == NULL ||
capture[2 * i + 1] == NULL) {
val = JS_UNDEFINED;
} else {
start = (capture[2 * i] - str_buf) >> shift;
end = (capture[2 * i + 1] - str_buf) >> shift;
val = js_sub_string(ctx, str, start, end);
if (JS_IsException(val))
goto fail;
}
if (group_name_ptr && i > 0) {
if (*group_name_ptr) {
if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,
JS_DupValue(ctx, val),
prop_flags) < 0) {
JS_FreeValue(ctx, val);
goto fail;
}
}
group_name_ptr += strlen(group_name_ptr) + 1;
}
if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
goto fail;
}
if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
groups, prop_flags) < 0)
goto fail;
if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,
JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)
goto fail;
if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)
goto fail1;
}
js_free(ctx, capture);
return obj;
fail:
JS_FreeValue(ctx, groups);
JS_FreeValue(ctx, str_val);
fail1:
JS_FreeValue(ctx, obj);
js_free(ctx, capture);
return JS_EXCEPTION;
}
JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
{
JSValue method, ret;
method = JS_GetProperty(ctx, r, JS_ATOM_exec);
if (JS_IsException(method))
return method;
if (JS_IsFunction(ctx, method)) {
ret = JS_CallFree(ctx, method, r, 1, &s);
if (JS_IsException(ret))
return ret;
if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
JS_FreeValue(ctx, ret);
return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
}
return ret;
}
JS_FreeValue(ctx, method);
return js_regexp_exec(ctx, r, 1, &s);
}
BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
{
JSContext *ctx = opaque;
return js_check_stack_overflow(ctx->rt, alloca_size);
}
void *lre_realloc(void *opaque, void *ptr, size_t size)
{
JSContext *ctx = opaque;
/* No JS exception is raised here */
return js_realloc_rt(ctx->rt, ptr, size);
}
/* delete portions of a string that match a given regex */
static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
{
JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
JSString *str;
JSValue str_val, val;
uint8_t *re_bytecode;
int ret;
uint8_t **capture, *str_buf;
int capture_count, shift, re_flags;
int next_src_pos, start, end;
int64_t last_index;
StringBuffer b_s, *b = &b_s;
if (!re)
return JS_EXCEPTION;
string_buffer_init(ctx, b, 0);
capture = NULL;
str_val = JS_ToString(ctx, arg);
if (JS_IsException(str_val))
goto fail;
str = JS_VALUE_GET_STRING(str_val);
re_bytecode = re->bytecode->u.str8;
re_flags = lre_get_flags(re_bytecode);
if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
last_index = 0;
} else {
val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
goto fail;
}
capture_count = lre_get_capture_count(re_bytecode);
if (capture_count > 0) {
capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
if (!capture)
goto fail;
}
shift = str->is_wide_char;
str_buf = str->u.str8;
next_src_pos = 0;
for (;;) {
if (last_index > str->len)
break;
ret = lre_exec(capture, re_bytecode,
str_buf, last_index, str->len, shift, ctx);
if (ret != 1) {
if (ret >= 0) {
if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
JS_NewInt32(ctx, 0)) < 0)
goto fail;
}
} else {
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
goto fail;
}
break;
}
start = (capture[0] - str_buf) >> shift;
end = (capture[1] - str_buf) >> shift;
last_index = end;
if (next_src_pos < start) {
if (string_buffer_concat(b, str, next_src_pos, start))
goto fail;
}
next_src_pos = end;
if (!(re_flags & LRE_FLAG_GLOBAL)) {
if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
JS_NewInt32(ctx, end)) < 0)
goto fail;
break;
}
if (end == start) {
if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {
end++;
} else {
string_getc(str, &end);
}
}
last_index = end;
}
if (string_buffer_concat(b, str, next_src_pos, str->len))
goto fail;
JS_FreeValue(ctx, str_val);
js_free(ctx, capture);
return string_buffer_end(b);
fail:
JS_FreeValue(ctx, str_val);
js_free(ctx, capture);
string_buffer_free(b);
return JS_EXCEPTION;
}
#if 0
static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_RegExpExec(ctx, argv[0], argv[1]);
}
static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_RegExpDelete(ctx, argv[0], argv[1]);
}
#endif
static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue val;
BOOL ret;
val = JS_RegExpExec(ctx, this_val, argv[0]);
if (JS_IsException(val))
return JS_EXCEPTION;
ret = !JS_IsNull(val);
JS_FreeValue(ctx, val);
return JS_NewBool(ctx, ret);
}
static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// [Symbol.match](str)
JSValueConst rx = this_val;
JSValue A, S, result, matchStr;
int global, n, fullUnicode, isEmpty;
JSString *p;
if (!JS_IsObject(rx))
return JS_ThrowTypeErrorNotAnObject(ctx);
A = JS_UNDEFINED;
result = JS_UNDEFINED;
matchStr = JS_UNDEFINED;
S = JS_ToString(ctx, argv[0]);
if (JS_IsException(S))
goto exception;
global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
if (global < 0)
goto exception;
if (!global) {
A = JS_RegExpExec(ctx, rx, S);
} else {
fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
if (fullUnicode < 0)
goto exception;
if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
goto exception;
A = JS_NewArray(ctx);
if (JS_IsException(A))
goto exception;
n = 0;
for(;;) {
JS_FreeValue(ctx, result);
result = JS_RegExpExec(ctx, rx, S);
if (JS_IsException(result))
goto exception;
if (JS_IsNull(result))
break;
matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
if (JS_IsException(matchStr))
goto exception;
isEmpty = JS_IsEmptyString(matchStr);
if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
goto exception;
if (isEmpty) {
int64_t thisIndex, nextIndex;
if (JS_ToLengthFree(ctx, &thisIndex,
JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
goto exception;
p = JS_VALUE_GET_STRING(S);
nextIndex = string_advance_index(p, thisIndex, fullUnicode);
if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
goto exception;
}
}
if (n == 0) {
JS_FreeValue(ctx, A);
A = JS_NULL;
}
}
JS_FreeValue(ctx, result);
JS_FreeValue(ctx, S);
return A;
exception:
JS_FreeValue(ctx, A);
JS_FreeValue(ctx, result);
JS_FreeValue(ctx, S);
return JS_EXCEPTION;
}
typedef struct ValueBuffer {
JSContext *ctx;
JSValue *arr;
JSValue def[4];
int len;
int size;
int error_status;
} ValueBuffer;
static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
{
b->ctx = ctx;
b->len = 0;
b->size = 4;
b->error_status = 0;
b->arr = b->def;
return 0;
}
static void value_buffer_free(ValueBuffer *b)
{
while (b->len > 0)
JS_FreeValue(b->ctx, b->arr[--b->len]);
if (b->arr != b->def)
js_free(b->ctx, b->arr);
b->arr = b->def;
b->size = 4;
}
static int value_buffer_append(ValueBuffer *b, JSValue val)
{
if (b->error_status)
return -1;
if (b->len >= b->size) {
int new_size = (b->len + (b->len >> 1) + 31) & ~16;
size_t slack;
JSValue *new_arr;
if (b->arr == b->def) {
new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
if (new_arr)
memcpy(new_arr, b->def, sizeof b->def);
} else {
new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
}
if (!new_arr) {
value_buffer_free(b);
JS_FreeValue(b->ctx, val);
b->error_status = -1;
return -1;
}
new_size += slack / sizeof(*new_arr);
b->arr = new_arr;
b->size = new_size;
}
b->arr[b->len++] = val;
return 0;
}
static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
{
JSValue val;
int res;
val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
if (JS_IsException(val))
return -1;
// rx.constructor === RegExp
res = js_same_value(ctx, val, ctx->regexp_ctor);
JS_FreeValue(ctx, val);
if (res) {
val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
if (JS_IsException(val))
return -1;
// rx.exec === RE_exec
res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
JS_FreeValue(ctx, val);
}
return res;
}
static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// [Symbol.replace](str, rep)
JSValueConst rx = this_val, rep = argv[1];
JSValueConst args[6];
JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;
JSString *sp, *rp;
StringBuffer b_s, *b = &b_s;
ValueBuffer v_b, *results = &v_b;
int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
uint32_t nCaptures;
int64_t position;
if (!JS_IsObject(rx))
return JS_ThrowTypeErrorNotAnObject(ctx);
string_buffer_init(ctx, b, 0);
value_buffer_init(ctx, results);
rep_val = JS_UNDEFINED;
matched = JS_UNDEFINED;
tab = JS_UNDEFINED;
rep_str = JS_UNDEFINED;
namedCaptures = JS_UNDEFINED;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
goto exception;
sp = JS_VALUE_GET_STRING(str);
rp = NULL;
functionalReplace = JS_IsFunction(ctx, rep);
if (!functionalReplace) {
rep_val = JS_ToString(ctx, rep);
if (JS_IsException(rep_val))
goto exception;
rp = JS_VALUE_GET_STRING(rep_val);
}
fullUnicode = 0;
is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
if (is_global < 0)
goto exception;
if (is_global) {
fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
if (fullUnicode < 0)
goto exception;
if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
goto exception;
}
if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
/* use faster version for simple cases */
res = JS_RegExpDelete(ctx, rx, str);
goto done;
}
for(;;) {
JSValue result;
result = JS_RegExpExec(ctx, rx, str);
if (JS_IsException(result))
goto exception;
if (JS_IsNull(result))
break;
if (value_buffer_append(results, result) < 0)
goto exception;
if (!is_global)
break;
JS_FreeValue(ctx, matched);
matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
if (JS_IsException(matched))
goto exception;
if (JS_IsEmptyString(matched)) {
/* always advance of at least one char */
int64_t thisIndex, nextIndex;
if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
goto exception;
nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
goto exception;
}
}
nextSourcePosition = 0;
for(j = 0; j < results->len; j++) {
JSValueConst result;
result = results->arr[j];
if (js_get_length32(ctx, &nCaptures, result) < 0)
goto exception;
JS_FreeValue(ctx, matched);
matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
if (JS_IsException(matched))
goto exception;
if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
goto exception;
if (position > sp->len)
position = sp->len;
else if (position < 0)
position = 0;
/* ignore substition if going backward (can happen
with custom regexp object) */
JS_FreeValue(ctx, tab);
tab = JS_NewArray(ctx);
if (JS_IsException(tab))
goto exception;
if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
for(n = 1; n < nCaptures; n++) {
JSValue capN;
capN = JS_GetPropertyInt64(ctx, result, n);
if (JS_IsException(capN))
goto exception;
if (!JS_IsUndefined(capN)) {
capN = JS_ToStringFree(ctx, capN);
if (JS_IsException(capN))
goto exception;
}
if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
}
JS_FreeValue(ctx, namedCaptures);
namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
if (JS_IsException(namedCaptures))
goto exception;
if (functionalReplace) {
if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
if (!JS_IsUndefined(namedCaptures)) {
if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
}
args[0] = JS_UNDEFINED;
args[1] = tab;
JS_FreeValue(ctx, rep_str);
rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
} else {
JSValue namedCaptures1;
if (!JS_IsUndefined(namedCaptures)) {
namedCaptures1 = JS_ToObject(ctx, namedCaptures);
if (JS_IsException(namedCaptures1))
goto exception;
} else {
namedCaptures1 = JS_UNDEFINED;
}
args[0] = matched;
args[1] = str;
args[2] = JS_NewInt32(ctx, position);
args[3] = tab;
args[4] = namedCaptures1;
args[5] = rep_val;
JS_FreeValue(ctx, rep_str);
rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
JS_FreeValue(ctx, namedCaptures1);
}
if (JS_IsException(rep_str))
goto exception;
if (position >= nextSourcePosition) {
string_buffer_concat(b, sp, nextSourcePosition, position);
string_buffer_concat_value(b, rep_str);
nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
}
}
string_buffer_concat(b, sp, nextSourcePosition, sp->len);
res = string_buffer_end(b);
goto done1;
exception:
res = JS_EXCEPTION;
done:
string_buffer_free(b);
done1:
value_buffer_free(results);
JS_FreeValue(ctx, rep_val);
JS_FreeValue(ctx, matched);
JS_FreeValue(ctx, tab);
JS_FreeValue(ctx, rep_str);
JS_FreeValue(ctx, namedCaptures);
JS_FreeValue(ctx, str);
return res;
}
static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst rx = this_val;
JSValue str, previousLastIndex, currentLastIndex, result, index;
if (!JS_IsObject(rx))
return JS_ThrowTypeErrorNotAnObject(ctx);
result = JS_UNDEFINED;
currentLastIndex = JS_UNDEFINED;
previousLastIndex = JS_UNDEFINED;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
goto exception;
previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
if (JS_IsException(previousLastIndex))
goto exception;
if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
goto exception;
}
}
result = JS_RegExpExec(ctx, rx, str);
if (JS_IsException(result))
goto exception;
currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
if (JS_IsException(currentLastIndex))
goto exception;
if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
JS_FreeValue(ctx, previousLastIndex);
} else {
if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
previousLastIndex = JS_UNDEFINED;
goto exception;
}
}
JS_FreeValue(ctx, str);
JS_FreeValue(ctx, currentLastIndex);
if (JS_IsNull(result)) {
return JS_NewInt32(ctx, -1);
} else {
index = JS_GetProperty(ctx, result, JS_ATOM_index);
JS_FreeValue(ctx, result);
return index;
}
exception:
JS_FreeValue(ctx, result);
JS_FreeValue(ctx, str);
JS_FreeValue(ctx, currentLastIndex);
JS_FreeValue(ctx, previousLastIndex);
return JS_EXCEPTION;
}
static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// [Symbol.split](str, limit)
JSValueConst rx = this_val;
JSValueConst args[2];
JSValue str, ctor, splitter, A, flags, z, sub;
JSString *strp;
uint32_t lim, size, p, q;
int unicodeMatching;
int64_t lengthA, e, numberOfCaptures, i;
if (!JS_IsObject(rx))
return JS_ThrowTypeErrorNotAnObject(ctx);
ctor = JS_UNDEFINED;
splitter = JS_UNDEFINED;
A = JS_UNDEFINED;
flags = JS_UNDEFINED;
z = JS_UNDEFINED;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
goto exception;
ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
if (JS_IsException(ctor))
goto exception;
flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
if (JS_IsException(flags))
goto exception;
strp = JS_VALUE_GET_STRING(flags);
unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
if (string_indexof_char(strp, 'y', 0) < 0) {
flags = JS_ConcatString3(ctx, "", flags, "y");
if (JS_IsException(flags))
goto exception;
}
args[0] = rx;
args[1] = flags;
splitter = JS_CallConstructor(ctx, ctor, 2, args);
if (JS_IsException(splitter))
goto exception;
A = JS_NewArray(ctx);
if (JS_IsException(A))
goto exception;
lengthA = 0;
if (JS_IsUndefined(argv[1])) {
lim = 0xffffffff;
} else {
if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
goto exception;
if (lim == 0)
goto done;
}
strp = JS_VALUE_GET_STRING(str);
p = q = 0;
size = strp->len;
if (size == 0) {
z = JS_RegExpExec(ctx, splitter, str);
if (JS_IsException(z))
goto exception;
if (JS_IsNull(z))
goto add_tail;
goto done;
}
while (q < size) {
if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
goto exception;
JS_FreeValue(ctx, z);
z = JS_RegExpExec(ctx, splitter, str);
if (JS_IsException(z))
goto exception;
if (JS_IsNull(z)) {
q = string_advance_index(strp, q, unicodeMatching);
} else {
if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
goto exception;
if (e > size)
e = size;
if (e == p) {
q = string_advance_index(strp, q, unicodeMatching);
} else {
sub = js_sub_string(ctx, strp, p, q);
if (JS_IsException(sub))
goto exception;
if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
if (lengthA == lim)
goto done;
p = e;
if (js_get_length64(ctx, &numberOfCaptures, z))
goto exception;
for(i = 1; i < numberOfCaptures; i++) {
sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));
if (JS_IsException(sub))
goto exception;
if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
if (lengthA == lim)
goto done;
}
q = p;
}
}
}
add_tail:
if (p > size)
p = size;
sub = js_sub_string(ctx, strp, p, size);
if (JS_IsException(sub))
goto exception;
if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
goto done;
exception:
JS_FreeValue(ctx, A);
A = JS_EXCEPTION;
done:
JS_FreeValue(ctx, str);
JS_FreeValue(ctx, ctor);
JS_FreeValue(ctx, splitter);
JS_FreeValue(ctx, flags);
JS_FreeValue(ctx, z);
return A;
}
static const JSCFunctionListEntry js_regexp_funcs[] = {
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
//JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
//JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
};
static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),
JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),
JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),
JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),
JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),
JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),
JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
JS_CFUNC_DEF("test", 1, js_regexp_test ),
JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
//JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
//JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
};
static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
};
void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
{
ctx->compile_regexp = js_compile_regexp;
}
void JS_AddIntrinsicRegExp(JSContext *ctx)
{
JSValueConst obj;
JS_AddIntrinsicRegExpCompiler(ctx);
ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
countof(js_regexp_proto_funcs));
obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
ctx->class_proto[JS_CLASS_REGEXP]);
ctx->regexp_ctor = JS_DupValue(ctx, obj);
JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
JS_NewObjectProto(ctx, ctx->iterator_proto);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
js_regexp_string_iterator_proto_funcs,
countof(js_regexp_string_iterator_proto_funcs));
}