Bug 997590 (part 2) - Create RegExpStaticsObjects lazily. r=sstangl.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 22 Apr 2014 20:13:24 -0700
changeset 180641 91347e9322202bf354fafa5d931c7033942575f6
parent 180640 2e077142ca7335f8286d764c79eafd2caffbcd2a
child 180642 e18d4a0e3bbc466e1a52f3cb3e6ad73cddfb4200
push id42827
push usernnethercote@mozilla.com
push dateMon, 28 Apr 2014 23:50:26 +0000
treeherdermozilla-inbound@91347e932220 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstangl
bugs997590
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 997590 (part 2) - Create RegExpStaticsObjects lazily. r=sstangl.
js/src/builtin/RegExp.cpp
js/src/frontend/Parser.cpp
js/src/jit/IonBuilder.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsstr.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
js/src/vm/RegExpStaticsObject.h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -194,17 +194,19 @@ EscapeNakedForwardSlashes(JSContext *cx,
  *  RegExp, _ => throw TypeError
  *  _ => pattern := ToString(pattern) if defined(pattern) else ''
  *       flags := ToString(flags) if defined(flags) else ''
  */
 static bool
 CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
 {
     if (args.length() == 0) {
-        RegExpStatics *res = cx->global()->getRegExpStatics();
+        RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+        if (!res)
+            return false;
         Rooted<JSAtom*> empty(cx, cx->runtime()->emptyString);
         RegExpObject *reobj = builder.build(empty, res->getFlags());
         if (!reobj)
             return false;
         args.rval().setObject(*reobj);
         return true;
     }
 
@@ -279,17 +281,19 @@ CompileRegExpObject(JSContext *cx, RegEx
 
     RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
     if (!escapedSourceStr)
         return false;
 
     if (!js::RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr))
         return false;
 
-    RegExpStatics *res = cx->global()->getRegExpStatics();
+    RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
     RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
     if (!reobj)
         return false;
 
     args.rval().setObject(*reobj);
     return true;
 }
 
@@ -382,17 +386,19 @@ static const JSFunctionSpec regexp_metho
  *  RegExp.rightContext         $'
  */
 
 #define DEFINE_STATIC_GETTER(name, code)                                        \
     static bool                                                                 \
     name(JSContext *cx, unsigned argc, Value *vp)                               \
     {                                                                           \
         CallArgs args = CallArgsFromVp(argc, vp);                               \
-        RegExpStatics *res = cx->global()->getRegExpStatics();                  \
+        RegExpStatics *res = cx->global()->getRegExpStatics(cx);                \
+        if (!res)                                                               \
+            return false;                                                       \
         code;                                                                   \
     }
 
 DEFINE_STATIC_GETTER(static_input_getter,        return res->createPendingInput(cx, args.rval()))
 DEFINE_STATIC_GETTER(static_multiline_getter,    args.rval().setBoolean(res->multiline());
                                                  return true)
 DEFINE_STATIC_GETTER(static_lastMatch_getter,    return res->createLastMatch(cx, args.rval()))
 DEFINE_STATIC_GETTER(static_lastParen_getter,    return res->createLastParen(cx, args.rval()))
@@ -408,41 +414,47 @@ DEFINE_STATIC_GETTER(static_paren6_gette
 DEFINE_STATIC_GETTER(static_paren7_getter,       return res->createParen(cx, 7, args.rval()))
 DEFINE_STATIC_GETTER(static_paren8_getter,       return res->createParen(cx, 8, args.rval()))
 DEFINE_STATIC_GETTER(static_paren9_getter,       return res->createParen(cx, 9, args.rval()))
 
 #define DEFINE_STATIC_SETTER(name, code)                                        \
     static bool                                                                 \
     name(JSContext *cx, unsigned argc, Value *vp)                               \
     {                                                                           \
-        RegExpStatics *res = cx->global()->getRegExpStatics();                  \
+        RegExpStatics *res = cx->global()->getRegExpStatics(cx);                \
+        if (!res)                                                               \
+            return false;                                                       \
         code;                                                                   \
         return true;                                                            \
     }
 
 static bool
 static_input_setter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RegExpStatics *res = cx->global()->getRegExpStatics();
+    RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
 
     RootedString str(cx, ToString<CanGC>(cx, args.get(0)));
     if (!str)
         return false;
 
     res->setPendingInput(str);
     args.rval().setString(str);
     return true;
 }
 
 static bool
 static_multiline_setter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RegExpStatics *res = cx->global()->getRegExpStatics();
+    RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
 
     bool b = ToBoolean(args.get(0));
     res->setMultiline(cx, b);
     args.rval().setBoolean(b);
     return true;
 }
 
 static const JSPropertySpec regexp_static_props[] = {
@@ -516,19 +528,24 @@ js::ExecuteRegExp(JSContext *cx, HandleO
 {
     /* Step 1 (b) was performed by CallNonGenericMethod. */
     Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
 
     RegExpGuard re(cx);
     if (!reobj->getShared(cx, &re))
         return RegExpRunStatus_Error;
 
-    RegExpStatics *res = (staticsUpdate == UpdateRegExpStatics)
-                         ? cx->global()->getRegExpStatics()
-                         : nullptr;
+    RegExpStatics *res;
+    if (staticsUpdate == UpdateRegExpStatics) {
+        res = cx->global()->getRegExpStatics(cx);
+        if (!res)
+            return RegExpRunStatus_Error;
+    } else {
+        res = nullptr;
+    }
 
     /* Step 3. */
     Rooted<JSLinearString*> input(cx, string->ensureLinear(cx));
     if (!input)
         return RegExpRunStatus_Error;
 
     /* Step 4. */
     RootedValue lastIndex(cx, reobj->getLastIndex());
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6905,17 +6905,20 @@ typename ParseHandler::Node
 Parser<ParseHandler>::newRegExp()
 {
     // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
     const jschar *chars = tokenStream.getTokenbuf().begin();
     size_t length = tokenStream.getTokenbuf().length();
     RegExpFlag flags = tokenStream.currentToken().regExpFlags();
 
     Rooted<RegExpObject*> reobj(context);
-    RegExpStatics *res = context->global()->getRegExpStatics();
+    RegExpStatics *res = context->global()->getRegExpStatics(context);
+    if (!res)
+        return null();
+
     reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
     if (!reobj)
         return null();
 
     return handler.newRegExp(reobj, pos(), *this);
 }
 
 template <typename ParseHandler>
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9418,21 +9418,27 @@ IonBuilder::jsop_regexp(RegExpObject *re
     //
     // First, make sure the regex is one we can safely optimize. Lowering can
     // then check if this regex object only flows into known natives and can
     // avoid cloning in this case.
 
     bool mustClone = true;
     types::TypeObjectKey *typeObj = types::TypeObjectKey::get(&script()->global());
     if (!typeObj->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
-        RegExpStatics *res = script()->global().getRegExpStatics();
-
-        DebugOnly<uint32_t> origFlags = reobj->getFlags();
-        DebugOnly<uint32_t> staticsFlags = res->getFlags();
-        JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
+#ifdef DEBUG
+        // Only compare the statics if the one on script()->global() has been
+        // instantiated.
+        if (script()->global().hasRegExpStatics()) {
+            RegExpStatics *res = script()->global().getAlreadyCreatedRegExpStatics();
+            MOZ_ASSERT(res);
+            uint32_t origFlags = reobj->getFlags();
+            uint32_t staticsFlags = res->getFlags();
+            JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
+        }
+#endif
 
         if (!reobj->global() && !reobj->sticky())
             mustClone = false;
     }
 
     MRegExp *regexp = MRegExp::New(alloc(), constraints(), reobj, mustClone);
     current->add(regexp);
     current->push(regexp);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5730,62 +5730,80 @@ JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObject(JSContext *cx, HandleObject obj, char *bytes, size_t length, unsigned flags)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     jschar *chars = InflateString(cx, bytes, &length);
     if (!chars)
         return nullptr;
 
-    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics();
+    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics(cx);
+    if (!res)
+        return nullptr;
+
     RegExpObject *reobj = RegExpObject::create(cx, res, chars, length,
                                                RegExpFlag(flags), nullptr);
     js_free(chars);
     return reobj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewUCRegExpObject(JSContext *cx, HandleObject obj, jschar *chars, size_t length,
                      unsigned flags)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics();
+    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics(cx);
+    if (!res)
+        return nullptr;
+
     return RegExpObject::create(cx, res, chars, length,
                                 RegExpFlag(flags), nullptr);
 }
 
-JS_PUBLIC_API(void)
+JS_PUBLIC_API(bool)
 JS_SetRegExpInput(JSContext *cx, HandleObject obj, HandleString input, bool multiline)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, input);
 
-    obj->as<GlobalObject>().getRegExpStatics()->reset(cx, input, !!multiline);
-}
-
-JS_PUBLIC_API(void)
+    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics(cx);
+    if (!res)
+        return false;
+
+    res->reset(cx, input, !!multiline);
+    return true;
+}
+
+JS_PUBLIC_API(bool)
 JS_ClearRegExpStatics(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     JS_ASSERT(obj);
 
-    obj->as<GlobalObject>().getRegExpStatics()->clear();
+    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics(cx);
+    if (!res)
+        return false;
+
+    res->clear();
+    return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_ExecuteRegExp(JSContext *cx, HandleObject obj, HandleObject reobj, jschar *chars,
                  size_t length, size_t *indexp, bool test, MutableHandleValue rval)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
-    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics();
+    RegExpStatics *res = obj->as<GlobalObject>().getRegExpStatics(cx);
+    if (!res)
+        return false;
 
     return ExecuteRegExpLegacy(cx, res, reobj->as<RegExpObject>(), NullPtr(), chars, length, indexp,
                                test, rval);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, unsigned flags)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4538,21 +4538,21 @@ JS_ClearDateCaches(JSContext *cx);
 extern JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObject(JSContext *cx, JS::HandleObject obj, char *bytes, size_t length,
                    unsigned flags);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewUCRegExpObject(JSContext *cx, JS::HandleObject obj, jschar *chars, size_t length,
                      unsigned flags);
 
