Skip to content

Commit

Permalink
Make sure slashes in RegExp source/toString() are escaped.
Browse files Browse the repository at this point in the history
Fixes bug #510265 except for the source property of empty regexp
where we follow Spidermonkey/V8.
  • Loading branch information
hns committed May 24, 2012
1 parent 20c659b commit 06e155e
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 7 deletions.
32 changes: 28 additions & 4 deletions src/org/mozilla/javascript/regexp/NativeRegExp.java
Expand Up @@ -171,9 +171,9 @@ public static void init(Context cx, Scriptable scope, boolean sealed)
defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM);
}

NativeRegExp(Scriptable scope, Object regexpCompiled)
NativeRegExp(Scriptable scope, RECompiled regexpCompiled)
{
this.re = (RECompiled)regexpCompiled;
this.re = regexpCompiled;
this.lastIndex = 0;
ScriptRuntime.setBuiltinProtoAndParent(this, scope, TopLevel.Builtins.RegExp);
}
Expand Down Expand Up @@ -218,7 +218,7 @@ Scriptable compile(Context cx, Scriptable scope, Object[] args)
this.lastIndex = thatObj.lastIndex;
return this;
}
String s = args.length == 0 ? "" : ScriptRuntime.toString(args[0]);
String s = args.length == 0 ? "" : escapeRegExp(args[0]);
String global = args.length > 1 && args[1] != Undefined.instance
? ScriptRuntime.toString(args[1])
: null;
Expand All @@ -230,7 +230,7 @@ Scriptable compile(Context cx, Scriptable scope, Object[] args)
@Override
public String toString()
{
StringBuffer buf = new StringBuffer();
StringBuilder buf = new StringBuilder();
buf.append('/');
if (re.source.length != 0) {
buf.append(re.source);
Expand All @@ -255,6 +255,30 @@ private static RegExpImpl getImpl(Context cx)
return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
}

private static String escapeRegExp(Object src) {
String s = ScriptRuntime.toString(src);
// Escape any naked slashes in regexp source, see bug #510265
StringBuilder sb = null; // instantiated only if necessary
int start = 0;
int slash = s.indexOf('/');
while (slash > -1) {
if (slash == start || s.charAt(slash - 1) != '\\') {
if (sb == null) {
sb = new StringBuilder();
}
sb.append(s, start, slash);
sb.append("\\/");
start = slash + 1;
}
slash = s.indexOf('/', slash + 1);
}
if (sb != null) {
sb.append(s, start, s.length());
s = sb.toString();
}
return s;
}

private Object execSub(Context cx, Scriptable scopeObj,
Object[] args, int matchType)
{
Expand Down
6 changes: 3 additions & 3 deletions src/org/mozilla/javascript/regexp/RegExpImpl.java
Expand Up @@ -56,7 +56,7 @@ public Object compileRegExp(Context cx, String source, String flags)
public Scriptable wrapRegExp(Context cx, Scriptable scope,
Object compiled)
{
return new NativeRegExp(scope, compiled);
return new NativeRegExp(scope, (RECompiled) compiled);
}

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

if (args.length == 0) {
Object compiled = NativeRegExp.compileRE(cx, "", "", false);
RECompiled compiled = NativeRegExp.compileRE(cx, "", "", false);
re = new NativeRegExp(topScope, compiled);
} else if (args[0] instanceof NativeRegExp) {
re = (NativeRegExp) args[0];
Expand All @@ -149,7 +149,7 @@ private static Object matchOrReplace(Context cx, Scriptable scope,
} else {
opt = null;
}
Object compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat);
RECompiled compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat);
re = new NativeRegExp(topScope, compiled);
}

Expand Down
File renamed without changes.
90 changes: 90 additions & 0 deletions testsrc/doctests/regexp.source.doctest
@@ -0,0 +1,90 @@
js> x = RegExp('')
/(?:)/
js> x.source

js> RegExp('a').source
a
js> RegExp('\\\\').source
\\
js> RegExp('\\\\\\\\').source
\\\\
js> x = RegExp('/')
/\//
js> x.source
\/
js> x.test('/')
true
js> x = RegExp('//')
/\/\//
js> x.source
\/\/
js> x.test('//')
true
js> x = RegExp('\/')
/\//
js> x.source
\/
js> x.test('/')
true
js> x = RegExp('\\/')
/\//
js> x.source
\/
js> x.test('/')
true
js> x = RegExp('\\\/')
/\//
js> x.source
\/
js> x.test('/')
true
js> x = RegExp('\\\\/')
/\\//
js> x.source
\\/
js> x.test('/')
false
js> x.test('\\/')
true
js> x = RegExp('/abc\/foo\\/bar\\\/xyz/')
/\/abc\/foo\/bar\/xyz\//
js> x.source
\/abc\/foo\/bar\/xyz\/
js> x.test('/abc/foo/bar/xyz/')
true
js> RegExp('[^/]*')
/[^\/]*/
js> RegExp('[^\/]*')
/[^\/]*/
js> RegExp('[^\\/]*')
/[^\/]*/

js> /./.compile('')
/(?:)/
js> /./.compile('a')
/a/
js> /./.compile('\\\\')
/\\/
js> /./.compile('\\\\\\\\')
/\\\\/
js> /./.compile('/')
/\//
js> /./.compile('//')
/\/\//
js> /./.compile('\/')
/\//
js> /./.compile('\\/')
/\//
js> /./.compile('\\\/')
/\//
js> /./.compile('\\\\/')
/\\//
js> /./.compile('/abc\/foo\\/bar\\\/xyz/')
/\/abc\/foo\/bar\/xyz\//
js> /./.compile('[^/]*')
/[^\/]*/
js> /./.compile('[^\/]*')
/[^\/]*/
js> /./.compile('[^\\/]*')
/[^\/]*/

0 comments on commit 06e155e

Please sign in to comment.