Fix redefinition of aliased elements in arguments.

This refactors the way we (re)define elements to perform normalization
and attribute updating at a much deeper level, thereby removing some
bogus special cases in upper runtime layers.

Most element setters take an indicator flag that distinguishes between
setting and defining. Setting of an element causes attributes to remain
unchanged, writability to be checked and callbacks to be called.
Defining of an element causes attributes to be updated and callbacks to
be overridden. The same approach could be taken for properties.

R=svenpanne@chromium.org
BUG=v8:1772
TEST=test262,test262/15.2.3.6-4-333-11

Review URL: https://chromiumcodereview.appspot.com/9443014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10808 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 67c6e926
......@@ -2760,6 +2760,7 @@ bool v8::Object::Set(uint32_t index, v8::Handle<Value> value) {
self,
index,
value_obj,
NONE,
i::kNonStrictMode);
has_pending_exception = obj.is_null();
EXCEPTION_BAILOUT_CHECK(isolate, false);
......
......@@ -1329,7 +1329,7 @@ MaybeObject* StoreIC::Store(State state,
uint32_t index;
if (name->AsArrayIndex(&index)) {
Handle<Object> result =
JSObject::SetElement(receiver, index, value, strict_mode);
JSObject::SetElement(receiver, index, value, NONE, strict_mode);
RETURN_IF_EMPTY_HANDLE(isolate(), result);
return *value;
}
......@@ -1786,7 +1786,7 @@ MaybeObject* KeyedStoreIC::Store(State state,
uint32_t index;
if (name->AsArrayIndex(&index)) {
Handle<Object> result =
JSObject::SetElement(receiver, index, value, strict_mode);
JSObject::SetElement(receiver, index, value, NONE, strict_mode);
RETURN_IF_EMPTY_HANDLE(isolate(), result);
return *value;
}
......
......@@ -53,8 +53,8 @@ void SetElementNonStrict(Handle<JSObject> object,
// Ignore return value from SetElement. It can only be a failure if there
// are element setters causing exceptions and the debugger context has none
// of these.
Handle<Object> no_failure;
no_failure = JSObject::SetElement(object, index, value, kNonStrictMode);
Handle<Object> no_failure =
JSObject::SetElement(object, index, value, NONE, kNonStrictMode);
ASSERT(!no_failure.is_null());
USE(no_failure);
}
......
This diff is collapsed.
......@@ -1348,6 +1348,16 @@ enum EnsureElementsMode {
};
// Indicates whether a property should be set or (re)defined. Setting of a
// property causes attributes to remain unchanged, writability to be checked
// and callbacks to be called. Defining of a property causes attributes to
// be updated and callbacks to be overridden.
enum SetPropertyMode {
SET_PROPERTY,
DEFINE_PROPERTY
};
// JSReceiver includes types on which properties can be defined, i.e.,
// JSObject and JSProxy.
class JSReceiver: public HeapObject {
......@@ -1386,6 +1396,7 @@ class JSReceiver: public HeapObject {
// Can cause GC, or return failure if GC is required.
MUST_USE_RESULT MaybeObject* SetElement(uint32_t index,
Object* value,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype);
......@@ -1739,10 +1750,13 @@ class JSObject: public JSReceiver {
StrictModeFlag strict_mode,
bool check_prototype);
MUST_USE_RESULT MaybeObject* SetDictionaryElement(uint32_t index,
Object* value,
StrictModeFlag strict_mode,
bool check_prototype);
MUST_USE_RESULT MaybeObject* SetDictionaryElement(
uint32_t index,
Object* value,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype,
SetPropertyMode set_mode = SET_PROPERTY);
MUST_USE_RESULT MaybeObject* SetFastDoubleElement(
uint32_t index,
......@@ -1750,23 +1764,28 @@ class JSObject: public JSReceiver {
StrictModeFlag strict_mode,
bool check_prototype = true);
static Handle<Object> SetOwnElement(Handle<JSObject> object,
uint32_t index,
Handle<Object> value,
StrictModeFlag strict_mode);
// Empty handle is returned if the element cannot be set to the given value.
static MUST_USE_RESULT Handle<Object> SetElement(Handle<JSObject> object,
uint32_t index,
Handle<Object> value,
StrictModeFlag strict_mode);
static MUST_USE_RESULT Handle<Object> SetElement(
Handle<JSObject> object,
uint32_t index,
Handle<Object> value,
PropertyAttributes attr,
StrictModeFlag strict_mode,
SetPropertyMode set_mode = SET_PROPERTY);
// A Failure object is returned if GC is needed.
MUST_USE_RESULT MaybeObject* SetElement(uint32_t index,
Object* value,
StrictModeFlag strict_mode,
bool check_prototype);
MUST_USE_RESULT MaybeObject* SetElement(
uint32_t index,
Object* value,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype = true,
SetPropertyMode set_mode = SET_PROPERTY);
// Returns the index'th element.
// The undefined object if index is out of bounds.
......@@ -2087,13 +2106,17 @@ class JSObject: public JSReceiver {
MUST_USE_RESULT MaybeObject* SetElementWithInterceptor(
uint32_t index,
Object* value,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype);
bool check_prototype,
SetPropertyMode set_mode);
MUST_USE_RESULT MaybeObject* SetElementWithoutInterceptor(
uint32_t index,
Object* value,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype);
bool check_prototype,
SetPropertyMode set_mode);
// Searches the prototype chain for a callback setter and sets the property
// with the setter if it finds one. The '*found' flag indicates whether
......
......@@ -4355,53 +4355,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
// Check if this is an element.
uint32_t index;
bool is_element = name->AsArrayIndex(&index);
// Special case for elements if any of the flags might be involved.
// If elements are in fast case we always implicitly assume that:
// DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
if (is_element && (attr != NONE ||
js_object->HasLocalElement(index) == JSObject::DICTIONARY_ELEMENT)) {
// Normalize the elements to enable attributes on the property.
if (js_object->IsJSGlobalProxy()) {
// We do not need to do access checks here since these has already
// been performed by the call to GetOwnProperty.
Handle<Object> proto(js_object->GetPrototype());
// If proxy is detached, ignore the assignment. Alternatively,
// we could throw an exception.
if (proto->IsNull()) return *obj_value;
js_object = Handle<JSObject>::cast(proto);
}
// Don't allow element properties to be redefined on objects with external
// array elements.
if (js_object->HasExternalArrayElements()) {
Handle<Object> args[2] = { js_object, name };
Handle<Object> error =
isolate->factory()->NewTypeError("redef_external_array_element",
HandleVector(args, 2));
return isolate->Throw(*error);
}
Handle<SeededNumberDictionary> dictionary =
JSObject::NormalizeElements(js_object);
// Make sure that we never go back to fast case.
dictionary->set_requires_slow_elements();
PropertyDetails details = PropertyDetails(attr, NORMAL);
Handle<SeededNumberDictionary> extended_dictionary =
SeededNumberDictionary::Set(dictionary, index, obj_value, details);
if (*extended_dictionary != *dictionary) {
if (js_object->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) {
FixedArray::cast(js_object->elements())->set(1, *extended_dictionary);
} else {
js_object->set_elements(*extended_dictionary);
}
}
return *obj_value;
}
LookupResult result(isolate);
js_object->LocalLookupRealNamedProperty(*name, &result);
......@@ -4457,35 +4410,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
}
// Special case for elements if any of the flags are true.
// If elements are in fast case we always implicitly assume that:
// DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
static MaybeObject* NormalizeObjectSetElement(Isolate* isolate,
Handle<JSObject> js_object,
uint32_t index,
Handle<Object> value,
PropertyAttributes attr) {
// Normalize the elements to enable attributes on the property.
Handle<SeededNumberDictionary> dictionary =
JSObject::NormalizeElements(js_object);
// Make sure that we never go back to fast case.
dictionary->set_requires_slow_elements();
PropertyDetails details = PropertyDetails(attr, NORMAL);
Handle<SeededNumberDictionary> extended_dictionary =
SeededNumberDictionary::Set(dictionary, index, value, details);
if (*extended_dictionary != *dictionary) {
js_object->set_elements(*extended_dictionary);
}
return *value;
}
MaybeObject* Runtime::SetObjectProperty(Isolate* isolate,
Handle<Object> object,
Handle<Object> key,
Handle<Object> value,
PropertyAttributes attr,
StrictModeFlag strict_mode) {
SetPropertyMode set_mode = attr == NONE ? SET_PROPERTY : DEFINE_PROPERTY;
HandleScope scope(isolate);
if (object->IsUndefined() || object->IsNull()) {
......@@ -4523,12 +4454,8 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate,
return *value;
}
if (((attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0)) {
return NormalizeObjectSetElement(isolate, js_object, index, value, attr);
}
Handle<Object> result =
JSObject::SetElement(js_object, index, value, strict_mode);
Handle<Object> result = JSObject::SetElement(
js_object, index, value, attr, strict_mode, set_mode);
if (result.is_null()) return Failure::Exception();
return *value;
}
......@@ -4536,15 +4463,8 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate,
if (key->IsString()) {
Handle<Object> result;
if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
if (((attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0)) {
return NormalizeObjectSetElement(isolate,
js_object,
index,
value,
attr);
}
result =
JSObject::SetElement(js_object, index, value, strict_mode);
result = JSObject::SetElement(
js_object, index, value, attr, strict_mode, set_mode);
} else {
Handle<String> key_string = Handle<String>::cast(key);
key_string->TryFlatten();
......@@ -4562,7 +4482,8 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate,
Handle<String> name = Handle<String>::cast(converted);
if (name->AsArrayIndex(&index)) {
return js_object->SetElement(index, *value, strict_mode, true);
return js_object->SetElement(
index, *value, attr, strict_mode, true, set_mode);
} else {
return js_object->SetProperty(*name, *value, attr, strict_mode);
}
......@@ -4590,12 +4511,14 @@ MaybeObject* Runtime::ForceSetObjectProperty(Isolate* isolate,
return *value;
}
return js_object->SetElement(index, *value, kNonStrictMode, true);
return js_object->SetElement(
index, *value, attr, kNonStrictMode, false, DEFINE_PROPERTY);
}
if (key->IsString()) {
if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
return js_object->SetElement(index, *value, kNonStrictMode, true);
return js_object->SetElement(
index, *value, attr, kNonStrictMode, false, DEFINE_PROPERTY);
} else {
Handle<String> key_string = Handle<String>::cast(key);
key_string->TryFlatten();
......@@ -4612,7 +4535,8 @@ MaybeObject* Runtime::ForceSetObjectProperty(Isolate* isolate,
Handle<String> name = Handle<String>::cast(converted);
if (name->AsArrayIndex(&index)) {
return js_object->SetElement(index, *value, kNonStrictMode, true);
return js_object->SetElement(
index, *value, attr, kNonStrictMode, false, DEFINE_PROPERTY);
} else {
return js_object->SetLocalPropertyIgnoreAttributes(*name, *value, attr);
}
......@@ -10316,9 +10240,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SwapElements) {
RETURN_IF_EMPTY_HANDLE(isolate, tmp2);
RETURN_IF_EMPTY_HANDLE(
isolate, JSObject::SetElement(jsobject, index1, tmp2, kStrictMode));
isolate, JSObject::SetElement(jsobject, index1, tmp2, NONE, kStrictMode));
RETURN_IF_EMPTY_HANDLE(
isolate, JSObject::SetElement(jsobject, index2, tmp1, kStrictMode));
isolate, JSObject::SetElement(jsobject, index2, tmp1, NONE, kStrictMode));
return isolate->heap()->undefined_value();
}
......
......@@ -12331,18 +12331,21 @@ THREADED_TEST(PixelArray) {
i::Handle<i::Smi> value(i::Smi::FromInt(2));
i::Handle<i::Object> no_failure;
no_failure = i::JSObject::SetElement(jsobj, 1, value, i::kNonStrictMode);
no_failure =
i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode);
ASSERT(!no_failure.is_null());
i::USE(no_failure);
CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
*value.location() = i::Smi::FromInt(256);
no_failure = i::JSObject::SetElement(jsobj, 1, value, i::kNonStrictMode);
no_failure =
i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode);
ASSERT(!no_failure.is_null());
i::USE(no_failure);
CHECK_EQ(255,
i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
*value.location() = i::Smi::FromInt(-1);
no_failure = i::JSObject::SetElement(jsobj, 1, value, i::kNonStrictMode);
no_failure =
i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode);
ASSERT(!no_failure.is_null());
i::USE(no_failure);
CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
......
......@@ -676,7 +676,7 @@ TEST(JSArray) {
CHECK(array->HasFastTypeElements());
// array[length] = name.
array->SetElement(0, *name, kNonStrictMode, true)->ToObjectChecked();
array->SetElement(0, *name, NONE, kNonStrictMode)->ToObjectChecked();
CHECK_EQ(Smi::FromInt(1), array->length());
CHECK_EQ(array->GetElement(0), *name);
......@@ -691,7 +691,7 @@ TEST(JSArray) {
CHECK(array->HasDictionaryElements()); // Must be in slow mode.
// array[length] = name.
array->SetElement(int_length, *name, kNonStrictMode, true)->ToObjectChecked();
array->SetElement(int_length, *name, NONE, kNonStrictMode)->ToObjectChecked();
uint32_t new_int_length = 0;
CHECK(array->length()->ToArrayIndex(&new_int_length));
CHECK_EQ(static_cast<double>(int_length), new_int_length - 1);
......@@ -718,8 +718,8 @@ TEST(JSObjectCopy) {
obj->SetProperty(
*second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked();
obj->SetElement(0, *first, kNonStrictMode, true)->ToObjectChecked();
obj->SetElement(1, *second, kNonStrictMode, true)->ToObjectChecked();
obj->SetElement(0, *first, NONE, kNonStrictMode)->ToObjectChecked();
obj->SetElement(1, *second, NONE, kNonStrictMode)->ToObjectChecked();
// Make the clone.
Handle<JSObject> clone = Copy(obj);
......@@ -737,8 +737,8 @@ TEST(JSObjectCopy) {
clone->SetProperty(
*second, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
clone->SetElement(0, *second, kNonStrictMode, true)->ToObjectChecked();
clone->SetElement(1, *first, kNonStrictMode, true)->ToObjectChecked();
clone->SetElement(0, *second, NONE, kNonStrictMode)->ToObjectChecked();
clone->SetElement(1, *first, NONE, kNonStrictMode)->ToObjectChecked();
CHECK_EQ(obj->GetElement(1), clone->GetElement(0));
CHECK_EQ(obj->GetElement(0), clone->GetElement(1));
......
......@@ -49,7 +49,6 @@ S10.4.2.1_A1: FAIL
15.2.3.6-4-294-1: FAIL
15.2.3.6-4-295-1: FAIL
15.2.3.6-4-296-1: FAIL
15.2.3.6-4-333-11: FAIL
15.2.3.7-6-a-281: FAIL
15.2.3.7-6-a-282: FAIL
15.2.3.7-6-a-283: FAIL
......
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