Bug 1314545 - Fix Ion to handle stores to frozen elements correctly. r=nbp
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 09 Nov 2016 17:50:36 +0100
changeset 348612 3c43332516cc73d418f03fff2e247865a051b207
parent 348611 ef7bd28df4eca8b610f0f6aa1327ff1d81ba2f3a
child 348613 adfcc194af1c2f58b11271b57b3e676f8143c7d3
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1314545
milestone52.0a1
Bug 1314545 - Fix Ion to handle stores to frozen elements correctly. r=nbp
js/src/jit-test/tests/ion/bug1314545.js
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1314545.js
@@ -0,0 +1,31 @@
+function f() {
+    Object.prototype[0] = 10;
+
+    var arr = [];
+    for (var i=3; i<20; i++) {
+        arr[0] = i;
+        Object.freeze(arr);
+        while (!inIon()) {}
+    }
+    assertEq(arr[0], 3);
+}
+f();
+
+function g() {
+    var c = 0;
+    Object.defineProperty(Object.prototype, 18, {set: function() { c++; }});
+
+    var arrays = [];
+    for (var i=0; i<2; i++)
+        arrays.push([1, 2]);
+
+    for (var i=0; i<20; i++) {
+        arrays[0][i] = 1;
+        arrays[1][i] = 2;
+        if (i === 0)
+            Object.freeze(arrays[0]);
+        while (!inIon()) {}
+    }
+    assertEq(c, 2);
+}
+g();
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10389,21 +10389,25 @@ IonBuilder::setElemTryDense(bool* emitte
     // Don't generate a fast path if there have been bounds check failures
     // and this access might be on a sparse property.
     if (ElementAccessHasExtraIndexedProperty(this, object) && failedBoundsCheck_) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
         return true;
     }
 
     // Emit dense setelem variant.
-    if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole))
-        return false;
+    if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted))
+        return false;
+
+    if (!*emitted) {
+        trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
+        return true;
+    }
 
     trackOptimizationSuccess();
-    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::setElemTryArguments(bool* emitted, MDefinition* object,
                                 MDefinition* index, MDefinition* value)
 {
     MOZ_ASSERT(*emitted == false);
@@ -10475,29 +10479,40 @@ IonBuilder::setElemTryCache(bool* emitte
     trackOptimizationSuccess();
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
                                MDefinition* obj, MDefinition* id, MDefinition* value,
-                               JSValueType unboxedType, bool writeHole)
-{
+                               JSValueType unboxedType, bool writeHole, bool* emitted)
+{
+    MOZ_ASSERT(*emitted == false);
+
     MIRType elementType = MIRType::None;
     if (unboxedType == JSVAL_TYPE_MAGIC)
         elementType = DenseNativeElementType(constraints(), obj);
     bool packed = ElementAccessIsPacked(constraints(), obj);
 
     // Writes which are on holes in the object do not have to bail out if they
     // cannot hit another indexed property on the object or its prototypes.
     bool hasNoExtraIndexedProperty = !ElementAccessHasExtraIndexedProperty(this, obj);
 
     bool mayBeFrozen = ElementAccessMightBeFrozen(constraints(), obj);
 
+    if (mayBeFrozen && !hasNoExtraIndexedProperty) {
+        // FallibleStoreElement does not know how to deal with extra indexed
+        // properties on the prototype. This case should be rare so we fall back
+        // to an IC.
+        return true;
+    }
+
+    *emitted = true;
+
     // Ensure id is an integer.
     MInstruction* idInt32 = MToInt32::New(alloc(), id);
     current->add(idInt32);
     id = idInt32;
 
     if (NeedsPostBarrier(value))
         current->add(MPostWriteElementBarrier::New(alloc(), obj, value, id));
 
@@ -10535,25 +10550,28 @@ IonBuilder::jsop_setelem_dense(Temporary
     }
 
     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
     // the initialized length and bounds check.
     // If an object may have been frozen, no previous expectation hold and we
     // fallback to MFallibleStoreElement.
     MInstruction* store;
-    MStoreElementCommon *common = nullptr;
+    MStoreElementCommon* common = nullptr;
     if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) {
         MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
         store = ins;
         common = ins;
 
         current->add(ins);
         current->push(value);
-    } else if (hasNoExtraIndexedProperty && mayBeFrozen) {
+    } else if (mayBeFrozen) {
+        MOZ_ASSERT(hasNoExtraIndexedProperty,
+                   "FallibleStoreElement codegen assumes no extra indexed properties");
+
         bool strict = IsStrictSetPC(pc);
         MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
                                                                 newValue, unboxedType, strict);
         store = ins;
         common = ins;
 
         current->add(ins);
         current->push(value);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -732,17 +732,17 @@ class IonBuilder
     MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index,
                                          JSValueType unboxedType);
     MOZ_MUST_USE bool jsop_getelem_typed(MDefinition* obj, MDefinition* index,
                                          ScalarTypeDescr::Type arrayType);
     MOZ_MUST_USE bool jsop_setelem();
     MOZ_MUST_USE bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
                                          MDefinition* object, MDefinition* index,
                                          MDefinition* value, JSValueType unboxedType,
-                                         bool writeHole);
+                                         bool writeHole, bool* emitted);
     MOZ_MUST_USE bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
                                          MDefinition* object, MDefinition* index,
                                          MDefinition* value);
     MOZ_MUST_USE bool jsop_length();
     MOZ_MUST_USE bool jsop_length_fastPath();
     MOZ_MUST_USE bool jsop_arguments();
     MOZ_MUST_USE bool jsop_arguments_getelem();
     MOZ_MUST_USE bool jsop_runonce();