Fix non-standard element handling
authorverwaest <verwaest@chromium.org>
Wed, 15 Jul 2015 12:06:20 +0000 (05:06 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 15 Jul 2015 12:06:31 +0000 (12:06 +0000)
BUG=

Review URL: https://codereview.chromium.org/1228113003

Cr-Commit-Position: refs/heads/master@{#29677}

src/elements.cc
src/lookup.cc
src/objects.cc
src/objects.h
test/mjsunit/element-read-only.js [new file with mode: 0644]

index 85eb3f266ecdb135e0ac8081d2b0d08bdd330477..d1ddc8dc481a6778c878e05229344bef86bd22b5 100644 (file)
@@ -950,7 +950,7 @@ class DictionaryElementsAccessor
                               Handle<Object> value,
                               PropertyAttributes attributes) {
     SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(*store);
-    if (attributes != NONE) dictionary->set_requires_slow_elements();
+    if (attributes != NONE) object->RequireSlowElements(dictionary);
     dictionary->ValueAtPut(entry, *value);
     PropertyDetails details = dictionary->DetailsAt(entry);
     details = PropertyDetails(attributes, DATA, details.dictionary_index(),
@@ -969,7 +969,7 @@ class DictionaryElementsAccessor
     Handle<SeededNumberDictionary> new_dictionary =
         SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
                                                details);
-    if (attributes != NONE) new_dictionary->set_requires_slow_elements();
+    if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
     if (dictionary.is_identical_to(new_dictionary)) return;
     object->set_elements(*new_dictionary);
   }
@@ -1614,7 +1614,7 @@ class SlowSloppyArgumentsElementsAccessor
     Handle<SeededNumberDictionary> new_dictionary =
         SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
                                                details);
-    if (attributes != NONE) new_dictionary->set_requires_slow_elements();
+    if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
     if (*dictionary != *new_dictionary) {
       FixedArray::cast(object->elements())->set(1, *new_dictionary);
     }
@@ -1647,6 +1647,10 @@ class SlowSloppyArgumentsElementsAccessor
           SeededNumberDictionary::cast(parameter_map->get(1)));
       arguments = SeededNumberDictionary::AddNumberEntry(arguments, entry,
                                                          value, details);
