Bug 1192038 - Use ordinary object for RegExp prototype. r=arai,jandem
authorTom Schuster <evilpies@gmail.com>
Sat, 26 Nov 2016 19:01:17 +0100
changeset 324318 47d405339a6fbf6e8ea0350db40607c73a65c324
parent 324317 dec2a039683fe1c0588bbee14817c5416d692fa1
child 324319 023c20b89e6a2dabbf80e25fdc4e8ac3beada472
push id30999
push userphilringnalda@gmail.com
push dateSun, 27 Nov 2016 01:56:56 +0000
treeherdermozilla-central@2a0abcff5cfc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai, jandem
bugs1192038
milestone53.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 1192038 - Use ordinary object for RegExp prototype. r=arai,jandem
js/src/builtin/RegExp.cpp
js/src/vm/ObjectGroup.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -572,141 +572,188 @@ js::regexp_clone(JSContext* cx, unsigned
         return false;
 
     regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
 
     args.rval().setObject(*regexp);
     return true;
 }
 
-/* ES6 draft rev32 21.2.5.4. */
+MOZ_ALWAYS_INLINE bool
+IsRegExpInstanceOrPrototype(HandleValue v)
+{
+    if (!v.isObject())
+        return false;
+
+    return StandardProtoKeyOrNull(&v.toObject()) == JSProto_RegExp;
+}
+
+// ES 2017 draft 21.2.5.4.
 MOZ_ALWAYS_INLINE bool
 regexp_global_impl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsRegExpObject(args.thisv()));
+    MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv()));
+
+    // Step 3.a.
+    if (!IsRegExpObject(args.thisv())) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    // Steps 4-6.
     Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
-
-    /* Steps 4-6. */
     args.rval().setBoolean(reObj->global());
     return true;
 }
 
 bool
 js::regexp_global(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    /* Steps 1-3. */
+    // Steps 1-3.
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsRegExpObject, regexp_global_impl>(cx, args);
+    return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_global_impl>(cx, args);
 }
 
-/* ES6 draft rev32 21.2.5.5. */
+// ES 2017 draft 21.2.5.5.
 MOZ_ALWAYS_INLINE bool
 regexp_ignoreCase_impl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsRegExpObject(args.thisv()));
+    MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv()));
+
+    // Step 3.a
+    if (!IsRegExpObject(args.thisv())) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    // Steps 4-6.
     Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
-
-    /* Steps 4-6. */
     args.rval().setBoolean(reObj->ignoreCase());
     return true;
 }
 
 bool
 js::regexp_ignoreCase(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    /* Steps 1-3. */
+    // Steps 1-3.
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsRegExpObject, regexp_ignoreCase_impl>(cx, args);
+    return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_ignoreCase_impl>(cx, args);
 }
 
-/* ES6 draft rev32 21.2.5.7. */
+// ES 2017 draft 21.2.5.7.
 MOZ_ALWAYS_INLINE bool
 regexp_multiline_impl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsRegExpObject(args.thisv()));
+    MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv()));
+
+    // Step 3.a.
+    if (!IsRegExpObject(args.thisv())) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    // Steps 4-6.
     Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
-
-    /* Steps 4-6. */
     args.rval().setBoolean(reObj->multiline());
     return true;
 }
 
 bool
 js::regexp_multiline(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    /* Steps 1-3. */
+    // Steps 1-3.
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsRegExpObject, regexp_multiline_impl>(cx, args);
+    return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_multiline_impl>(cx, args);
 }
 
-/* ES6 draft rev32 21.2.5.10. */
+// ES 2017 draft rev32 21.2.5.10.
 MOZ_ALWAYS_INLINE bool
 regexp_source_impl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsRegExpObject(args.thisv()));
+    MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv()));
+
+    // Step 3.a.
+    if (!IsRegExpObject(args.thisv())) {
+        args.rval().setString(cx->names().emptyRegExp);
+        return true;
+    }
+
+    // Step 5.
     Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
-
-    /* Step 5. */
     RootedAtom src(cx, reObj->getSource());
     if (!src)
         return false;
 
-    /* Step 7. */
+    // Step 7.
     RootedString str(cx, EscapeRegExpPattern(cx, src));
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
 static bool
 regexp_source(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    /* Steps 1-4. */
+    // Steps 1-4.
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsRegExpObject, regexp_source_impl>(cx, args);
+    return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_source_impl>(cx, args);
 }
 
-/* ES6 draft rev32 21.2.5.12. */
+// ES 2017 draft 21.2.5.12.
 MOZ_ALWAYS_INLINE bool
 regexp_sticky_impl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsRegExpObject(args.thisv()));
+    MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv()));
+
+    // Step 3.a.
+    if (!IsRegExpObject(args.thisv())) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    // Steps 4-6.
     Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
-
-    /* Steps 4-6. */
     args.rval().setBoolean(reObj->sticky());
     return true;
 }
 
 bool
 js::regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    /* Steps 1-3. */
+    // Steps 1-3.
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsRegExpObject, regexp_sticky_impl>(cx, args);
+    return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_sticky_impl>(cx, args);
 }
 
-/* ES6 21.2.5.15. */
+// ES 2017 draft 21.2.5.15.
 MOZ_ALWAYS_INLINE bool
 regexp_unicode_impl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsRegExpObject(args.thisv()));
