Skip to content
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

Various fixes and implementations to NativeRegExp #1434

Merged
merged 5 commits into from
Jan 24, 2024
Merged
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
65 changes: 42 additions & 23 deletions src/org/mozilla/javascript/NativeString.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ protected void fillConstructorProperties(IdFunctionObject ctor) {
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_match, "match", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_search, "search", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_replace, "replace", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_replaceAll, "replaceAll", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_localeCompare, "localeCompare", 2);
addIdFunctionProperty(
ctor, STRING_TAG, ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
Expand Down Expand Up @@ -246,6 +247,10 @@ protected void initPrototypeId(int id) {
arity = 2;
s = "replace";
break;
case Id_replaceAll:
arity = 2;
s = "replaceAll";
break;
case Id_at:
arity = 1;
s = "at";
Expand Down Expand Up @@ -345,6 +350,7 @@ public Object execIdCall(
case ConstructorId_match:
case ConstructorId_search:
case ConstructorId_replace:
case ConstructorId_replaceAll:
case ConstructorId_localeCompare:
case ConstructorId_toLocaleLowerCase:
{
Expand Down Expand Up @@ -463,10 +469,15 @@ public Object execIdCall(
String thisString =
ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
if (args.length > 0 && args[0] instanceof NativeRegExp) {
throw ScriptRuntime.typeErrorById(
"msg.first.arg.not.regexp",
String.class.getSimpleName(),
f.getFunctionName());
if (ScriptableObject.isTrue(
ScriptableObject.getProperty(
ScriptableObject.ensureScriptable(args[0]),
SymbolKey.MATCH))) {
throw ScriptRuntime.typeErrorById(
"msg.first.arg.not.regexp",
String.class.getSimpleName(),
f.getFunctionName());
}
}

int idx = js_indexOf(id, thisString, args);
Expand Down Expand Up @@ -599,14 +610,17 @@ public Object execIdCall(
case Id_match:
case Id_search:
case Id_replace:
case Id_replaceAll:
{
int actionType;
if (id == Id_match) {
actionType = RegExpProxy.RA_MATCH;
} else if (id == Id_search) {
actionType = RegExpProxy.RA_SEARCH;
} else {
} else if (id == Id_replace) {
actionType = RegExpProxy.RA_REPLACE;
} else {
actionType = RegExpProxy.RA_REPLACE_ALL;
}

requireObjectCoercible(cx, thisObj, f);
Expand Down Expand Up @@ -1286,6 +1300,9 @@ protected int findPrototypeId(String s) {
case "replace":
id = Id_replace;
break;
case "replaceAll":
id = Id_replaceAll;
break;
case "localeCompare":
id = Id_localeCompare;
break;
Expand Down Expand Up @@ -1380,24 +1397,25 @@ protected int findPrototypeId(String s) {
Id_match = 31,
Id_search = 32,
Id_replace = 33,
Id_localeCompare = 34,
Id_toLocaleLowerCase = 35,
Id_toLocaleUpperCase = 36,
Id_trim = 37,
Id_trimLeft = 38,
Id_trimRight = 39,
Id_includes = 40,
Id_startsWith = 41,
Id_endsWith = 42,
Id_normalize = 43,
Id_repeat = 44,
Id_codePointAt = 45,
Id_padStart = 46,
Id_padEnd = 47,
SymbolId_iterator = 48,
Id_trimStart = 49,
Id_trimEnd = 50,
Id_at = 51,
Id_replaceAll = 34,
Id_localeCompare = 35,
Id_toLocaleLowerCase = 36,
Id_toLocaleUpperCase = 37,
Id_trim = 38,
Id_trimLeft = 39,
Id_trimRight = 40,
Id_includes = 41,
Id_startsWith = 42,
Id_endsWith = 43,
Id_normalize = 44,
Id_repeat = 45,
Id_codePointAt = 46,
Id_padStart = 47,
Id_padEnd = 48,
SymbolId_iterator = 49,
Id_trimStart = 50,
Id_trimEnd = 51,
Id_at = 52,
MAX_PROTOTYPE_ID = Id_at;
private static final int ConstructorId_charAt = -Id_charAt,
ConstructorId_charCodeAt = -Id_charCodeAt,
Expand All @@ -1414,6 +1432,7 @@ protected int findPrototypeId(String s) {
ConstructorId_match = -Id_match,
ConstructorId_search = -Id_search,
ConstructorId_replace = -Id_replace,
ConstructorId_replaceAll = -Id_replaceAll,
ConstructorId_localeCompare = -Id_localeCompare,
ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;

Expand Down
3 changes: 2 additions & 1 deletion src/org/mozilla/javascript/RegExpProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public interface RegExpProxy {
// Types of regexp actions
public static final int RA_MATCH = 1;
public static final int RA_REPLACE = 2;
public static final int RA_SEARCH = 3;
public static final int RA_REPLACE_ALL = 3;
public static final int RA_SEARCH = 4;

public boolean isRegExp(Scriptable obj);

Expand Down
4 changes: 2 additions & 2 deletions src/org/mozilla/javascript/TokenStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -1496,8 +1496,8 @@ void readRegExp(int startToken) throws IOException {
if (matchChar('g')) addToString('g');
else if (matchChar('i')) addToString('i');
else if (matchChar('m')) addToString('m');
else if (matchChar('y')) // FireFox 3
addToString('y');
else if (matchChar('s')) addToString('s');
else if (matchChar('y')) addToString('y');
else break;
}
tokenEnd = start + stringBufferTop + 2; // include slashes
Expand Down
37 changes: 33 additions & 4 deletions src/org/mozilla/javascript/regexp/NativeRegExp.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.mozilla.javascript.IdFunctionObject;
import org.mozilla.javascript.IdScriptableObject;
import org.mozilla.javascript.Kit;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
Expand All @@ -37,7 +38,8 @@ public class NativeRegExp extends IdScriptableObject {
public static final int JSREG_GLOB = 0x1; // 'g' flag: global
public static final int JSREG_FOLD = 0x2; // 'i' flag: fold
public static final int JSREG_MULTILINE = 0x4; // 'm' flag: multiline
public static final int JSREG_STICKY = 0x8; // 'y' flag: sticky
public static final int JSREG_DOTALL = 0x8; // 's' flag: dotAll
public static final int JSREG_STICKY = 0x10; // 'y' flag: sticky

// type of match to perform
public static final int TEST = 0;
Expand Down Expand Up @@ -196,6 +198,7 @@ private void appendFlags(StringBuilder buf) {
if ((re.flags & JSREG_GLOB) != 0) buf.append('g');
if ((re.flags & JSREG_FOLD) != 0) buf.append('i');
if ((re.flags & JSREG_MULTILINE) != 0) buf.append('m');
if ((re.flags & JSREG_DOTALL) != 0) buf.append('s');
if ((re.flags & JSREG_STICKY) != 0) buf.append('y');
}

Expand Down Expand Up @@ -278,6 +281,8 @@ static RECompiled compileRE(Context cx, String str, String global, boolean flat)
f = JSREG_FOLD;
} else if (c == 'm') {
f = JSREG_MULTILINE;
} else if (c == 's') {
f = JSREG_DOTALL;
} else if (c == 'y') {
f = JSREG_STICKY;
} else {
Expand Down Expand Up @@ -1709,7 +1714,9 @@ private static int simpleMatch(
^ ((gData.cp < end) && isWord(input.charAt(gData.cp))));
break;
case REOP_DOT:
if (gData.cp != end && !isLineTerm(input.charAt(gData.cp))) {
if (gData.cp != end
&& ((gData.regexp.flags & JSREG_DOTALL) != 0
|| !isLineTerm(input.charAt(gData.cp)))) {
result = true;
gData.cp++;
}
Expand Down Expand Up @@ -2521,8 +2528,9 @@ private static void reportError(String messageId, String arg) {
Id_global = 4,
Id_ignoreCase = 5,
Id_multiline = 6,
Id_sticky = 7,
MAX_INSTANCE_ID = 7;
Id_dotAll = 7,
Id_sticky = 8,
MAX_INSTANCE_ID = 8;

@Override
protected int getMaxInstanceId() {
Expand Down Expand Up @@ -2551,6 +2559,9 @@ protected int findInstanceIdInfo(String s) {
case "multiline":
id = Id_multiline;
break;
case "dotAll":
id = Id_dotAll;
break;
case "sticky":
id = Id_sticky;
break;
Expand All @@ -2571,6 +2582,7 @@ protected int findInstanceIdInfo(String s) {
case Id_global:
case Id_ignoreCase:
case Id_multiline:
case Id_dotAll:
case Id_sticky:
attr = PERMANENT | READONLY | DONTENUM;
break;
Expand All @@ -2595,6 +2607,8 @@ protected String getInstanceIdName(int id) {
return "ignoreCase";
case Id_multiline:
return "multiline";
case Id_dotAll:
return "dotAll";
case Id_sticky:
return "sticky";
}
Expand All @@ -2620,6 +2634,8 @@ protected Object getInstanceIdValue(int id) {
return ScriptRuntime.wrapBoolean((re.flags & JSREG_FOLD) != 0);
case Id_multiline:
return ScriptRuntime.wrapBoolean((re.flags & JSREG_MULTILINE) != 0);
case Id_dotAll:
return ScriptRuntime.wrapBoolean((re.flags & JSREG_DOTALL) != 0);
case Id_sticky:
return ScriptRuntime.wrapBoolean((re.flags & JSREG_STICKY) != 0);
}
Expand All @@ -2644,6 +2660,7 @@ protected void setInstanceIdValue(int id, Object value) {
case Id_global:
case Id_ignoreCase:
case Id_multiline:
case Id_dotAll:
case Id_sticky:
return;
}
Expand Down Expand Up @@ -2716,6 +2733,18 @@ public Object execIdCall(
return realThis(thisObj, f).compile(cx, scope, args);

case Id_toString:
// thisObj != scope is a strange hack but i had no better idea for the moment
if (thisObj != scope && thisObj instanceof NativeObject) {
Object sourceObj = thisObj.get("source", thisObj);
String source =
sourceObj.equals(NOT_FOUND) ? "undefined" : escapeRegExp(sourceObj);
Object flagsObj = thisObj.get("flags", thisObj);
String flags = flagsObj.equals(NOT_FOUND) ? "undefined" : flagsObj.toString();

return "/" + source + "/" + flags;
}
return realThis(thisObj, f).toString();

case Id_toSource:
return realThis(thisObj, f).toString();

Expand Down
66 changes: 47 additions & 19 deletions src/org/mozilla/javascript/regexp/RegExpImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public Object action(
}

case RA_REPLACE:
case RA_REPLACE_ALL:
{
boolean useRE = args.length > 0 && args[0] instanceof NativeRegExp;

Expand All @@ -78,6 +79,11 @@ public Object action(
String search = null;
if (useRE) {
re = createRegExp(cx, scope, args, 2, true);
if (RA_REPLACE_ALL == actionType
&& (re.getFlags() & NativeRegExp.JSREG_GLOB) == 0) {
throw ScriptRuntime.typeError(
"replaceAll must be called with a global RegExp");
}
} else {
Object arg0 = args.length < 1 ? Undefined.instance : args[0];
search = ScriptRuntime.toString(arg0);
Expand All @@ -102,32 +108,54 @@ public Object action(

Object val;
if (useRE) {
val = matchOrReplace(cx, scope, thisObj, args, this, data, re);
Object result = matchOrReplace(cx, scope, thisObj, args, this, data, re);
if (data.charBuf == null) {
if (data.global || result == null || !Boolean.TRUE.equals(result)) {
/* Didn't match even once. */
return data.str;
}
SubString lc = this.leftContext;
replace_glob(data, cx, scope, this, lc.index, lc.length);
}
} else {
String str = data.str;
int index = str.indexOf(search);
if (index >= 0) {
int slen = search.length();
final String str = data.str;
final int strLen = str.length(), searchLen = search.length();
int index = -1, lastIndex = 0;
for (; ; ) {
if (search.isEmpty()) {
if (index == -1) {
index = 0;
} else {
index = (lastIndex < strLen) ? lastIndex + 1 : -1;
}
} else {
index = str.indexOf(search, lastIndex);
}

if (index == -1) {
if (data.charBuf == null) {
return str;
}
break;
}

this.parens = null;
this.lastParen = null;
this.leftContext = new SubString(str, 0, index);
this.lastMatch = new SubString(str, index, slen);
this.lastMatch = new SubString(str, index, searchLen);
this.rightContext =
new SubString(str, index + slen, str.length() - index - slen);
val = Boolean.TRUE;
} else {
val = Boolean.FALSE;
}
}
new SubString(
str, index + searchLen, strLen - index - searchLen);

replace_glob(data, cx, scope, this, lastIndex, index - lastIndex);
lastIndex = index + searchLen;

if (data.charBuf == null) {
if (data.global || val == null || !val.equals(Boolean.TRUE)) {
/* Didn't match even once. */
return data.str;
if (actionType != RA_REPLACE_ALL) {
break;
}
}
SubString lc = this.leftContext;
replace_glob(data, cx, scope, this, lc.index, lc.length);
}

SubString rc = this.rightContext;
data.charBuf.append(rc.str, rc.index, rc.index + rc.length);
return data.charBuf.toString();
Expand Down Expand Up @@ -192,7 +220,7 @@ private static Object matchOrReplace(
if (data.mode == RA_MATCH) {
match_glob(data, cx, scope, count, reImpl);
} else {
if (data.mode != RA_REPLACE) Kit.codeBug();
if (data.mode != RA_REPLACE && data.mode != RA_REPLACE_ALL) Kit.codeBug();
SubString lastMatch = reImpl.lastMatch;
int leftIndex = data.leftIndex;
int leftlen = lastMatch.index - leftIndex;
Expand Down
Loading