Bug 1117017 - Initialize the IdSet lazily in jsiter.cpp:Snapshot(). r=sfink.
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 07 Jan 2015 13:47:10 -0800
changeset 222579 e95a4191ea5eddc3e1543fa26633bef16a008d38
parent 222578 51e4e9fcde248895ce6713cdb52cb7e2713f9425
child 222580 5e04df19d58130e890eeb47e52e26eddebead1a9
push id28068
push usercbook@mozilla.com
push dateThu, 08 Jan 2015 13:16:34 +0000
treeherdermozilla-central@2880e05d5e32 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1117017
milestone37.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 1117017 - Initialize the IdSet lazily in jsiter.cpp:Snapshot(). r=sfink.
js/src/jsiter.cpp
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* JavaScript iterators. */
 
 #include "jsiter.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsobj.h"
@@ -40,16 +41,17 @@
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using JS::ForOfIterator;
 
 using mozilla::ArrayLength;
+using mozilla::Maybe;
 #ifdef JS_MORE_DETERMINISTIC
 using mozilla::PodCopy;
 #endif
 using mozilla::PodZero;
 
 typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
 
 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
@@ -91,53 +93,60 @@ NewKeyValuePair(JSContext *cx, jsid id, 
     if (!aobj)
         return false;
     rval.setObject(*aobj);
     return true;
 }
 
 static inline bool
 Enumerate(JSContext *cx, HandleObject pobj, jsid id,
-          bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
+          bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector *props)
 {
     // We implement __proto__ using a property on |Object.prototype|, but
     // because __proto__ is highly deserving of removal, we don't want it to
     // show up in property enumeration, even if only for |Object.prototype|
     // (think introspection by Prototype-like frameworks that add methods to
     // the built-in prototypes).  So exclude __proto__ if the object where the
     // property was found has no [[Prototype]] and might be |Object.prototype|.
     if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto)))
         return true;
 
     if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
+        if (!ht) {
+            ht.emplace(cx);
+            // Most of the time there are only a handful of entries.
+            if (!ht->init(5))
+                return false;
+        }
+
         // If we've already seen this, we definitely won't add it.
-        IdSet::AddPtr p = ht.lookupForAdd(id);
+        IdSet::AddPtr p = ht->lookupForAdd(id);
         if (MOZ_UNLIKELY(!!p))
             return true;
 
         // It's not necessary to add properties to the hash table at the end of
         // the prototype chain, but custom enumeration behaviors might return
         // duplicated properties, so always add in such cases.
-        if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOps()->enumerate) && !ht.add(p, id))
+        if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOps()->enumerate) && !ht->add(p, id))
             return false;
     }
 
     // Symbol-keyed properties and nonenumerable properties are skipped unless
     // the caller specifically asks for them. A caller can also filter out
     // non-symbols by asking for JSITER_SYMBOLSONLY.
     if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
         return true;
     if (!enumerable && !(flags & JSITER_HIDDEN))
         return true;
 
     return props->append(id);
 }
 
 static bool
-EnumerateNativeProperties(JSContext *cx, HandleNativeObject pobj, unsigned flags, IdSet &ht,
+EnumerateNativeProperties(JSContext *cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet> &ht,
                           AutoIdVector *props)
 {
     bool enumerateSymbols;
     if (flags & JSITER_SYMBOLSONLY) {
         enumerateSymbols = true;
     } else {
         /* Collect any dense elements from this object. */
         size_t initlen = pobj->getDenseInitializedLength();
@@ -268,21 +277,19 @@ struct SortComparatorIds
     }
 };
 
 #endif /* JS_MORE_DETERMINISTIC */
 
 static bool
 Snapshot(JSContext *cx, HandleObject pobj_, unsigned flags, AutoIdVector *props)
 {
-    // ~90% of the time this table ends up with 3 or fewer elements.
-    IdSet ht(cx);
-    if (!ht.init(3))
-        return false;
-
+    // We initialize |ht| lazily (in Enumerate()) because it ends up unused
+    // anywhere from 67--99.9% of the time.
+    Maybe<IdSet> ht;
     RootedObject pobj(cx, pobj_);
 
     do {
         if (JSNewEnumerateOp enumerate = pobj->getOps()->enumerate) {
             // This hook has the full control over what gets enumerated.
             AutoIdVector properties(cx);
             if (!enumerate(cx, pobj, properties))
                  return false;