Bug 1394682 - Use template object for iterator result object. r=djvj
authorTooru Fujisawa <arai_a@mac.com>
Wed, 20 Sep 2017 18:43:01 +0900
changeset 431407 4c86474c75be02a4d568a33bce49d31bbbf88fa5
parent 431406 1d76fe320d1a10c903ea139296c35bde38136db8
child 431408 6d8d80beb8c82d1617b9fe939fe6035df3044738
push id7784
push userryanvm@gmail.com
push dateThu, 21 Sep 2017 00:40:13 +0000
treeherdermozilla-beta@efff4f307675 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1394682
milestone57.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 1394682 - Use template object for iterator result object. r=djvj
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsiter.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/NativeObject.h
js/src/vm/TypeInference.h
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -93,16 +93,17 @@ JSCompartment::JSCompartment(Zone* zone,
     debugEnvs(nullptr),
     enumerators(nullptr),
     compartmentStats_(nullptr),
     scheduledForDestruction(false),
     maybeAlive(true),
     jitCompartment_(nullptr),
     mappedArgumentsTemplate_(nullptr),
     unmappedArgumentsTemplate_(nullptr),
+    iterResultTemplate_(nullptr),
     lcovOutput()
 {
     PodArrayZero(sawDeprecatedLanguageExtension);
     runtime_->numCompartments++;
     MOZ_ASSERT_IF(creationOptions_.mergeable(),
                   creationOptions_.invisibleToDebugger());
 }
 
@@ -988,16 +989,19 @@ CrossCompartmentKey::needsSweep()
 void
 JSCompartment::sweepTemplateObjects()
 {
     if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
         mappedArgumentsTemplate_.set(nullptr);
 
     if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
         unmappedArgumentsTemplate_.set(nullptr);
+
+    if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_))
+        iterResultTemplate_.set(nullptr);
 }
 
 /* static */ void
 JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
 {
     MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());
 
     for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -1193,27 +1193,32 @@ struct JSCompartment
     bool scheduledForDestruction;
     bool maybeAlive;
 
   private:
     js::jit::JitCompartment* jitCompartment_;
 
     js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_;
     js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
+    js::ReadBarriered<js::NativeObject*> iterResultTemplate_;
 
   public:
     bool ensureJitCompartmentExists(JSContext* cx);
     js::jit::JitCompartment* jitCompartment() {
         return jitCompartment_;
     }
 
     js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
 
     js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
 
+    static const size_t IterResultObjectValueSlot = 0;
+    static const size_t IterResultObjectDoneSlot = 1;
+    js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
+
   private:
     // Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
     bool sawDeprecatedLanguageExtension[size_t(js::DeprecatedLanguageExtension::Count)];
 
     void reportTelemetry();
 
   public:
     void addTelemetry(const char* filename, js::DeprecatedLanguageExtension e);
--- 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/DebugOnly.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Unused.h"
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
@@ -41,16 +42,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::DebugOnly;
 using mozilla::Maybe;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::PodZero;
 
 typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
 
 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
@@ -977,35 +979,88 @@ js::LookupInIteratorCache(JSContext* cx,
 
 // ES 2017 draft 7.4.7.
 JSObject*
 js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
 {
     // Step 1 (implicit).
 
     // Step 2.
-    RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
-    if (!resultObj)
+    RootedObject templateObject(cx, cx->compartment()->getOrCreateIterResultTemplateObject(cx));
+    if (!templateObject)
         return nullptr;
 
+    NativeObject* resultObj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, resultObj, NativeObject::createWithTemplate(cx, gc::DefaultHeap,
+                                                                              templateObject));
+
     // Step 3.
-    if (!DefineDataProperty(cx, resultObj, cx->names().value, value))
-        return nullptr;
+    resultObj->setSlot(JSCompartment::IterResultObjectValueSlot, value);
 
     // Step 4.
-    if (!DefineDataProperty(cx, resultObj, cx->names().done,
-                            done ? TrueHandleValue : FalseHandleValue))
-    {
-        return nullptr;
-    }
+    resultObj->setSlot(JSCompartment::IterResultObjectDoneSlot,
+                       done ? TrueHandleValue : FalseHandleValue);
 
     // Step 5.
     return resultObj;
 }
 
