Bug 1363150 - Simplify the code for freezing dense elements. r=anba
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 07 Jun 2017 11:42:04 +0200
changeset 362670 b14e3c9ba39cc0b09eb75025a1ec44eb0ebcd576
parent 362669 b55ffc5807df6fb7d563c7af86f02d13ce32a816
child 362671 520ab29a56decd094c94005a44a32876b43538ed
push id31987
push userryanvm@gmail.com
push dateThu, 08 Jun 2017 02:55:14 +0000
treeherdermozilla-central@7efda263a842 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanba
bugs1363150
milestone55.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 1363150 - Simplify the code for freezing dense elements. r=anba
js/src/jit/MIR.cpp
js/src/jsobj.cpp
js/src/vm/NativeObject.cpp
js/src/vm/NativeObject.h
js/src/vm/TypeInference.h
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -6048,17 +6048,17 @@ jit::ElementAccessMightBeCopyOnWrite(Com
     TemporaryTypeSet* types = obj->resultTypeSet();
     return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE);
 }
 
 bool
 jit::ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition* obj)
 {
     TemporaryTypeSet* types = obj->resultTypeSet();
-    return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_FROZEN);
+    return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_FROZEN_ELEMENTS);
 }
 
 AbortReasonOr<bool>
 jit::ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj)
 {
     TemporaryTypeSet* types = obj->resultTypeSet();
 
     if (!types || types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -572,16 +572,22 @@ js::SetIntegrityLevel(JSContext* cx, Han
             }
 
             // 8.a.i-ii. / 9.a.iii.3-4
             if (!DefineProperty(cx, obj, id, desc))
                 return false;
         }
     }
 
+    // Finally, freeze the dense elements.
+    if (level == IntegrityLevel::Frozen && obj->isNative()) {
+        if (!ObjectElements::FreezeElements(cx, obj.as<NativeObject>()))
+            return false;
+    }
+
     return true;
 }
 
 // ES6 draft rev33 (12 Feb 2015) 7.3.15
 bool
 js::TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* result)
 {
     // Steps 3-6. (Steps 1-2 are redundant assertions.)
@@ -2760,39 +2766,27 @@ js::PreventExtensions(JSContext* cx, Han
     if (!MaybeConvertUnboxedObjectToNative(cx, obj))
         return false;
 
     // Force lazy properties to be resolved.
     AutoIdVector props(cx);
     if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
         return false;
 
-    // Actually prevent extension. If the object is being frozen, do it by
-    // setting the frozen flag on both the object and the object group.
-    // Otherwise, fallback to sparsifying the object, which makes sure no
-    // element can be added without a call to isExtensible, at the cost of
-    // performance.
-    if (obj->isNative()) {
-        if (level == IntegrityLevel::Frozen) {
-            MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_FROZEN);
-            if (!ObjectElements::FreezeElements(cx, obj.as<NativeObject>()))
-                return false;
-        } else if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>())) {
+    // Sparsify dense elements, to make sure no element can be added without a
+    // call to isExtensible, at the cost of performance. If the object is being
+    // frozen, the caller is responsible for freezing the elements (and all
+    // other properties).
+    if (obj->isNative() && level != IntegrityLevel::Frozen) {
+        if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
             return false;
-        }
     }
 
-    if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) {
-        // We failed to mark the object non-extensible, so reset the frozen
-        // flag on the elements.
-        MOZ_ASSERT(obj->nonProxyIsExtensible());
-        if (obj->isNative() && obj->as<NativeObject>().getElementsHeader()->isFrozen())
-            obj->as<NativeObject>().getElementsHeader()->markNotFrozen();
+    if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
         return false;
-    }
 
     return result.succeed();
 }
 
 bool
 js::PreventExtensions(JSContext* cx, HandleObject obj, IntegrityLevel level)
 {
     ObjectOpResult result;
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -103,30 +103,30 @@ ObjectElements::MakeElementsCopyOnWrite(
 
     header->ownerObject().init(obj);
     return true;
 }
 
 /* static */ bool
 ObjectElements::FreezeElements(JSContext* cx, HandleNativeObject obj)
 {
+    MOZ_ASSERT_IF(obj->is<ArrayObject>(),
+                  !obj->as<ArrayObject>().lengthIsWritable());
+
     if (!obj->maybeCopyElementsForWrite(cx))
         return false;
 
-    if (obj->hasEmptyElements())
+    if (obj->hasEmptyElements() || obj->denseElementsAreFrozen())
         return true;
 
     if (obj->getElementsHeader()->numShiftedElements() > 0)
         obj->moveShiftedElements();
 
-    ObjectElements* header = obj->getElementsHeader();
-
-    // Note: this method doesn't update type information to indicate that the
-    // elements might be frozen. Handling this is left to the caller.
-    header->freeze();
+    MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_FROZEN_ELEMENTS);
+    obj->getElementsHeader()->freeze();
 
     return true;
 }
 
 #ifdef DEBUG
 void
 js::NativeObject::checkShapeConsistency()
 {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -350,21 +350,16 @@ class ObjectElements
     bool isFrozen() const {
         return flags & FROZEN;
     }
     void freeze() {
         MOZ_ASSERT(!isFrozen());
         MOZ_ASSERT(!isCopyOnWrite());
         flags |= FROZEN;
     }
-    void markNotFrozen() {
-        MOZ_ASSERT(isFrozen());
-        MOZ_ASSERT(!isCopyOnWrite());
-        flags &= ~FROZEN;
-    }
 
     uint8_t elementAttributes() const {
         if (isFrozen())
             return JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
         return JSPROP_ENUMERATE;
     }
 
     uint32_t numShiftedElements() const {
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -141,18 +141,18 @@ enum : uint32_t {
      * Whether any objects this represents may be arrays whose length does not
      * fit in an int32.
      */
     OBJECT_FLAG_LENGTH_OVERFLOW       = 0x00040000,
 
     /* Whether any objects have been iterated over. */
     OBJECT_FLAG_ITERATED              = 0x00080000,
 
-    /* Whether any object this represents may be frozen. */
-    OBJECT_FLAG_FROZEN                = 0x00100000,
+    /* Whether any object this represents may have frozen elements. */
+    OBJECT_FLAG_FROZEN_ELEMENTS       = 0x00100000,
 
     /*
      * For the function on a run-once script, whether the function has actually
      * run multiple times.
      */
     OBJECT_FLAG_RUNONCE_INVALIDATED   = 0x00200000,
 
     /*