Bug 1342345 part 1 - Use a Vector for JSON cycle detector. r=evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 25 Feb 2017 12:22:28 +0100
changeset 373959 d82cdc1a1bf3fd95325e9b3132e3cfc6ce531dd1
parent 373958 eb61b6e1e6b36660c95adb2189a33ca29a41164b
child 373960 7e6204be142a74d93a66bb17758fa9fcfe08afb6
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1342345
milestone54.0a1
Bug 1342345 part 1 - Use a Vector for JSON cycle detector. r=evilpie
js/src/json.cpp
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -123,44 +123,40 @@ Quote(JSContext* cx, StringBuffer& sb, J
 
     return linear->hasLatin1Chars()
            ? Quote<Latin1Char>(sb, linear)
            : Quote<char16_t>(sb, linear);
 }
 
 namespace {
 
-using ObjectSet = GCHashSet<JSObject*, MovableCellHasher<JSObject*>, SystemAllocPolicy>;
+using ObjectVector = GCVector<JSObject*, 8>;
 
 class StringifyContext
 {
   public:
     StringifyContext(JSContext* cx, StringBuffer& sb, const StringBuffer& gap,
                      HandleObject replacer, const AutoIdVector& propertyList,
                      bool maybeSafely)
       : sb(sb),
         gap(gap),
         replacer(cx, replacer),
-        stack(cx),
+        stack(cx, ObjectVector(cx)),
         propertyList(propertyList),
         depth(0),
         maybeSafely(maybeSafely)
     {
         MOZ_ASSERT_IF(maybeSafely, !replacer);
         MOZ_ASSERT_IF(maybeSafely, gap.empty());
     }
 
-    bool init() {
-        return stack.init(8);
-    }
-
     StringBuffer& sb;
     const StringBuffer& gap;
     RootedObject replacer;
-    Rooted<ObjectSet> stack;
+    Rooted<ObjectVector> stack;
     const AutoIdVector& propertyList;
     uint32_t depth;
     bool maybeSafely;
 };
 
 } /* anonymous namespace */
 
 static bool Str(JSContext* cx, const Value& v, StringifyContext* scx);
@@ -298,39 +294,42 @@ IsFilteredValue(const Value& v)
 {
     return v.isUndefined() || v.isSymbol() || IsCallable(v);
 }
 
 class CycleDetector
 {
   public:
     CycleDetector(StringifyContext* scx, HandleObject obj)
-      : stack(&scx->stack), obj_(obj) {
+      : stack_(&scx->stack), obj_(obj), appended_(false) {
     }
 
-    bool foundCycle(JSContext* cx) {
-        auto addPtr = stack.lookupForAdd(obj_);
-        if (addPtr) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_JSON_CYCLIC_VALUE);
-            return false;
+    MOZ_ALWAYS_INLINE bool foundCycle(JSContext* cx) {
+        JSObject* obj = obj_;
+        for (JSObject* obj2 : stack_) {
+            if (MOZ_UNLIKELY(obj == obj2)) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_JSON_CYCLIC_VALUE);
+                return false;
+            }
         }
-        if (!stack.add(addPtr, obj_)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        return true;
+        appended_ = stack_.append(obj);
+        return appended_;
     }
 
     ~CycleDetector() {
-        stack.remove(obj_);
+        if (MOZ_LIKELY(appended_)) {
+            MOZ_ASSERT(stack_.back() == obj_);
+            stack_.popBack();
+        }
     }
 
   private:
-    MutableHandle<ObjectSet> stack;
+    MutableHandle<ObjectVector> stack_;
     HandleObject obj_;
+    bool appended_;
 };
 
 /* ES5 15.12.3 JO. */
 static bool
 JO(JSContext* cx, HandleObject obj, StringifyContext* scx)
 {
     /*
      * This method implements the JO algorithm in ES5 15.12.3, but:
@@ -743,18 +742,16 @@ js::Stringify(JSContext* cx, MutableHand
     /* Steps 10-11. */
     RootedId emptyId(cx, NameToId(cx->names().empty));
     if (!NativeDefineProperty(cx, wrapper, emptyId, vp, nullptr, nullptr, JSPROP_ENUMERATE))
         return false;
 
     /* Step 12. */
     StringifyContext scx(cx, sb, gap, replacer, propertyList,
                          stringifyBehavior == StringifyBehavior::RestrictedSafe);
-    if (!scx.init())
-        return false;
     if (!PreprocessValue(cx, wrapper, HandleId(emptyId), vp, &scx))
         return false;
     if (IsFilteredValue(vp))
         return true;
 
     return Str(cx, vp, &scx);
 }