+NativeObject*
+JSCompartment::getOrCreateIterResultTemplateObject(JSContext* cx)
+{
+    if (iterResultTemplate_)
+        return iterResultTemplate_;
+
+    // Create template plain object
+    RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
+    if (!templateObject)
+        return iterResultTemplate_; // = nullptr
+
+    // Create a new group for the template.
+    Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
+    RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(),
+                                                                  proto));
+    if (!group)
+        return iterResultTemplate_; // = nullptr
+    templateObject->setGroup(group);
+
+    // Set dummy `value` property
+    if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
+                                  JSPROP_ENUMERATE))
+    {
+        return iterResultTemplate_; // = nullptr
+    }
+
+    // Set dummy `done` property
+    if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
+                                  JSPROP_ENUMERATE))
+    {
+        return iterResultTemplate_; // = nullptr
+    }
+
+    // Update `value` property typeset, since it can be any value.
+    HeapTypeSet* types = group->maybeGetProperty(NameToId(cx->names().value));
+    MOZ_ASSERT(types);
+    {
+        AutoEnterAnalysis enter(cx);
+        types->makeUnknown(cx);
+    }
+
+    // Make sure that the properties are in the right slots.
+    DebugOnly<Shape*> shape = templateObject->lastProperty();
+    MOZ_ASSERT(shape->previous()->slot() == JSCompartment::IterResultObjectValueSlot &&
+               shape->previous()->propidRef() == NameToId(cx->names().value));
+    MOZ_ASSERT(shape->slot() == JSCompartment::IterResultObjectDoneSlot &&
+               shape->propidRef() == NameToId(cx->names().done));
+
+    iterResultTemplate_.set(templateObject);
+
+    return iterResultTemplate_;
+}
+
 bool
 js::ThrowStopIteration(JSContext* cx)
 {
     MOZ_ASSERT(!JS_IsExceptionPending(cx));
 
     // StopIteration isn't a constructor, but it's stored in GlobalObject
     // as one, out of laziness. Hence the GetBuiltinConstructor call here.
     RootedObject ctor(cx);
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -495,16 +495,30 @@ NativeObject::create(JSContext* cx, js::
     else
         nobj = SetNewObjectMetadata(cx, nobj);
 
     js::gc::TraceCreateObject(nobj);
 
     return nobj;
 }
 
+/* static */ inline JS::Result<NativeObject*, JS::OOM&>
+NativeObject::createWithTemplate(JSContext* cx, js::gc::InitialHeap heap,
+                                 HandleObject templateObject)
+{
+    RootedObjectGroup group(cx, templateObject->group());
+    RootedShape shape(cx, templateObject->as<NativeObject>().lastProperty());
+
+    gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
+    MOZ_ASSERT(CanBeFinalizedInBackground(kind, shape->getObjectClass()));
+    kind = gc::GetBackgroundAllocKind(kind);
+
+    return create(cx, kind, heap, shape, group);
+}
+
 MOZ_ALWAYS_INLINE uint32_t
 NativeObject::numDynamicSlots() const
 {
     return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
 }
 
 /* static */ MOZ_ALWAYS_INLINE uint32_t
 NativeObject::dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class* clasp)
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -538,16 +538,19 @@ class NativeObject : public ShapedObject
     }
 
     inline bool isInWholeCellBuffer() const;
 
     static inline JS::Result<NativeObject*, JS::OOM&>
     create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
            js::HandleShape shape, js::HandleObjectGroup group);
 
+    static inline JS::Result<NativeObject*, JS::OOM&>
+    createWithTemplate(JSContext* cx, js::gc::InitialHeap heap, HandleObject templateObject);
+
   protected:
 #ifdef DEBUG
     void checkShapeConsistency();
 #else
     void checkShapeConsistency() { }
 #endif
 
     static Shape*
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -695,16 +695,21 @@ class ConstraintTypeSet : public TypeSet
     }
 
     /*
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     void addType(JSContext* cx, Type type);
 
+    /* Generalize to any type. */
+    void makeUnknown(JSContext* cx) {
+        addType(cx, UnknownType());
+    }
+
     // Trigger a post barrier when writing to this set, if necessary.
     // addType(cx, type) takes care of this automatically.
     void postWriteBarrier(JSContext* cx, Type type);
 
     /* Add a new constraint to this set. */
     bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);
 
     inline void sweep(JS::Zone* zone, AutoClearTypeInferenceStateOnOOM& oom);