Skip to content

Commit

Permalink
Bug 1419785 - Avoid repeated calls to ParsePatternSyntax by using Reg…
Browse files Browse the repository at this point in the history
…ExpShared more. r=arai
  • Loading branch information
jandem committed Nov 22, 2017
1 parent 13364cc commit 6e5bc11
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 28 deletions.
78 changes: 50 additions & 28 deletions js/src/builtin/RegExp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,38 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
}

static bool
CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlag flags)
CheckPatternSyntaxSlow(JSContext* cx, HandleAtom pattern, RegExpFlag flags)
{
CompileOptions options(cx);
frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
return irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
flags & UnicodeFlag);
}

enum RegExpSharedUse {
UseRegExpShared,
DontUseRegExpShared
};
static RegExpShared*
CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlag flags)
{
// If we already have a RegExpShared for this pattern/flags, we can
// avoid the much slower CheckPatternSyntaxSlow call.

if (RegExpShared* shared = cx->zone()->regExps.maybeGet(pattern, flags)) {
#ifdef DEBUG
// Assert the pattern is valid.
if (!CheckPatternSyntaxSlow(cx, pattern, flags)) {
MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
return nullptr;
}
#endif
return shared;
}

if (!CheckPatternSyntaxSlow(cx, pattern, flags))
return nullptr;

// Allocate and return a new RegExpShared so we will hit the fast path
// next time.
return cx->zone()->regExps.get(cx, pattern, flags);
}

/*
* ES 2016 draft Mar 25, 2016 21.2.3.2.2.
Expand All @@ -206,8 +226,7 @@ enum RegExpSharedUse {
*/
static bool
RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
HandleValue patternValue, HandleValue flagsValue,
RegExpSharedUse sharedUse = DontUseRegExpShared)
HandleValue patternValue, HandleValue flagsValue)
{
RootedAtom pattern(cx);
if (patternValue.isUndefined()) {
Expand All @@ -233,24 +252,15 @@ RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
return false;
}

if (sharedUse == UseRegExpShared) {
/* Steps 7-8. */
RegExpShared* re = cx->zone()->regExps.get(cx, pattern, flags);
if (!re)
return false;

/* Steps 9-12. */
obj->initIgnoringLastIndex(pattern, flags);
/* Steps 7-8. */
RegExpShared* shared = CheckPatternSyntax(cx, pattern, flags);
if (!shared)
return false;

obj->setShared(*re);
} else {
/* Steps 7-8. */
if (!CheckPatternSyntax(cx, pattern, flags))
return false;
/* Steps 9-12. */
obj->initIgnoringLastIndex(pattern, flags);

/* Steps 9-12. */
obj->initIgnoringLastIndex(pattern, flags);
}
obj->setShared(*shared);

return true;
}
Expand All @@ -266,7 +276,7 @@ js::RegExpCreate(JSContext* cx, HandleValue patternValue, HandleValue flagsValue
return false;

/* Step 2. */
if (!RegExpInitializeIgnoringLastIndex(cx, regexp, patternValue, flagsValue, UseRegExpShared))
if (!RegExpInitializeIgnoringLastIndex(cx, regexp, patternValue, flagsValue))
return false;
regexp->zeroLastIndex(cx);

Expand Down Expand Up @@ -428,22 +438,26 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
return false;
if (cls == ESClass::RegExp) {
// Beware! |patternObj| might be a proxy into another compartment, so
// don't assume |patternObj.is<RegExpObject>()|. For the same reason,
// don't reuse the RegExpShared below.
// don't assume |patternObj.is<RegExpObject>()|.
RootedObject patternObj(cx, &patternValue.toObject());

RootedAtom sourceAtom(cx);
RegExpFlag flags;
RootedRegExpShared shared(cx);
{
// Step 4.a.
RegExpShared* shared = RegExpToShared(cx, patternObj);
shared = RegExpToShared(cx, patternObj);
if (!shared)
return false;
sourceAtom = shared->getSource();

// Step 4.b.
// Get original flags in all cases, to compare with passed flags.
flags = shared->getFlags();

// If the RegExpShared is in another Zone, don't reuse it.
if (cx->zone() != shared->zone())
shared = nullptr;
}

// Step 7.
Expand All @@ -465,19 +479,27 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
if (!ParseRegExpFlags(cx, flagStr, &flagsArg))
return false;

// Don't reuse the RegExpShared if we have different flags.
if (flags != flagsArg)
shared = nullptr;

if (!(flags & UnicodeFlag) && flagsArg & UnicodeFlag) {
// Have to check syntax again when adding 'u' flag.

// ES 2017 draft rev 9b49a888e9dfe2667008a01b2754c3662059ae56
// 21.2.3.2.2 step 7.
if (!CheckPatternSyntax(cx, sourceAtom, flagsArg))
shared = CheckPatternSyntax(cx, sourceAtom, flagsArg);
if (!shared)
return false;
}
flags = flagsArg;
}

regexp->initAndZeroLastIndex(sourceAtom, flags, cx);

if (shared)
regexp->setShared(*shared);

args.rval().setObject(*regexp);
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions js/src/jit-test/tests/regexp/bug1419785.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// |jit-test| error:character class
Object.defineProperty(RegExp.prototype, Symbol.search, {get: () => { throw "wrong"; }});
"abc".search("[[");
5 changes: 5 additions & 0 deletions js/src/vm/RegExpShared.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ class RegExpZone

bool empty() const { return set_.empty(); }

RegExpShared* maybeGet(JSAtom* source, RegExpFlag flags) const {
Set::Ptr p = set_.lookup(Key(source, flags));
return p ? *p : nullptr;
}

RegExpShared* get(JSContext* cx, HandleAtom source, RegExpFlag flags);

/* Like 'get', but compile 'maybeOpt' (if non-null). */
Expand Down

0 comments on commit 6e5bc11

Please sign in to comment.