Bug 1257979 - Use a GCHashSet for a hash in the JSON stringifying algorithm. r=sfink
authorJeff Walden <jwalden@mit.edu>
Fri, 18 Mar 2016 16:43:53 -0700
changeset 289901 928b0a26ff0f5468b3ffd8a4ff02c42d055c782c
parent 289900 16e429ad56969b36f7b7af0c2f1076771dba8652
child 289902 5e251884f69f8e4ab1149d6ca7f9dc18b7414646
push id18337
push usercbook@mozilla.com
push dateWed, 23 Mar 2016 15:30:25 +0000
treeherderfx-team@67ac681f7e53 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1257979
milestone48.0a1
Bug 1257979 - Use a GCHashSet for a hash in the JSON stringifying algorithm. r=sfink
js/public/GCHashTable.h
js/public/Id.h
js/src/builtin/ModuleObject.h
js/src/jsatom.h
js/src/json.cpp
js/src/vm/RegExpObject.cpp
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -298,16 +298,17 @@ class MutableGCHashSetOperations
     using Enum = typename Set::Enum;
 
     Set& set() { return static_cast<Outer*>(this)->extract(); }
 
   public:
     bool init(uint32_t len = 16) { return set().init(len); }
     void clear()                 { set().clear(); }
     void finish()                { set().finish(); }
+    void remove(Ptr p)           { set().remove(p); }
     void remove(const Lookup& l) { set().remove(l); }
 
     template<typename TInput>
     bool add(AddPtr& p, TInput&& t) {
         return set().add(p, mozilla::Forward<TInput>(t));
     }
 
     template<typename TInput>
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -22,21 +22,25 @@
 
 #include "jstypes.h"
 
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
+namespace js {
+template <typename T> struct DefaultHasher;
+} // namespace js
+
 struct jsid
 {
     size_t asBits;
-    bool operator==(jsid rhs) const { return asBits == rhs.asBits; }
-    bool operator!=(jsid rhs) const { return asBits != rhs.asBits; }
+    bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
+    bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
 } JS_HAZ_GC_POINTER;
 #define JSID_BITS(id) (id.asBits)
 
 #define JSID_TYPE_STRING                 0x0
 #define JSID_TYPE_INT                    0x1
 #define JSID_TYPE_VOID                   0x2
 #define JSID_TYPE_SYMBOL                 0x4
 #define JSID_TYPE_MASK                   0x7
@@ -164,16 +168,28 @@ extern JS_PUBLIC_DATA(const jsid) JSID_V
 extern JS_PUBLIC_DATA(const jsid) JSID_EMPTY;
 
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE;
 
 namespace js {
 
 template <>
+struct DefaultHasher<jsid>
+{
+    typedef jsid Lookup;
+    static HashNumber hash(jsid id) {
+        return JSID_BITS(id);
+    }
+    static bool match(jsid id1, jsid id2) {
+        return id1 == id2;
+    }
+};
+
+template <>
 struct GCPolicy<jsid>
 {
     static jsid initial() { return JSID_VOID; }
     static void trace(JSTracer* trc, jsid* idp, const char* name) {
         js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
     }
 };
 
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -8,16 +8,17 @@
 #define builtin_ModuleObject_h
 
 #include "jsapi.h"
 #include "jsatom.h"
 
 #include "gc/Zone.h"
 
 #include "js/GCVector.h"
+#include "js/Id.h"
 
 #include "vm/NativeObject.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 class ModuleEnvironmentObject;
 class ModuleObject;
@@ -116,17 +117,17 @@ class IndirectBindingMap
   private:
     struct Binding
     {
         Binding(ModuleEnvironmentObject* environment, Shape* shape);
         RelocatablePtr<ModuleEnvironmentObject*> environment;
         RelocatablePtrShape shape;
     };
 
-    typedef HashMap<jsid, Binding, JsidHasher, ZoneAllocPolicy> Map;
+    typedef HashMap<jsid, Binding, DefaultHasher<jsid>, ZoneAllocPolicy> Map;
 
     Map map_;
 };
 
 class ModuleNamespaceObject : public ProxyObject
 {
   public:
     static bool isInstance(HandleValue value);
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -26,27 +26,16 @@ namespace js {
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
 
 static MOZ_ALWAYS_INLINE js::HashNumber
 HashId(jsid id)
 {
     return mozilla::HashGeneric(JSID_BITS(id));
 }
 
-struct JsidHasher
-{
-    typedef jsid Lookup;
-    static HashNumber hash(const Lookup& l) {
-        return HashNumber(JSID_BITS(l));
-    }
-    static bool match(const jsid& id, const Lookup& l) {
-        return id == l;
-    }
-};
-
 /*
  * Return a printable, lossless char[] representation of a string-type atom.
  * The lifetime of the result matches the lifetime of bytes.
  */
 extern const char*
 AtomToPrintableString(ExclusiveContext* cx, JSAtom* atom, JSAutoByteString* bytes);
 
 class AtomStateEntry
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -630,17 +630,17 @@ js::Stringify(JSContext* cx, MutableHand
             if (!GetLengthProperty(cx, replacer, &len))
                 return false;
 
             // Cap the initial size to a moderately small value.  This avoids
             // ridiculous over-allocation if an array with bogusly-huge length
             // is passed in.  If we end up having to add elements past this
             // size, the set will naturally resize to accommodate them.
             const uint32_t MaxInitialSize = 32;
-            HashSet<jsid, JsidHasher> idSet(cx);
+            Rooted<GCHashSet<jsid>> idSet(cx, GCHashSet<jsid>(cx));
             if (!idSet.init(Min(len, MaxInitialSize)))
                 return false;
 
             /* Step 4b(iii)(4). */
             uint32_t k = 0;
 
             /* Step 4b(iii)(5). */
             RootedValue item(cx);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -920,16 +920,19 @@ js::CloneRegExpObject(JSContext* cx, JSO
 
     // Check that the RegExpShared for |regex| is okay to reuse in the clone.
     // If the |RegExpStatics| provides additional flags, we'll need a new
     // |RegExpShared|.
     RegExpStatics* currentStatics = regex->getProto()->global().getRegExpStatics(cx);
     if (!currentStatics)
         return nullptr;
 
+    if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, clone))
+        return nullptr;
+
     Rooted<JSAtom*> source(cx, regex->getSource());
 
     RegExpFlag origFlags = regex->getFlags();
     RegExpFlag staticsFlags = currentStatics->getFlags();
     if ((origFlags & staticsFlags) != staticsFlags) {
         Rooted<RegExpObject*> clone(cx, RegExpAlloc(cx));
         if (!clone)
             return nullptr;