Bug 1253099. r=arai, r=efaust
authorJeff Walden <jwalden@mit.edu>
Sat, 05 Mar 2016 04:28:53 -0800
changeset 289835 131a296d48a96a30104572f6dd1c9cd078f4d4be
parent 289834 5e251884f69f8e4ab1149d6ca7f9dc18b7414646
child 289836 a45be9c4dd5a282f58d192817c1f2452967593d0
push id74011
push userjwalden@mit.edu
push dateTue, 22 Mar 2016 21:15:56 +0000
treeherdermozilla-inbound@131a296d48a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai, efaust
bugs1253099
milestone48.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 1253099. r=arai, r=efaust
js/src/builtin/RegExp.cpp
js/src/jsstr.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -152,63 +152,62 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
     return CreateRegExpMatchResult(cx, input, matches, rval);
 }
 
 /*
  * ES6 21.2.3.2.2.  Because this function only ever returns |obj| in the spec,
  * provided by the user, we omit it and just return the usual success/failure.
  */
 static bool
-RegExpInitialize(JSContext* cx, Handle<RegExpObject*> obj, HandleValue patternValue,
-                 HandleValue flagsValue, RegExpStaticsUse staticsUse)
+RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
+                                  HandleValue patternValue, HandleValue flagsValue,
+                                  RegExpStaticsUse staticsUse)
 {
     RootedAtom pattern(cx);
     if (patternValue.isUndefined()) {
         /* Step 1. */
-        pattern = cx->runtime()->emptyString;
+        pattern = cx->names().empty;
     } else {
         /* Steps 2-3. */
         pattern = ToAtom<CanGC>(cx, patternValue);
         if (!pattern)
             return false;
     }
 
     /* Step 4. */
     RegExpFlag flags = RegExpFlag(0);
     if (!flagsValue.isUndefined()) {
         /* Steps 5-6. */
         RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue));
         if (!flagStr)
             return false;
+
         /* Step 7. */
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
-    /* Steps 9-10. */
+    /* Steps 8-10. */
     CompileOptions options(cx);
     frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
     if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
                                       flags & UnicodeFlag))
     {
         return false;
     }
 
     if (staticsUse == UseRegExpStatics) {
         RegExpStatics* res = cx->global()->getRegExpStatics(cx);
         if (!res)
             return false;
         flags = RegExpFlag(flags | res->getFlags());
     }
 
-    /* Steps 11-15. */
-    if (!RegExpObject::initFromAtom(cx, obj, pattern, flags))
-        return false;
-
-    /* Step 16. */
+    /* Steps 11-13. */
+    obj->initIgnoringLastIndex(pattern, flags);
     return true;
 }
 
 MOZ_ALWAYS_INLINE bool
 IsRegExpObject(HandleValue v)
 {
     return v.isObject() && v.toObject().is<RegExpObject>();
 }
@@ -277,31 +276,35 @@ regexp_compile_impl(JSContext* cx, const
             RegExpGuard g(cx);
             if (!RegExpToShared(cx, patternObj, &g))
                 return false;
 
             sourceAtom = g->getSource();
             flags = g->getFlags();
         }
 
-        // Step 5.
-        if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags))
+        // Step 5, minus lastIndex zeroing.
+        regexp->initIgnoringLastIndex(sourceAtom, flags);
+    } else {
+        // Step 4.
+        RootedValue P(cx, patternValue);
+        RootedValue F(cx, args.get(1));
+
+        // Step 5, minus lastIndex zeroing.
+        if (!RegExpInitializeIgnoringLastIndex(cx, regexp, P, F, UseRegExpStatics))
             return false;
-
-        args.rval().setObject(*regexp);
-        return true;
     }
 
-    // Step 4.
-    RootedValue P(cx, patternValue);
-    RootedValue F(cx, args.get(1));
-
-    // Step 5.
-    if (!RegExpInitialize(cx, regexp, P, F, UseRegExpStatics))
-        return false;
+    if (regexp->lookupPure(cx->names().lastIndex)->writable()) {
+        regexp->zeroLastIndex(cx);
+    } else {
+        RootedValue zero(cx, Int32Value(0));
+        if (!SetProperty(cx, regexp, cx->names().lastIndex, zero))
+            return false;
+    }
 
     args.rval().setObject(*regexp);
     return true;
 }
 
 static bool
 regexp_compile(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -387,18 +390,17 @@ js::regexp_construct(JSContext* cx, unsi
             flags = RegExpFlag(0);
             RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
             if (!flagStr)
                 return false;
             if (!ParseRegExpFlags(cx, flagStr, &flags))
                 return false;
         }
 
-        if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags))
-            return false;
+        regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
 
         args.rval().setObject(*regexp);
         return true;
     }
 
     RootedValue P(cx);
     RootedValue F(cx);
 