+      // If the attributes were NONE, we would have called set rather than
+      // reconfigure.
+      DCHECK_NE(NONE, attributes);
+      object->RequireSlowElements(*arguments);
       parameter_map->set(1, *arguments);
     } else {
       Handle<FixedArrayBase> arguments(
index 7bc3aebe84cec4a11bd2bdd69dd1beeb235ad1ae..e63e8f148c57bc602bafa3af22b3fec2804c2122 100644 (file)
@@ -318,16 +318,12 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
                           PropertyCellType::kMutable);
 
   if (IsElement()) {
-    // TODO(verwaest): Remove this hack once we have a quick way to check the
-    // prototype chain in element setters.
     // TODO(verwaest): Move code into the element accessor.
-    bool was_dictionary = receiver->HasDictionaryElements();
     Handle<SeededNumberDictionary> dictionary =
         JSObject::NormalizeElements(receiver);
-    was_dictionary = was_dictionary && dictionary->requires_slow_elements();
 
     dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details);
-    dictionary->set_requires_slow_elements();
+    receiver->RequireSlowElements(*dictionary);
 
     if (receiver->HasSlowArgumentsElements()) {
       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
@@ -338,7 +334,6 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
       FixedArray::cast(receiver->elements())->set(1, *dictionary);
     } else {
       receiver->set_elements(*dictionary);
-      if (!was_dictionary) heap()->ClearAllICsByKind(Code::KEYED_STORE_IC);
     }
   } else {
     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
index 45f7ab9ea32e91083b51d4de6df68028a9810823..34e825945905c9f663744039d231b884934fc8a4 100644 (file)
@@ -4789,6 +4789,16 @@ static Handle<SeededNumberDictionary> CopyFastElementsToDictionary(
 }
 
 
+void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) {
+  if (dictionary->requires_slow_elements()) return;
+  dictionary->set_requires_slow_elements();
+  // TODO(verwaest): Remove this hack.
+  if (map()->is_prototype_map()) {
+    GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC);
+  }
+}
+
+
 Handle<SeededNumberDictionary> JSObject::NormalizeElements(
     Handle<JSObject> object) {
   DCHECK(!object->HasExternalArrayElements() &&
@@ -5430,7 +5440,7 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
   DCHECK(object->HasDictionaryElements() || object->HasSlowArgumentsElements());
 
   // Make sure that we never go back to fast case.
-  dictionary->set_requires_slow_elements();
+  object->RequireSlowElements(*dictionary);
 
   // Do a map transition, other objects with this map may still
   // be extensible.
@@ -5603,7 +5613,7 @@ MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
   if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
     SeededNumberDictionary* dictionary = object->element_dictionary();
     // Make sure we never go back to the fast case
-    dictionary->set_requires_slow_elements();
+    object->RequireSlowElements(dictionary);
     if (attrs != NONE) {
       ApplyAttributesToDictionary(dictionary, attrs);
     }
@@ -6224,11 +6234,20 @@ bool Map::DictionaryElementsInPrototypeChainOnly() {
     if (iter.GetCurrent()->IsJSProxy()) return true;
     // String wrappers have non-configurable, non-writable elements.
     if (iter.GetCurrent()->IsStringWrapper()) return true;
+    JSObject* current = JSObject::cast(iter.GetCurrent());
 
-    if (IsDictionaryElementsKind(
-            JSObject::cast(iter.GetCurrent())->map()->elements_kind())) {
+    if (current->HasDictionaryElements() &&
+        current->element_dictionary()->requires_slow_elements()) {
       return true;
     }
+
+    if (current->HasSlowArgumentsElements()) {
+      FixedArray* parameter_map = FixedArray::cast(current->elements());
+      Object* arguments = parameter_map->get(1);
+      if (SeededNumberDictionary::cast(arguments)->requires_slow_elements()) {
+        return true;
+      }
+    }
   }
 
   return false;
@@ -14663,6 +14682,8 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) {
   // Check if this index is high enough that we should require slow
   // elements.
   if (key > kRequiresSlowElementsLimit) {
+    // TODO(verwaest): Remove this hack.
+    GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC);
     set_requires_slow_elements();
     return;
   }
index 42828c5152deb835dd3e74e96faf702e372a961f..78bbc9916e56ae59f28ce3d949d2c26f19314773 100644 (file)
@@ -2102,6 +2102,8 @@ class JSObject: public JSReceiver {
   static Handle<SeededNumberDictionary> NormalizeElements(
       Handle<JSObject> object);
 
+  void RequireSlowElements(SeededNumberDictionary* dictionary);
+
   // Transform slow named properties to fast variants.
   static void MigrateSlowToFast(Handle<JSObject> object,
                                 int unused_property_fields, const char* reason);
diff --git a/test/mjsunit/element-read-only.js b/test/mjsunit/element-read-only.js
new file mode 100644 (file)
index 0000000..9ec027f
--- /dev/null
@@ -0,0 +1,154 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function f(a, b, c, d) { return arguments; }
+
+// Ensure non-configurable argument elements stay non-configurable.
+(function () {
+  var args = f(1);
+  Object.defineProperty(args, "0", {value: 10, configurable: false});
+  assertFalse(Object.getOwnPropertyDescriptor(args, "0").configurable);
+  for (var i = 0; i < 10; i++) {
+    args[i] = 1;
+  }
+  assertFalse(Object.getOwnPropertyDescriptor(args, "0").configurable);
+})();
+
+// Ensure read-only properties on the prototype chain cause TypeError.
+
+// Newly added.
+(function () {
+  var o = [];
+  var proto = {};
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Reconfigured.
+(function () {
+  var o = [];
+  var proto = {3: 10000};
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Newly added to arguments object.
+(function () {
+  var o = [];
+  var proto = f(100);
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Reconfigured on to arguments object.
+(function () {
+  var o = [];
+  var proto = f(100, 200, 300, 400);
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Extensions prevented object.
+(function () {
+  var o = [];
+  var proto = [0, 1, 2, 3];
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  Object.preventExtensions(proto);
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Extensions prevented arguments object.
+(function () {
+  var o = [];
+  var proto = f(100, 200, 300, 400);
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  Object.preventExtensions(proto);
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Array with large index.
+(function () {
+  var o = [];
+  var proto = [];
+  var index = 3;
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < index; i++) {
+    store(o, i, 0);
+  }
+  proto[1 << 30] = 1;
+  Object.defineProperty(proto, index, {value: 100, writable: false});
+  assertThrows(function() { store(o, index, 0); });
+  assertEquals(100, o[index]);
+})();
+
+// Frozen object.
+(function () {
+  var o = [];
+  var proto = [0, 1, 2, 3];
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < 3; i++) {
+    store(o, i, 0);
+  }
+  Object.freeze(proto);
+  assertThrows(function() { store(o, 3, 0); });
+  assertEquals(3, o[3]);
+})();
+
+// Frozen arguments object.
+(function () {
+  var o = [];
+  var proto = f(0, 1, 2, 3);
+  function store(o, i, v) { "use strict"; o[i] = v; };
+  o.__proto__ = proto;
+  for (var i = 0; i < 3; i++) {
+    store(o, i, 0);
+  }
+  Object.freeze(proto);
+  assertThrows(function() { store(o, 3, 0); });
+  assertEquals(3, o[3]);
+})();