Skip to content

Commit 06e155e

Browse files
committed
Make sure slashes in RegExp source/toString() are escaped.
Fixes bug #510265 except for the source property of empty regexp where we follow Spidermonkey/V8.
1 parent 20c659b commit 06e155e

File tree

4 files changed

+121
-7
lines changed

4 files changed

+121
-7
lines changed

src/org/mozilla/javascript/regexp/NativeRegExp.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,9 @@ public static void init(Context cx, Scriptable scope, boolean sealed)
171171
defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM);
172172
}
173173

174-
NativeRegExp(Scriptable scope, Object regexpCompiled)
174+
NativeRegExp(Scriptable scope, RECompiled regexpCompiled)
175175
{
176-
this.re = (RECompiled)regexpCompiled;
176+
this.re = regexpCompiled;
177177
this.lastIndex = 0;
178178
ScriptRuntime.setBuiltinProtoAndParent(this, scope, TopLevel.Builtins.RegExp);
179179
}
@@ -218,7 +218,7 @@ Scriptable compile(Context cx, Scriptable scope, Object[] args)
218218
this.lastIndex = thatObj.lastIndex;
219219
return this;
220220
}
221-
String s = args.length == 0 ? "" : ScriptRuntime.toString(args[0]);
221+
String s = args.length == 0 ? "" : escapeRegExp(args[0]);
222222
String global = args.length > 1 && args[1] != Undefined.instance
223223
? ScriptRuntime.toString(args[1])
224224
: null;
@@ -230,7 +230,7 @@ Scriptable compile(Context cx, Scriptable scope, Object[] args)
230230
@Override
231231
public String toString()
232232
{
233-
StringBuffer buf = new StringBuffer();
233+
StringBuilder buf = new StringBuilder();
234234
buf.append('/');
235235
if (re.source.length != 0) {
236236
buf.append(re.source);
@@ -255,6 +255,30 @@ private static RegExpImpl getImpl(Context cx)
255255
return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
256256
}
257257

258+
private static String escapeRegExp(Object src) {
259+
String s = ScriptRuntime.toString(src);
260+
// Escape any naked slashes in regexp source, see bug #510265
261+
StringBuilder sb = null; // instantiated only if necessary
262+
int start = 0;
263+
int slash = s.indexOf('/');
264+
while (slash > -1) {
265+
if (slash == start || s.charAt(slash - 1) != '\\') {
266+
if (sb == null) {
267+
sb = new StringBuilder();
268+
}
269+
sb.append(s, start, slash);
270+
sb.append("\\/");
271+
start = slash + 1;
272+
}
273+
slash = s.indexOf('/', slash + 1);
274+
}
275+
if (sb != null) {
276+
sb.append(s, start, s.length());
277+
s = sb.toString();
278+
}
279+
return s;
280+
}
281+
258282
private Object execSub(Context cx, Scriptable scopeObj,
259283
Object[] args, int matchType)
260284
{

src/org/mozilla/javascript/regexp/RegExpImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public Object compileRegExp(Context cx, String source, String flags)
5656
public Scriptable wrapRegExp(Context cx, Scriptable scope,
5757
Object compiled)
5858
{
59-
return new NativeRegExp(scope, compiled);
59+
return new NativeRegExp(scope, (RECompiled) compiled);
6060
}
6161

6262
public Object action(Context cx, Scriptable scope,
@@ -136,7 +136,7 @@ private static Object matchOrReplace(Context cx, Scriptable scope,
136136
Scriptable topScope = ScriptableObject.getTopLevelScope(scope);
137137

138138
if (args.length == 0) {
139-
Object compiled = NativeRegExp.compileRE(cx, "", "", false);
139+
RECompiled compiled = NativeRegExp.compileRE(cx, "", "", false);
140140
re = new NativeRegExp(topScope, compiled);
141141
} else if (args[0] instanceof NativeRegExp) {
142142
re = (NativeRegExp) args[0];
@@ -149,7 +149,7 @@ private static Object matchOrReplace(Context cx, Scriptable scope,
149149
} else {
150150
opt = null;
151151
}
152-
Object compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat);
152+
RECompiled compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat);
153153
re = new NativeRegExp(topScope, compiled);
154154
}
155155

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
js> x = RegExp('')
2+
/(?:)/
3+
js> x.source
4+
5+
js> RegExp('a').source
6+
a
7+
js> RegExp('\\\\').source
8+
\\
9+
js> RegExp('\\\\\\\\').source
10+
\\\\
11+
js> x = RegExp('/')
12+
/\//
13+
js> x.source
14+
\/
15+
js> x.test('/')
16+
true
17+
js> x = RegExp('//')
18+
/\/\//
19+
js> x.source
20+
\/\/
21+
js> x.test('//')
22+
true
23+
js> x = RegExp('\/')
24+
/\//
25+
js> x.source
26+
\/
27+
js> x.test('/')
28+
true
29+
js> x = RegExp('\\/')
30+
/\//
31+
js> x.source
32+
\/
33+
js> x.test('/')
34+
true
35+
js> x = RegExp('\\\/')
36+
/\//
37+
js> x.source
38+
\/
39+
js> x.test('/')
40+
true
41+
js> x = RegExp('\\\\/')
42+
/\\//
43+
js> x.source
44+
\\/
45+
js> x.test('/')
46+
false
47+
js> x.test('\\/')
48+
true
49+
js> x = RegExp('/abc\/foo\\/bar\\\/xyz/')
50+
/\/abc\/foo\/bar\/xyz\//
51+
js> x.source
52+
\/abc\/foo\/bar\/xyz\/
53+
js> x.test('/abc/foo/bar/xyz/')
54+
true
55+
js> RegExp('[^/]*')
56+
/[^\/]*/
57+
js> RegExp('[^\/]*')
58+
/[^\/]*/
59+
js> RegExp('[^\\/]*')
60+
/[^\/]*/
61+
62+
js> /./.compile('')
63+
/(?:)/
64+
js> /./.compile('a')
65+
/a/
66+
js> /./.compile('\\\\')
67+
/\\/
68+
js> /./.compile('\\\\\\\\')
69+
/\\\\/
70+
js> /./.compile('/')
71+
/\//
72+
js> /./.compile('//')
73+
/\/\//
74+
js> /./.compile('\/')
75+
/\//
76+
js> /./.compile('\\/')
77+
/\//
78+
js> /./.compile('\\\/')
79+
/\//
80+
js> /./.compile('\\\\/')
81+
/\\//
82+
js> /./.compile('/abc\/foo\\/bar\\\/xyz/')
83+
/\/abc\/foo\/bar\/xyz\//
84+
js> /./.compile('[^/]*')
85+
/[^\/]*/
86+
js> /./.compile('[^\/]*')
87+
/[^\/]*/
88+
js> /./.compile('[^\\/]*')
89+
/[^\/]*/
90+

0 commit comments

Comments
 (0)