-    /* Steps 4-6. */
-    args.rval().setBoolean(args.thisv().toObject().as<RegExpObject>().unicode());
+    MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv()));
+
+    // Step 3.a.
+    if (!IsRegExpObject(args.thisv())) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    // Steps 4-6.
+    Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
+    args.rval().setBoolean(reObj->unicode());
     return true;
 }
 
 bool
 js::regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    /* Steps 1-3. */
+    // Steps 1-3.
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsRegExpObject, regexp_unicode_impl>(cx, args);
+    return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_unicode_impl>(cx, args);
 }
 
 const JSPropertySpec js::regexp_properties[] = {
     JS_SELF_HOSTED_GET("flags", "RegExpFlagsGetter", 0),
     JS_PSG("global", regexp_global, 0),
     JS_PSG("ignoreCase", regexp_ignoreCase, 0),
     JS_PSG("multiline", regexp_multiline, 0),
     JS_PSG("source", regexp_source, 0),
@@ -824,35 +871,16 @@ const JSPropertySpec js::regexp_static_p
     JS_PSG("$&", static_lastMatch_getter, JSPROP_PERMANENT),
     JS_PSG("$+", static_lastParen_getter, JSPROP_PERMANENT),
     JS_PSG("$`", static_leftContext_getter, JSPROP_PERMANENT),
     JS_PSG("$'", static_rightContext_getter, JSPROP_PERMANENT),
     JS_SELF_HOSTED_SYM_GET(species, "RegExpSpecies", 0),
     JS_PS_END
 };
 
-JSObject*
-js::CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
-{
-    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);
-    proto->initAndZeroLastIndex(source, RegExpFlag(0), cx);
-
-    return proto;
-}
-
 template <typename CharT>
 static bool
 IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
 {
     JS::AutoCheckCannotGC nogc;
     MOZ_ASSERT(index > 0 && index < input->length());
     const CharT* inputChars = input->chars<CharT>(nogc);
 
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -572,17 +572,17 @@ ObjectGroup::defaultNewGroup(ExclusiveCo
          * Some builtin objects have slotful native properties baked in at
          * creation via the Shape::{insert,get}initialShape mechanism. Since
          * these properties are never explicitly defined on new objects, update
          * the type information for them here.
          */
 
         const JSAtomState& names = cx->names();
 
-        if (obj->is<RegExpObject>())
+        if (StandardProtoKeyOrNull(obj) == JSProto_RegExp)
             AddTypePropertyId(cx, group, nullptr, NameToId(names.lastIndex), TypeSet::Int32Type());
 
         if (obj->is<StringObject>())
             AddTypePropertyId(cx, group, nullptr, NameToId(names.length), TypeSet::Int32Type());
 
         if (IsErrorProtoKey(StandardProtoKeyOrNull(obj))) {
             AddTypePropertyId(cx, group, nullptr, NameToId(names.fileName), TypeSet::StringType());
             AddTypePropertyId(cx, group, nullptr, NameToId(names.lineNumber), TypeSet::Int32Type());
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -189,16 +189,22 @@ RegExpObject::trace(JSTracer* trc, JSObj
         !obj->asTenured().zone()->isPreservingCode())
     {
         obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
     } else {
         shared->trace(trc);
     }
 }
 
+static JSObject*
+CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
+{
+    return cx->global()->createBlankPrototype(cx, &RegExpObject::protoClass_);
+}
+
 static const ClassOps RegExpObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
@@ -222,16 +228,23 @@ const Class RegExpObject::class_ = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     &RegExpObjectClassOps,
     &RegExpObjectClassSpec
 };
 
+const Class RegExpObject::protoClass_ = {
+    js_Object_str,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
+    JS_NULL_CLASS_OPS,
+    &RegExpObjectClassSpec
+};
+
 RegExpObject*
 RegExpObject::create(ExclusiveContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
                      TokenStream* tokenStream, LifoAlloc& alloc)
 {
     RootedAtom source(cx, AtomizeChars(cx, chars, length));
     if (!source)
         return nullptr;
 
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -74,19 +74,16 @@ enum RegExpRunStatus
 
 extern RegExpObject*
 RegExpAlloc(ExclusiveContext* cx, HandleObject proto = nullptr);
 
 // |regexp| is under-typed because this function's used in the JIT.
 extern JSObject*
 CloneRegExpObject(JSContext* cx, JSObject* regexp);
 
-extern JSObject*
-CreateRegExpPrototype(JSContext* cx, JSProtoKey key);
-
 /*
  * A RegExpShared is the compiled representation of a regexp. A RegExpShared is
  * potentially pointed to by multiple RegExpObjects. Additionally, C++ code may
  * have pointers to RegExpShareds on the stack. The RegExpShareds are kept in a
  * table so that they can be reused when compiling the same regex string.
  *
  * During a GC, RegExpShared instances are marked and swept like GC things.
  * Usually, RegExpObjects clear their pointers to their RegExpShareds rather
@@ -402,16 +399,17 @@ class RegExpObject : public NativeObject
     static_assert(RegExpObject::FLAGS_SLOT == REGEXP_FLAGS_SLOT,
                   "FLAGS_SLOT values should be in sync with self-hosted JS");
 
   public:
     static const unsigned RESERVED_SLOTS = 3;
     static const unsigned PRIVATE_SLOT = 3;
 
     static const Class class_;
+    static const Class protoClass_;
 
     // The maximum number of pairs a MatchResult can have, without having to
     // allocate a bigger MatchResult.
     static const size_t MaxPairCount = 14;
 
     static RegExpObject*
     create(ExclusiveContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
            frontend::TokenStream* ts, LifoAlloc& alloc);