@@ -427,18 +429,19 @@ js::regexp_construct(JSContext* cx, unsi
     if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
         return false;
 
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto));
     if (!regexp)
         return false;
 
     // Step 10.
-    if (!RegExpInitialize(cx, regexp, P, F, UseRegExpStatics))
+    if (!RegExpInitializeIgnoringLastIndex(cx, regexp, P, F, UseRegExpStatics))
         return false;
+    regexp->zeroLastIndex(cx);
 
     args.rval().setObject(*regexp);
     return true;
 }
 
 bool
 js::regexp_construct_no_statics(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -451,18 +454,19 @@ js::regexp_construct_no_statics(JSContex
 
     /* Steps 1-6 are not required since pattern is always string. */
 
     /* Steps 7-10. */
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
     if (!regexp)
         return false;
 
-    if (!RegExpInitialize(cx, regexp, args[0], args.get(1), DontUseRegExpStatics))
+    if (!RegExpInitializeIgnoringLastIndex(cx, regexp, args[0], args.get(1), DontUseRegExpStatics))
         return false;
+    regexp->zeroLastIndex(cx);
 
     args.rval().setObject(*regexp);
     return true;
 }
 
 /* ES6 draft rev32 21.2.5.4. */
 MOZ_ALWAYS_INLINE bool
 regexp_global_impl(JSContext* cx, const CallArgs& args)
@@ -766,19 +770,22 @@ js::CreateRegExpPrototype(JSContext* cx,
 {
     MOZ_ASSERT(key == JSProto_RegExp);
 
     Rooted<RegExpObject*> proto(cx, cx->global()->createBlankPrototype<RegExpObject>(cx));
     if (!proto)
         return nullptr;
     proto->NativeObject::setPrivate(nullptr);
 
+    if (!RegExpObject::assignInitialShape(cx, proto))
+        return nullptr;
+
     RootedAtom source(cx, cx->names().empty);
-    if (!RegExpObject::initFromAtom(cx, proto, source, RegExpFlag(0)))
-        return nullptr;
+    proto->initAndZeroLastIndex(source, RegExpFlag(0), cx);
+
     return proto;
 }
 
 template <typename CharT>
 static bool
 IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
 {
     JS::AutoCheckCannotGC nogc;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2230,17 +2230,17 @@ class MOZ_STACK_CLASS StringRegExpGuard
         if (!regExpIsObject())
             return true;
 
         // Use a fast path for same-global RegExp objects with writable
         // lastIndex.
         if (obj_->is<RegExpObject>()) {
             RegExpObject* nobj = &obj_->as<RegExpObject>();
             if (nobj->lookup(cx, cx->names().lastIndex)->writable()) {
-                nobj->zeroLastIndex();
+                nobj->zeroLastIndex(cx);
                 return true;
             }
         }
 
         // Handle everything else generically (including throwing if .lastIndex is non-writable).
         RootedValue zero(cx, Int32Value(0));
         return SetProperty(cx, obj_, cx->names().lastIndex, zero);
     }
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -41,20 +41,30 @@ JS_STATIC_ASSERT(MultilineFlag == JSREG_
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 JS_STATIC_ASSERT(UnicodeFlag == JSREG_UNICODE);
 
 RegExpObject*
 js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */)
 {
     // Note: RegExp objects are always allocated in the tenured heap. This is
     // not strictly required, but simplifies embedding them in jitcode.
-    RegExpObject* regexp = NewObjectWithClassProto<RegExpObject>(cx, proto, TenuredObject);
+    Rooted<RegExpObject*> regexp(cx);
+
+    regexp = NewObjectWithClassProto<RegExpObject>(cx, proto, TenuredObject);
     if (!regexp)
         return nullptr;
+
     regexp->initPrivate(nullptr);
+
+    if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, regexp))
+        return nullptr;
+
+    MOZ_ASSERT(regexp->lookupPure(cx->names().lastIndex)->slot() ==
+               RegExpObject::lastIndexSlot());
+
     return regexp;
 }
 
 /* MatchPairs */
 
 bool
 MatchPairs::initArrayFrom(MatchPairs& copyFrom)
 {
@@ -170,23 +180,16 @@ RegExpObject::trace(JSTracer* trc, JSObj
         !obj->asTenured().zone()->isPreservingCode())
     {
         obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
     } else {
         shared->trace(trc);
     }
 }
 
-/* static */ bool
-RegExpObject::initFromAtom(ExclusiveContext* cx, Handle<RegExpObject*> regexp, HandleAtom source,
-                           RegExpFlag flags)
-{
-    return regexp->init(cx, source, flags);
-}
-
 const Class RegExpObject::class_ = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -246,18 +249,17 @@ RegExpObject::createNoStatics(ExclusiveC
 
     if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag))
         return nullptr;
 
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
     if (!regexp)
         return nullptr;
 
