Commit 047b4bfb authored by verwaest's avatar verwaest Committed by Commit bot

Fix non-standard element handling

BUG=

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

Cr-Commit-Position: refs/heads/master@{#29677}
parent a9c7712e
...@@ -950,7 +950,7 @@ class DictionaryElementsAccessor ...@@ -950,7 +950,7 @@ class DictionaryElementsAccessor
Handle<Object> value, Handle<Object> value,
PropertyAttributes attributes) { PropertyAttributes attributes) {
SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(*store); SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(*store);
if (attributes != NONE) dictionary->set_requires_slow_elements(); if (attributes != NONE) object->RequireSlowElements(dictionary);
dictionary->ValueAtPut(entry, *value); dictionary->ValueAtPut(entry, *value);
PropertyDetails details = dictionary->DetailsAt(entry); PropertyDetails details = dictionary->DetailsAt(entry);
details = PropertyDetails(attributes, DATA, details.dictionary_index(), details = PropertyDetails(attributes, DATA, details.dictionary_index(),
...@@ -969,7 +969,7 @@ class DictionaryElementsAccessor ...@@ -969,7 +969,7 @@ class DictionaryElementsAccessor
Handle<SeededNumberDictionary> new_dictionary = Handle<SeededNumberDictionary> new_dictionary =
SeededNumberDictionary::AddNumberEntry(dictionary, index, value, SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
details); 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; if (dictionary.is_identical_to(new_dictionary)) return;
object->set_elements(*new_dictionary); object->set_elements(*new_dictionary);
} }
...@@ -1614,7 +1614,7 @@ class SlowSloppyArgumentsElementsAccessor ...@@ -1614,7 +1614,7 @@ class SlowSloppyArgumentsElementsAccessor
Handle<SeededNumberDictionary> new_dictionary = Handle<SeededNumberDictionary> new_dictionary =
SeededNumberDictionary::AddNumberEntry(dictionary, index, value, SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
details); details);
if (attributes != NONE) new_dictionary->set_requires_slow_elements(); if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
if (*dictionary != *new_dictionary) { if (*dictionary != *new_dictionary) {
FixedArray::cast(object->elements())->set(1, *new_dictionary); FixedArray::cast(object->elements())->set(1, *new_dictionary);
} }
...@@ -1647,6 +1647,10 @@ class SlowSloppyArgumentsElementsAccessor ...@@ -1647,6 +1647,10 @@ class SlowSloppyArgumentsElementsAccessor
SeededNumberDictionary::cast(parameter_map->get(1))); SeededNumberDictionary::cast(parameter_map->get(1)));
arguments = SeededNumberDictionary::AddNumberEntry(arguments, entry, arguments = SeededNumberDictionary::AddNumberEntry(arguments, entry,
value, details); 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); parameter_map->set(1, *arguments);
} else { } else {
Handle<FixedArrayBase> arguments( Handle<FixedArrayBase> arguments(
......
...@@ -318,16 +318,12 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, ...@@ -318,16 +318,12 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
PropertyCellType::kMutable); PropertyCellType::kMutable);
if (IsElement()) { 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. // TODO(verwaest): Move code into the element accessor.
bool was_dictionary = receiver->HasDictionaryElements();
Handle<SeededNumberDictionary> dictionary = Handle<SeededNumberDictionary> dictionary =
JSObject::NormalizeElements(receiver); JSObject::NormalizeElements(receiver);
was_dictionary = was_dictionary && dictionary->requires_slow_elements();
dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details); dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details);
dictionary->set_requires_slow_elements(); receiver->RequireSlowElements(*dictionary);
if (receiver->HasSlowArgumentsElements()) { if (receiver->HasSlowArgumentsElements()) {
FixedArray* parameter_map = FixedArray::cast(receiver->elements()); FixedArray* parameter_map = FixedArray::cast(receiver->elements());
...@@ -338,7 +334,6 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, ...@@ -338,7 +334,6 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
FixedArray::cast(receiver->elements())->set(1, *dictionary); FixedArray::cast(receiver->elements())->set(1, *dictionary);
} else { } else {
receiver->set_elements(*dictionary); receiver->set_elements(*dictionary);
if (!was_dictionary) heap()->ClearAllICsByKind(Code::KEYED_STORE_IC);
} }
} else { } else {
PropertyNormalizationMode mode = receiver->map()->is_prototype_map() PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
......
...@@ -4789,6 +4789,16 @@ static Handle<SeededNumberDictionary> CopyFastElementsToDictionary( ...@@ -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<SeededNumberDictionary> JSObject::NormalizeElements(
Handle<JSObject> object) { Handle<JSObject> object) {
DCHECK(!object->HasExternalArrayElements() && DCHECK(!object->HasExternalArrayElements() &&
...@@ -5430,7 +5440,7 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { ...@@ -5430,7 +5440,7 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
DCHECK(object->HasDictionaryElements() || object->HasSlowArgumentsElements()); DCHECK(object->HasDictionaryElements() || object->HasSlowArgumentsElements());
// Make sure that we never go back to fast case. // 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 // Do a map transition, other objects with this map may still
// be extensible. // be extensible.
...@@ -5603,7 +5613,7 @@ MaybeHandle<Object> JSObject::PreventExtensionsWithTransition( ...@@ -5603,7 +5613,7 @@ MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) { if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
SeededNumberDictionary* dictionary = object->element_dictionary(); SeededNumberDictionary* dictionary = object->element_dictionary();
// Make sure we never go back to the fast case // Make sure we never go back to the fast case
dictionary->set_requires_slow_elements(); object->RequireSlowElements(dictionary);
if (attrs != NONE) { if (attrs != NONE) {
ApplyAttributesToDictionary(dictionary, attrs); ApplyAttributesToDictionary(dictionary, attrs);
} }
...@@ -6224,11 +6234,20 @@ bool Map::DictionaryElementsInPrototypeChainOnly() { ...@@ -6224,11 +6234,20 @@ bool Map::DictionaryElementsInPrototypeChainOnly() {
if (iter.GetCurrent()->IsJSProxy()) return true; if (iter.GetCurrent()->IsJSProxy()) return true;
// String wrappers have non-configurable, non-writable elements. // String wrappers have non-configurable, non-writable elements.
if (iter.GetCurrent()->IsStringWrapper()) return true; if (iter.GetCurrent()->IsStringWrapper()) return true;
JSObject* current = JSObject::cast(iter.GetCurrent());
if (IsDictionaryElementsKind( if (current->HasDictionaryElements() &&
JSObject::cast(iter.GetCurrent())->map()->elements_kind())) { current->element_dictionary()->requires_slow_elements()) {
return true; 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; return false;
...@@ -14663,6 +14682,8 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) { ...@@ -14663,6 +14682,8 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) {
// Check if this index is high enough that we should require slow // Check if this index is high enough that we should require slow
// elements. // elements.
if (key > kRequiresSlowElementsLimit) { if (key > kRequiresSlowElementsLimit) {
// TODO(verwaest): Remove this hack.
GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC);
set_requires_slow_elements(); set_requires_slow_elements();
return; return;
} }
......
...@@ -2102,6 +2102,8 @@ class JSObject: public JSReceiver { ...@@ -2102,6 +2102,8 @@ class JSObject: public JSReceiver {
static Handle<SeededNumberDictionary> NormalizeElements( static Handle<SeededNumberDictionary> NormalizeElements(
Handle<JSObject> object); Handle<JSObject> object);
void RequireSlowElements(SeededNumberDictionary* dictionary);
// Transform slow named properties to fast variants. // Transform slow named properties to fast variants.
static void MigrateSlowToFast(Handle<JSObject> object, static void MigrateSlowToFast(Handle<JSObject> object,
int unused_property_fields, const char* reason); int unused_property_fields, const char* reason);
......
// 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]);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment