Skip to content

feat: add support for RegExp.leftContext RegExp.rightContext #923

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,12 @@ struct JSContext {
JSValue global_obj; /* global object */
JSValue global_var_obj; /* contains the global let/const definitions */

JSValue regexp_left_ctx; // RegExp.leftContext
JSValue regexp_right_ctx; // RegExp.rightContext
JSValue regexp_last_match_str;
int64_t regexp_last_match_start;
int64_t regexp_last_match_end;

double time_origin;

uint64_t random_state;
Expand Down Expand Up @@ -2240,6 +2246,11 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
ctx->error_back_trace = JS_UNDEFINED;
ctx->error_prepare_stack = JS_UNDEFINED;
ctx->error_stack_trace_limit = js_int32(10);
ctx->regexp_left_ctx = JS_UNDEFINED;
ctx->regexp_right_ctx = JS_UNDEFINED;
ctx->regexp_last_match_str = JS_UNDEFINED;
ctx->regexp_last_match_start = -1;
ctx->regexp_last_match_end = -1;
init_list_head(&ctx->loaded_modules);

JS_AddIntrinsicBasicObjects(ctx);
Expand Down Expand Up @@ -2371,6 +2382,10 @@ static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
JS_MarkValue(rt, ctx->function_ctor, mark_func);
JS_MarkValue(rt, ctx->function_proto, mark_func);

JS_MarkValue(rt, ctx->regexp_last_match_str, mark_func);
JS_MarkValue(rt, ctx->regexp_left_ctx, mark_func);
JS_MarkValue(rt, ctx->regexp_right_ctx, mark_func);

if (ctx->array_shape)
mark_func(rt, &ctx->array_shape->header);
}
Expand Down Expand Up @@ -2440,6 +2455,9 @@ void JS_FreeContext(JSContext *ctx)
JS_FreeValue(ctx, ctx->regexp_ctor);
JS_FreeValue(ctx, ctx->function_ctor);
JS_FreeValue(ctx, ctx->function_proto);
JS_FreeValue(ctx, ctx->regexp_left_ctx);
JS_FreeValue(ctx, ctx->regexp_right_ctx);
JS_FreeValue(ctx, ctx->regexp_last_match_str);

js_free_shape_null(ctx->rt, ctx->array_shape);

Expand Down Expand Up @@ -43821,6 +43839,31 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValue this_val,
return string_buffer_end(b);
}

static JSValue js_regexp_get_leftContext(JSContext *ctx, JSValue this_val) {
if (ctx->regexp_last_match_start >= 0 && JS_IsString(ctx->regexp_last_match_str)) {
JSString* p;
JS_FreeValue(ctx, ctx->regexp_left_ctx);
ctx->regexp_left_ctx = JS_UNDEFINED;
p = JS_VALUE_GET_STRING(ctx->regexp_last_match_str);
ctx->regexp_left_ctx = js_sub_string(ctx, p, 0, ctx->regexp_last_match_start);
ctx->regexp_last_match_start = -1; // Reset to avoid recomputation
}
return JS_DupValue(ctx, ctx->regexp_left_ctx);
}

static JSValue js_regexp_get_rightContext(JSContext *ctx, JSValue this_val) {
if (ctx->regexp_last_match_end >= 0 && JS_IsString(ctx->regexp_last_match_str)) {
JSString* p;
JS_FreeValue(ctx, ctx->regexp_right_ctx);
ctx->regexp_right_ctx = JS_UNDEFINED;

p = JS_VALUE_GET_STRING(ctx->regexp_last_match_str);
ctx->regexp_right_ctx = js_sub_string(ctx, p, ctx->regexp_last_match_end, p->len);
ctx->regexp_last_match_end = -1; // Reset to avoid recomputation
}
return JS_DupValue(ctx, ctx->regexp_right_ctx);
}

static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
Expand Down Expand Up @@ -44007,6 +44050,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val,
goto fail;
}
}

JS_FreeValue(ctx, ctx->regexp_last_match_str);
ctx->regexp_last_match_str = JS_DupValue(ctx, argv[0]);
ctx->regexp_last_match_start = (capture[0] - str_buf) >> shift;
ctx->regexp_last_match_end = (capture[1] - str_buf) >> shift;
}
ret = obj;
obj = JS_UNDEFINED;
Expand Down Expand Up @@ -44850,6 +44898,8 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValue this_val,

static const JSCFunctionListEntry js_regexp_funcs[] = {
JS_CFUNC_DEF("escape", 1, js_regexp_escape ),
JS_CGETSET_DEF("leftContext", js_regexp_get_leftContext, NULL),
JS_CGETSET_DEF("rightContext", js_regexp_get_rightContext, NULL),
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
};

Expand Down
2 changes: 1 addition & 1 deletion test262.conf
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ iterator-sequencing=skip
json-modules=skip
json-parse-with-source=skip
json-superset
legacy-regexp=skip
legacy-regexp
let
logical-assignment-operators
Map
Expand Down
8 changes: 8 additions & 0 deletions tests/legacy_regexp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { assert } from "./assert.js";

const testString = "Hello, world!";
const regex = /world/;
regex.exec(testString);

assert(RegExp.leftContext === "Hello, ", true);
assert(RegExp.rightContext === "!", true);
Loading