-    if (!RegExpObject::initFromAtom(cx, regexp, source, flags))
-        return nullptr;
+    regexp->initAndZeroLastIndex(source, flags, cx);
 
     return regexp;
 }
 
 bool
 RegExpObject::createShared(JSContext* cx, RegExpGuard* g)
 {
     Rooted<RegExpObject*> self(cx, this);
@@ -276,37 +278,32 @@ RegExpObject::assignInitialShape(Exclusi
     MOZ_ASSERT(self->empty());
 
     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
 
     /* The lastIndex property alone is writable but non-configurable. */
     return self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT);
 }
 
-bool
-RegExpObject::init(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags)
+void
+RegExpObject::initIgnoringLastIndex(HandleAtom source, RegExpFlag flags)
 {
-    Rooted<RegExpObject*> self(cx, this);
-
-    if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, self))
-        return false;
-
-    MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().lastIndex))->slot() ==
-               LAST_INDEX_SLOT);
+    // If this is a re-initialization with an existing RegExpShared, 'flags'
+    // may not match getShared()->flags, so forget the RegExpShared.
+    NativeObject::setPrivate(nullptr);
 
-    /*
-     * If this is a re-initialization with an existing RegExpShared, 'flags'
-     * may not match getShared()->flags, so forget the RegExpShared.
-     */
-    self->NativeObject::setPrivate(nullptr);
+    setSource(source);
+    setFlags(flags);
+}
 
-    self->zeroLastIndex();
-    self->setSource(source);
-    self->setFlags(flags);
-    return true;
+void
+RegExpObject::initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx)
+{
+    initIgnoringLastIndex(source, flags);
+    zeroLastIndex(cx);
 }
 
 static MOZ_ALWAYS_INLINE bool
 IsLineTerminator(const JS::Latin1Char c)
 {
     return c == '\n' || c == '\r';
 }
 
@@ -936,28 +933,25 @@ js::CloneRegExpObject(JSContext* cx, JSO
     if (!currentStatics)
         return nullptr;
 
     RegExpFlag origFlags = regex->getFlags();
     RegExpFlag staticsFlags = currentStatics->getFlags();
     if ((origFlags & staticsFlags) != staticsFlags) {
         // If |currentStatics| provides additional flags, we'll have to use a
         // new |RegExpShared|.
-        if (!RegExpObject::initFromAtom(cx, clone, source, RegExpFlag(origFlags | staticsFlags)))
-            return nullptr;
+        clone->initAndZeroLastIndex(source, RegExpFlag(origFlags | staticsFlags), cx);
     } else {
         // Otherwise we can use |regexp|'s RegExpShared.  Initialize using its
         // flags and associate it with the clone.
         RegExpGuard g(cx);
         if (!regex->getShared(cx, &g))
             return nullptr;
 
-        if (!RegExpObject::initFromAtom(cx, clone, source, g->getFlags()))
-            return nullptr;
-
+        clone->initAndZeroLastIndex(source, g->getFlags(), cx);
         clone->setShared(*g.re());
     }
 
     return clone;
 }
 
 static bool
 HandleRegExpFlag(RegExpFlag flag, RegExpFlag* flags)
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -398,17 +398,20 @@ class RegExpObject : public NativeObject
     static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
 
     const Value& getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
 
     void setLastIndex(double d) {
         setSlot(LAST_INDEX_SLOT, NumberValue(d));
     }
 
-    void zeroLastIndex() {
+    void zeroLastIndex(ExclusiveContext* cx) {
+        MOZ_ASSERT(lookupPure(cx->names().lastIndex)->writable(),
+                   "can't infallibly zero a non-writable lastIndex on a "
+                   "RegExp that's been exposed to script");
         setSlot(LAST_INDEX_SLOT, Int32Value(0));
     }
 
     JSFlatString* toString(JSContext* cx) const;
 
     JSAtom* getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
 
     void setSource(JSAtom* source) {
@@ -438,22 +441,21 @@ class RegExpObject : public NativeObject
 
     void setShared(RegExpShared& shared) {
         MOZ_ASSERT(!maybeShared());
         NativeObject::setPrivate(&shared);
     }
 
     static void trace(JSTracer* trc, JSObject* obj);
 
-    static bool initFromAtom(ExclusiveContext* cx, Handle<RegExpObject*> regexp, HandleAtom source,
-                             RegExpFlag flags);
+    void initIgnoringLastIndex(HandleAtom source, RegExpFlag flags);
+
+    void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx);
 
   private:
-    bool init(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags);
-
     /*
      * Precondition: the syntax for |source| has already been validated.
      * Side effect: sets the private field.
      */
     bool createShared(JSContext* cx, RegExpGuard* g);
     RegExpShared* maybeShared() const {
         return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT));
     }