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 324351 47d405339a6fbf6e8ea0350db40607c73a65c324
parent 324350 dec2a039683fe1c0588bbee14817c5416d692fa1
child 324352 023c20b89e6a2dabbf80e25fdc4e8ac3beada472
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersarai, jandem
bugs1192038
milestone53.0a1
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);