-extern JS_PUBLIC_API(void)
+extern JS_PUBLIC_API(bool)
 JS_SetRegExpInput(JSContext *cx, JS::HandleObject obj, JS::HandleString input,
                   bool multiline);
 
-extern JS_PUBLIC_API(void)
+extern JS_PUBLIC_API(bool)
 JS_ClearRegExpStatics(JSContext *cx, JS::HandleObject obj);
 
 extern JS_PUBLIC_API(bool)
 JS_ExecuteRegExp(JSContext *cx, JS::HandleObject obj, JS::HandleObject reobj,
                  jschar *chars, size_t length, size_t *indexp, bool test,
                  JS::MutableHandleValue rval);
 
 /* RegExp interface for clients without a global object. */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2110,17 +2110,20 @@ js::str_match(JSContext *cx, unsigned ar
     /* Return if there was an error in tryFlatMatch. */
     if (cx->isExceptionPending())
         return false;
 
     /* Create regular-expression internals as needed to perform the match. */
     if (!g.normalizeRegExp(cx, false, 1, args))
         return false;
 
-    RegExpStatics *res = cx->global()->getRegExpStatics();
+    RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
+
     Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
     if (!linearStr)
         return false;
 
     /* Steps 5-6, 7. */
     if (!g.regExp().global())
         return DoMatchLocal(cx, args, res, linearStr, g.regExp());
 
@@ -2151,17 +2154,19 @@ js::str_search(JSContext *cx, unsigned a
         return false;
 
     Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
     if (!linearStr)
         return false;
 
     const jschar *chars = linearStr->chars();
     size_t length = linearStr->length();
-    RegExpStatics *res = cx->global()->getRegExpStatics();
+    RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
 
     /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
     size_t i = 0;
     MatchPair match;
 
     RegExpRunStatus status = g.regExp().executeMatchOnly(cx, chars, length, &i, match);
     if (status == RegExpRunStatus_Error)
         return false;
@@ -2824,26 +2829,36 @@ StrReplaceRegexpRemove(JSContext *cx, Ha
         if (match.isEmpty())
             startIndex++;
 
         /* Non-global removal executes at most once. */
         if (!re.global())
             break;
     }
 
+    RegExpStatics *res;
+
     /* If unmatched, return the input string. */
     if (!lastIndex) {
-        if (startIndex > 0)
-            cx->global()->getRegExpStatics()->updateLazily(cx, flatStr, &re, lazyIndex);
+        if (startIndex > 0) {
+            res = cx->global()->getRegExpStatics(cx);
+            if (!res)
+                return false;
+            res->updateLazily(cx, flatStr, &re, lazyIndex);
+        }
         rval.setString(str);
         return true;
     }
 
     /* The last successful match updates the RegExpStatics. */
-    cx->global()->getRegExpStatics()->updateLazily(cx, flatStr, &re, lazyIndex);
+    res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
+
+    res->updateLazily(cx, flatStr, &re, lazyIndex);
 
     /* Include any remaining part of the string. */
     if (lastIndex < charsLen) {
         if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
             return false;
     }
 
     /* Handle the empty string before calling .begin(). */
@@ -2861,17 +2876,20 @@ StrReplaceRegexpRemove(JSContext *cx, Ha
 }
 
 static inline bool
 StrReplaceRegExp(JSContext *cx, ReplaceData &rdata, MutableHandleValue rval)
 {
     rdata.leftIndex = 0;
     rdata.calledBack = false;
 
-    RegExpStatics *res = cx->global()->getRegExpStatics();
+    RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
+
     RegExpShared &re = rdata.g.regExp();
 
     // The spec doesn't describe this function very clearly, so we go ahead and
     // assume that when the input to String.prototype.replace is a global
     // RegExp, calling the replacer function (assuming one was provided) takes
     // place only after the matching is done. See the comment at the beginning
     // of DoMatchGlobal explaining why we can zero the the RegExp object's
     // lastIndex property here.
@@ -3278,17 +3296,20 @@ SplitHelper(JSContext *cx, Handle<JSLine
         if (splits.length() == limit)
             return NewDenseCopiedArray(cx, splits.length(), splits.begin());
 
         /* Step 13(c)(iii)(5). */
         lastEndIndex = endIndex;
 
         /* Step 13(c)(iii)(6-7). */
         if (Matcher::returnsCaptures) {
-            RegExpStatics *res = cx->global()->getRegExpStatics();
+            RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+            if (!res)
+                return nullptr;
+
             const MatchPairs &matches = res->getMatches();
             for (size_t i = 0; i < matches.parenCount(); i++) {
                 /* Steps 13(c)(iii)(7)(a-c). */
                 if (!matches[i + 1].isUndefined()) {
                     JSSubString parsub;
                     res->getParen(i + 1, &parsub);
                     sub = js_NewStringCopyN<CanGC>(cx, parsub.chars, parsub.length);
                     if (!sub || !splits.append(StringValue(sub)))
@@ -3489,17 +3510,20 @@ js::str_split(JSContext *cx, unsigned ar
     if (!re.initialized()) {
         if (sepstr->length() == 0) {
             aobj = CharSplitHelper(cx, linearStr, limit);
         } else {
             SplitStringMatcher matcher(cx, sepstr);
             aobj = SplitHelper(cx, linearStr, limit, matcher, type);
         }
     } else {
-        SplitRegExpMatcher matcher(*re, cx->global()->getRegExpStatics());
+        RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+        if (!res)
+            return false;
+        SplitRegExpMatcher matcher(*re, res);
         aobj = SplitHelper(cx, linearStr, limit, matcher, type);
     }
     if (!aobj)
         return false;
 
     /* Step 16. */
     aobj->setType(type);
     args.rval().setObject(*aobj);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -560,22 +560,16 @@ GlobalObject::create(JSContext *cx, cons
 
     cx->compartment()->initGlobal(*global);
 
     if (!global->setVarObj(cx))
         return nullptr;
     if (!global->setDelegate(cx))
         return nullptr;
 
-    /* Construct a regexp statics object for this global object. */
-    JSObject *res = RegExpStatics::create(cx, global);
-    if (!res)
-        return nullptr;
-
-    global->initSlot(REGEXP_STATICS, ObjectValue(*res));
     return global;
 }
 
 /* static */ bool
 GlobalObject::getOrCreateEval(JSContext *cx, Handle<GlobalObject*> global,
                               MutableHandleObject eval)
 {
     if (!global->getOrCreateObjectPrototype(cx))
@@ -779,16 +773,51 @@ GlobalObject::getOrCreateForOfPICObject(
     forOfPIC = ForOfPIC::createForOfPICObject(cx, global);
     if (!forOfPIC)
         return nullptr;
     global->setReservedSlot(FOR_OF_PIC_CHAIN, ObjectValue(*forOfPIC));
     return forOfPIC;
 }
 
 bool
+GlobalObject::hasRegExpStatics() const
+{
+    return !getSlot(REGEXP_STATICS).isUndefined();
+}
+
+RegExpStatics *
+GlobalObject::getRegExpStatics(ExclusiveContext *cx) const
+{
+    MOZ_ASSERT(cx);
+    Rooted<GlobalObject*> self(cx, const_cast<GlobalObject*>(this));
+
+    JSObject *resObj = nullptr;
+    const Value &val = this->getSlot(REGEXP_STATICS);
+    if (!val.isObject()) {
+        MOZ_ASSERT(val.isUndefined());
+        resObj = RegExpStatics::create(cx, self);
+        if (!resObj)
+            return nullptr;
+
+        self->initSlot(REGEXP_STATICS, ObjectValue(*resObj));
+    } else {
+        resObj = &val.toObject();
+    }
+    return static_cast<RegExpStatics*>(resObj->getPrivate(/* nfixed = */ 1));
+}
+
+RegExpStatics *
+GlobalObject::getAlreadyCreatedRegExpStatics() const
+{
+    const Value &val = this->getSlot(REGEXP_STATICS);
+    MOZ_ASSERT(val.isObject());
+    return static_cast<RegExpStatics*>(val.toObject().getPrivate(/* nfixed = */ 1));
+}
+
+bool
 GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name,
                                     unsigned nargs, MutableHandleValue funVal)
 {
     RootedId shId(cx, AtomToId(selfHostedName));
     RootedObject holder(cx, cx->global()->intrinsicsHolder());
 
     if (cx->global()->maybeGetIntrinsicValue(shId, funVal.address()))
         return true;
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -587,20 +587,19 @@ class GlobalObject : public JSObject
         RootedObject holder(cx, intrinsicsHolder());
         RootedValue valCopy(cx, value);
         return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
     }
 
     bool getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name,
                                unsigned nargs, MutableHandleValue funVal);
 
-    RegExpStatics *getRegExpStatics() const {
-        JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
-        return static_cast<RegExpStatics *>(resObj.getPrivate(/* nfixed = */ 1));
-    }
+    bool hasRegExpStatics() const;
+    RegExpStatics *getRegExpStatics(ExclusiveContext *cx) const;
+    RegExpStatics *getAlreadyCreatedRegExpStatics() const;
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
     Value createDataViewForThis() const {
         JS_ASSERT(dataViewClassInitialized());
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -101,17 +101,20 @@ RegExpObjectBuilder::clone(Handle<RegExp
     if (!getOrCreateClone(type))
         return nullptr;
 
     /*
      * Check that the RegExpShared for the original is okay to use in
      * the clone -- if the |RegExpStatics| provides more flags we'll
      * need a different |RegExpShared|.
      */
-    RegExpStatics *res = other->getProto()->getParent()->as<GlobalObject>().getRegExpStatics();
+    RegExpStatics *res = other->getProto()->getParent()->as<GlobalObject>().getRegExpStatics(cx);
+    if (!res)
+        return nullptr;
+
     RegExpFlag origFlags = other->getFlags();
     RegExpFlag staticsFlags = res->getFlags();
     if ((origFlags & staticsFlags) != staticsFlags) {
         RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
         Rooted<JSAtom *> source(cx, other->getSource());
         return build(source, newFlags);
     }
 
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -10,18 +10,18 @@
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 /*
  * RegExpStatics allocates memory -- in order to keep the statics stored
  * per-global and not leak, we create a js::Class to wrap the C++ instance and
- * provide an appropriate finalizer. We store an instance of that js::Class in
- * a global reserved slot.
+ * provide an appropriate finalizer. We lazily create and store an instance of
+ * that js::Class in a global reserved slot.
  */
 
 static void
 resc_finalize(FreeOp *fop, JSObject *obj)
 {
     RegExpStatics *res = static_cast<RegExpStatics *>(obj->getPrivate());
     fop->delete_(res);
 }
@@ -48,17 +48,17 @@ const Class RegExpStaticsObject::class_ 
     resc_finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     resc_trace
 };
 
 JSObject *
-RegExpStatics::create(JSContext *cx, GlobalObject *parent)
+RegExpStatics::create(ExclusiveContext *cx, GlobalObject *parent)
 {
     JSObject *obj = NewObjectWithGivenProto(cx, &RegExpStaticsObject::class_, nullptr, parent);
     if (!obj)
         return nullptr;
     RegExpStatics *res = cx->new_<RegExpStatics>();
     if (!res)
         return nullptr;
     obj->setPrivate(static_cast<void *>(res));
@@ -69,17 +69,17 @@ void
 RegExpStatics::markFlagsSet(JSContext *cx)
 {
     // Flags set on the RegExp function get propagated to constructed RegExp
     // objects, which interferes with optimizations that inline RegExp cloning
     // or avoid cloning entirely. Scripts making this assumption listen to
     // type changes on RegExp.prototype, so mark a state change to trigger
     // recompilation of all such code (when recompiling, a stub call will
     // always be performed).
-    JS_ASSERT(this == cx->global()->getRegExpStatics());
+    JS_ASSERT_IF(cx->global()->hasRegExpStatics(), this == cx->global()->getRegExpStatics(cx));
 
     types::MarkTypeObjectFlags(cx, cx->global(), types::OBJECT_FLAG_REGEXP_FLAGS_SET);
 }
 
 bool
 RegExpStatics::executeLazy(JSContext *cx)
 {
     if (!pendingLazyEvaluation)
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -42,17 +42,17 @@ class RegExpStatics
     bool                    pendingLazyEvaluation;
 
     /* Linkage for preserving RegExpStatics during nested RegExp execution. */
     RegExpStatics           *bufferLink;
     bool                    copied;
 
   public:
     RegExpStatics() : bufferLink(nullptr), copied(false) { clear(); }
-    static JSObject *create(JSContext *cx, GlobalObject *parent);
+    static JSObject *create(ExclusiveContext *cx, GlobalObject *parent);
 
   private:
     bool executeLazy(JSContext *cx);
 
     inline void aboutToWrite();
     inline void copyTo(RegExpStatics &dst);
 
     inline void restore();
--- a/js/src/vm/RegExpStaticsObject.h
+++ b/js/src/vm/RegExpStaticsObject.h
@@ -12,15 +12,17 @@
 namespace js {
 
 class RegExpStaticsObject : public JSObject
 {
   public:
     static const Class class_;
 
     size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf) {
+        // XXX: should really call RegExpStatics::sizeOfIncludingThis() here
+        // instead, but the extra memory it would measure is insignificant.
         return mallocSizeOf(getPrivate());
     }
 };
 
 } // namespace js
 
 #endif /* vm_RegExpStaticsObject_h */