Skip to content

Commit

Permalink
NativeRegExp: Implement Symbol.match
Browse files Browse the repository at this point in the history
The spec requires that Symbol.match call the exec method in a loop.
  • Loading branch information
balajirrao committed Feb 19, 2025
1 parent 0bdd23a commit a3371fc
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.io.Serializable;
import org.mozilla.javascript.AbstractEcmaObjectOperations;
import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Constructable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
Expand Down Expand Up @@ -2662,6 +2663,13 @@ protected Object getInstanceIdValue(int id) {
return super.getInstanceIdValue(id);
}

private void setLastIndex(ScriptableObject thisObj, Object value) {
if ((thisObj.getAttributes("lastIndex") & READONLY) != 0) {
throw ScriptRuntime.typeErrorById("msg.modify.readonly", "lastIndex");
}
ScriptableObject.putProperty(thisObj, "lastIndex", value);
}

private void setLastIndex(Object value) {
if ((lastIndexAttr & READONLY) != 0) {
throw ScriptRuntime.typeErrorById("msg.modify.readonly", "lastIndex");
Expand Down Expand Up @@ -2784,7 +2792,7 @@ public Object execIdCall(
return realThis(thisObj, f).execSub(cx, scope, args, PREFIX);

case SymbolId_match:
return realThis(thisObj, f).execSub(cx, scope, args, MATCH);
return js_SymbolMatch(cx, scope, thisObj, args);

case SymbolId_matchAll:
return js_SymbolMatchAll(cx, scope, thisObj, args);
Expand All @@ -2797,6 +2805,51 @@ public Object execIdCall(
throw new IllegalArgumentException(String.valueOf(id));
}

public static Object regExpExec(
Scriptable regexp, String string, Context cx, Scriptable scope) {
// See ECMAScript spec 22.2.7.1
Object execMethod = ScriptRuntime.getObjectProp(regexp, "exec", cx, scope);
if (execMethod instanceof Callable) {
return ((Callable) execMethod).call(cx, scope, regexp, new Object[] {string});
}
return NativeRegExp.js_exec(cx, scope, regexp, new Object[] {string});
}

private Object js_SymbolMatch(
Context cx, Scriptable scope, Scriptable thisScriptable, Object[] args) {
// See ECMAScript spec 22.2.6.8
var thisObj = ScriptableObject.ensureScriptableObject(thisScriptable);

String string = ScriptRuntime.toString(args.length > 0 ? args[0] : Undefined.instance);
String flags = ScriptRuntime.toString(ScriptRuntime.getObjectProp(thisObj, "flags", cx));
boolean fullUnicode = flags.indexOf('u') != -1 || flags.indexOf('v') != -1;

if (flags.indexOf('g') == -1) return regExpExec(thisObj, string, cx, scope);

setLastIndex(thisObj, ScriptRuntime.zeroObj);
Scriptable result = cx.newArray(scope, 0);
int i = 0;
while (true) {
Object match = regExpExec(thisObj, string, cx, scope);
if (match == null) {
if (i == 0) return null;
else return result;
}

String matchStr =
ScriptRuntime.toString(ScriptRuntime.getObjectIndex(match, 0, cx, scope));
result.put(i++, result, matchStr);

if (matchStr.isEmpty()) {
long thisIndex =
ScriptRuntime.toLength(
ScriptRuntime.getObjectProp(thisObj, "lastIndex", cx));
long nextIndex = ScriptRuntime.advanceStringIndex(string, thisIndex, fullUnicode);
setLastIndex(thisObj, nextIndex);
}
}
}

static Object js_exec(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
return realThis(thisObj, "exec").execSub(cx, scope, args, MATCH);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

package org.mozilla.javascript.regexp;

import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ES6Iterator;
import org.mozilla.javascript.ScriptRuntime;
Expand Down Expand Up @@ -66,7 +65,7 @@ protected boolean isDone(Context cx, Scriptable scope) {
return true;
}

next = regExpExec(cx, scope);
next = NativeRegExp.regExpExec(regexp, string, cx, scope);
if (next == null) {
// Done! Point ii of the spec
next = Undefined.instance;
Expand Down Expand Up @@ -95,15 +94,6 @@ protected Object nextValue(Context cx, Scriptable scope) {
return next;
}

private Object regExpExec(Context cx, Scriptable scope) {
// See ECMAScript spec 22.2.7.1
Object execMethod = ScriptRuntime.getObjectProp(regexp, "exec", cx);
if (execMethod instanceof Callable) {
return ((Callable) execMethod).call(cx, scope, regexp, new Object[] {string});
}
return NativeRegExp.js_exec(cx, scope, regexp, new Object[] {string});
}

@Override
protected String getTag() {
return ITERATOR_TAG;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ public void matchGlobal() throws Exception {
/**
* @throws Exception if an error occurs
*/
// TODO @Test
@Test
public void matchGlobalSymbol() throws Exception {
final String script =
"var result = /a/g[Symbol.match]('aaba');\n"
Expand Down Expand Up @@ -500,7 +500,7 @@ public void matchStickyAndGlobal() throws Exception {
/**
* @throws Exception if an error occurs
*/
// TODO @Test
@Test
public void matchStickyAndGlobalSymbol() throws Exception {
final String script =
"var result = /a/yg[Symbol.match]('aaba');\n"
Expand Down
18 changes: 1 addition & 17 deletions tests/testsrc/test262.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1578,7 +1578,7 @@ built-ins/Reflect 13/153 (8.5%)
set/return-false-if-receiver-is-not-writable.js
set/return-false-if-target-is-not-writable.js

built-ins/RegExp 1150/1854 (62.03%)
built-ins/RegExp 1134/1854 (61.17%)
CharacterClassEscapes 24/24 (100.0%)
dotall 4/4 (100.0%)
escape 20/20 (100.0%)
Expand Down Expand Up @@ -1667,30 +1667,14 @@ built-ins/RegExp 1150/1854 (62.03%)
prototype/Symbol.matchAll/this-tostring-flags-throws.js
prototype/Symbol.match/builtin-infer-unicode.js
prototype/Symbol.match/builtin-success-g-set-lastindex.js
prototype/Symbol.match/builtin-success-g-set-lastindex-err.js
prototype/Symbol.match/builtin-success-u-return-val-groups.js
prototype/Symbol.match/coerce-global.js
prototype/Symbol.match/exec-err.js
prototype/Symbol.match/exec-invocation.js
prototype/Symbol.match/exec-return-type-invalid.js
prototype/Symbol.match/exec-return-type-valid.js
prototype/Symbol.match/flags-tostring-error.js
prototype/Symbol.match/g-coerce-result-err.js
prototype/Symbol.match/g-get-exec-err.js
prototype/Symbol.match/g-get-result-err.js
prototype/Symbol.match/g-init-lastindex.js
prototype/Symbol.match/g-match-empty-advance-lastindex.js
prototype/Symbol.match/g-match-empty-coerce-lastindex-err.js
prototype/Symbol.match/g-match-empty-set-lastindex-err.js
prototype/Symbol.match/g-success-return-val.js
prototype/Symbol.match/get-exec-err.js
prototype/Symbol.match/get-flags-err.js
prototype/Symbol.match/get-global-err.js
prototype/Symbol.match/get-unicode-error.js
prototype/Symbol.match/not-a-constructor.js
prototype/Symbol.match/this-val-non-regexp.js
prototype/Symbol.match/u-advance-after-empty.js
prototype/Symbol.match/y-fail-global-return.js
prototype/Symbol.replace/arg-1-coerce.js
prototype/Symbol.replace/arg-1-coerce-err.js
prototype/Symbol.replace/arg-2-coerce.js
Expand Down

0 comments on commit a3371fc

Please sign in to comment.