Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 3ecbf22e4ded3636444716ddf76332023bc2e122..c435917834f9825996ef170f7c0b0a1a3bba7da5 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -2761,12 +2761,14 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( |
MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( |
- JSReceiver* receiver, |
+ JSReceiver* receiver_raw, |
uint32_t index) { |
Isolate* isolate = GetIsolate(); |
HandleScope scope(isolate); |
+ Handle<JSProxy> proxy(this); |
+ Handle<JSReceiver> receiver(receiver_raw); |
Handle<String> name = isolate->factory()->Uint32ToString(index); |
- return GetPropertyAttributeWithHandler(receiver, *name); |
+ return proxy->GetPropertyAttributeWithHandler(*receiver, *name); |
} |
@@ -2900,8 +2902,21 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, |
} |
Handle<Object> old_value(heap->the_hole_value()); |
+ PropertyAttributes old_attributes; |
if (FLAG_harmony_observation && map()->is_observed()) { |
- // TODO(observe): save oldValue |
+ switch (lookup->type()) { |
+ case NORMAL: |
+ case FIELD: |
+ case CONSTANT_FUNCTION: |
+ case INTERCEPTOR: |
+ old_value = |
+ Object::GetProperty(self, self, lookup, name, &old_attributes); |
+ case CALLBACKS: |
+ case TRANSITION: |
+ case HANDLER: |
+ case NONEXISTENT: |
+ break; |
+ } |
} |
// This is a real property that is not read-only, or it is a |
@@ -2981,7 +2996,18 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, |
if (!result->ToHandle(&hresult)) return result; |
if (FLAG_harmony_observation && map()->is_observed()) { |
- this->EnqueueChangeRecord("updated", name, old_value); |
+ PropertyAttributes new_attributes = self->GetLocalPropertyAttribute(*name); |
+ if (lookup->IsTransition()) { |
+ self->EnqueueChangeRecord("new", name, old_value); |
+ } else if (new_attributes != old_attributes) { |
+ self->EnqueueChangeRecord("reconfigured", name, old_value); |
+ } else { |
+ PropertyAttributes attributes; |
+ Handle<Object> new_value = |
+ Object::GetProperty(self, self, lookup, name, &attributes); |
+ if (!new_value->SameValue(*old_value)) |
+ self->EnqueueChangeRecord("updated", name, old_value); |
+ } |
} |
return *hresult; |
@@ -3010,22 +3036,22 @@ Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( |
MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
- String* name, |
- Object* value, |
+ String* name_raw, |
+ Object* value_raw, |
PropertyAttributes attributes) { |
// Make sure that the top context does not change when doing callbacks or |
// interceptor calls. |
AssertNoContextChange ncc; |
Isolate* isolate = GetIsolate(); |
LookupResult lookup(isolate); |
- LocalLookup(name, &lookup); |
- if (!lookup.IsFound()) map()->LookupTransition(this, name, &lookup); |
+ LocalLookup(name_raw, &lookup); |
+ if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup); |
// Check access rights if needed. |
if (IsAccessCheckNeeded()) { |
- if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { |
+ if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { |
return SetPropertyWithFailedAccessCheck(&lookup, |
- name, |
- value, |
+ name_raw, |
+ value_raw, |
false, |
kNonStrictMode); |
} |
@@ -3033,47 +3059,67 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
if (IsJSGlobalProxy()) { |
Object* proto = GetPrototype(); |
- if (proto->IsNull()) return value; |
+ if (proto->IsNull()) return value_raw; |
ASSERT(proto->IsJSGlobalObject()); |
return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes( |
- name, |
- value, |
+ name_raw, |
+ value_raw, |
attributes); |
} |
// Check for accessor in prototype chain removed here in clone. |
if (!lookup.IsFound()) { |
// Neither properties nor transitions found. |
- return AddProperty(name, value, attributes, kNonStrictMode); |
+ return AddProperty(name_raw, value_raw, attributes, kNonStrictMode); |
} |
+ // From this point on everything needs to be handlified. |
+ HandleScope scope(GetIsolate()); |
+ Handle<JSObject> self(this); |
+ Handle<String> name(name_raw); |
+ Handle<Object> value(value_raw); |
+ |
Handle<Object> old_value(isolate->heap()->the_hole_value()); |
+ PropertyAttributes old_attributes; |
if (FLAG_harmony_observation && map()->is_observed()) { |
- // TODO(observe): save oldValue |
+ switch (lookup.type()) { |
+ case NORMAL: |
+ case FIELD: |
+ case CONSTANT_FUNCTION: |
+ case INTERCEPTOR: |
+ old_value = |
+ Object::GetProperty(self, self, &lookup, name, &old_attributes); |
+ case CALLBACKS: |
+ case TRANSITION: |
+ case HANDLER: |
+ case NONEXISTENT: |
+ break; |
+ } |
} |
// Check of IsReadOnly removed from here in clone. |
- MaybeObject* result = value; |
+ MaybeObject* result = *value; |
switch (lookup.type()) { |
case NORMAL: { |
PropertyDetails details = PropertyDetails(attributes, NORMAL); |
- result = SetNormalizedProperty(name, value, details); |
+ result = self->SetNormalizedProperty(*name, *value, details); |
break; |
} |
case FIELD: |
- result = FastPropertyAtPut(lookup.GetFieldIndex(), value); |
+ result = self->FastPropertyAtPut(lookup.GetFieldIndex(), *value); |
break; |
case CONSTANT_FUNCTION: |
// Only replace the function if necessary. |
- if (value == lookup.GetConstantFunction()) return value; |
- // Preserve the attributes of this existing property. |
- attributes = lookup.GetAttributes(); |
- result = ConvertDescriptorToField(name, value, attributes); |
+ if (*value != lookup.GetConstantFunction()) { |
+ // Preserve the attributes of this existing property. |
+ attributes = lookup.GetAttributes(); |
+ result = self->ConvertDescriptorToField(*name, *value, attributes); |
+ } |
break; |
case CALLBACKS: |
case INTERCEPTOR: |
// Override callback in clone |
- result = ConvertDescriptorToField(name, value, attributes); |
+ result = self->ConvertDescriptorToField(*name, *value, attributes); |
break; |
case TRANSITION: { |
Map* transition_map = lookup.GetTransitionTarget(); |
@@ -3085,22 +3131,20 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
if (details.type() == FIELD) { |
if (attributes == details.attributes()) { |
int field_index = descriptors->GetFieldIndex(descriptor); |
- result = AddFastPropertyUsingMap(transition_map, |
- name, |
- value, |
- field_index); |
+ result = self->AddFastPropertyUsingMap( |
+ transition_map, *name, *value, field_index); |
} else { |
- result = ConvertDescriptorToField(name, value, attributes); |
+ result = self->ConvertDescriptorToField(*name, *value, attributes); |
} |
} else if (details.type() == CALLBACKS) { |
- result = ConvertDescriptorToField(name, value, attributes); |
+ result = self->ConvertDescriptorToField(*name, *value, attributes); |
} else { |
ASSERT(details.type() == CONSTANT_FUNCTION); |
// Replace transition to CONSTANT FUNCTION with a map transition to a |
// new map with a FIELD, even if the value is a function. |
- result = ConvertTransitionToMapTransition( |
- lookup.GetTransitionIndex(), name, value, attributes); |
+ result = self->ConvertTransitionToMapTransition( |
+ lookup.GetTransitionIndex(), *name, *value, attributes); |
} |
break; |
} |
@@ -3113,9 +3157,17 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
if (!result->ToHandle(&hresult)) return result; |
if (FLAG_harmony_observation && map()->is_observed()) { |
- const char* type = |
- attributes == lookup.GetAttributes() ? "updated" : "reconfigured"; |
- this->EnqueueChangeRecord(type, handle(name), old_value); |
+ PropertyAttributes new_attributes = self->GetLocalPropertyAttribute(*name); |
+ if (lookup.IsTransition()) { |
+ self->EnqueueChangeRecord("new", name, old_value); |
+ } else if (new_attributes != old_attributes) { |
+ self->EnqueueChangeRecord("reconfigured", name, old_value); |
+ } else { |
+ Handle<Object> new_value = |
+ Object::GetProperty(self, self, &lookup, name, &old_attributes); |
+ if (!new_value->SameValue(*old_value)) |
+ self->EnqueueChangeRecord("updated", name, old_value); |
+ } |
} |
return *hresult; |
@@ -3199,42 +3251,43 @@ PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver( |
String* key) { |
uint32_t index = 0; |
if (IsJSObject() && key->AsArrayIndex(&index)) { |
- return JSObject::cast(this)->HasElementWithReceiver(receiver, index) |
- ? NONE : ABSENT; |
+ return JSObject::cast(this)->GetElementAttributeWithReceiver( |
+ receiver, index, true); |
} |
// Named property. |
- LookupResult result(GetIsolate()); |
- Lookup(key, &result); |
- return GetPropertyAttribute(receiver, &result, key, true); |
+ LookupResult lookup(GetIsolate()); |
+ Lookup(key, &lookup); |
+ return GetPropertyAttributeForResult(receiver, &lookup, key, true); |
} |
-PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver, |
- LookupResult* result, |
- String* name, |
- bool continue_search) { |
+PropertyAttributes JSReceiver::GetPropertyAttributeForResult( |
+ JSReceiver* receiver, |
+ LookupResult* lookup, |
+ String* name, |
+ bool continue_search) { |
// Check access rights if needed. |
if (IsAccessCheckNeeded()) { |
JSObject* this_obj = JSObject::cast(this); |
Heap* heap = GetHeap(); |
if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) { |
return this_obj->GetPropertyAttributeWithFailedAccessCheck( |
- receiver, result, name, continue_search); |
+ receiver, lookup, name, continue_search); |
} |
} |
- if (result->IsFound()) { |
- switch (result->type()) { |
+ if (lookup->IsFound()) { |
+ switch (lookup->type()) { |
case NORMAL: // fall through |
case FIELD: |
case CONSTANT_FUNCTION: |
case CALLBACKS: |
- return result->GetAttributes(); |
+ return lookup->GetAttributes(); |
case HANDLER: { |
- return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler( |
+ return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler( |
receiver, name); |
} |
case INTERCEPTOR: |
- return result->holder()->GetPropertyAttributeWithInterceptor( |
+ return lookup->holder()->GetPropertyAttributeWithInterceptor( |
JSObject::cast(receiver), name, continue_search); |
case TRANSITION: |
case NONEXISTENT: |
@@ -3249,13 +3302,113 @@ PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) { |
// Check whether the name is an array index. |
uint32_t index = 0; |
if (IsJSObject() && name->AsArrayIndex(&index)) { |
- if (JSObject::cast(this)->HasLocalElement(index)) return NONE; |
- return ABSENT; |
+ return GetLocalElementAttribute(index); |
} |
// Named property. |
- LookupResult result(GetIsolate()); |
- LocalLookup(name, &result); |
- return GetPropertyAttribute(this, &result, name, false); |
+ LookupResult lookup(GetIsolate()); |
+ LocalLookup(name, &lookup); |
+ return GetPropertyAttributeForResult(this, &lookup, name, false); |
+} |
+ |
+ |
+PropertyAttributes JSObject::GetElementAttributeWithReceiver( |
+ JSReceiver* receiver, uint32_t index, bool continue_search) { |
+ Isolate* isolate = GetIsolate(); |
+ |
+ // Check access rights if needed. |
+ if (IsAccessCheckNeeded()) { |
+ if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { |
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); |
+ return ABSENT; |
+ } |
+ } |
+ |
+ if (IsJSGlobalProxy()) { |
+ Object* proto = GetPrototype(); |
+ if (proto->IsNull()) return ABSENT; |
+ ASSERT(proto->IsJSGlobalObject()); |
+ return JSReceiver::cast(proto)->GetElementAttributeWithReceiver( |
+ receiver, index, continue_search); |
+ } |
+ |
+ // Check for lookup interceptor except when bootstrapping. |
+ if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) { |
+ return GetElementAttributeWithInterceptor(receiver, index, continue_search); |
+ } |
+ |
+ return GetElementAttributeWithoutInterceptor( |
+ receiver, index, continue_search); |
+} |
+ |
+ |
+PropertyAttributes JSObject::GetElementAttributeWithInterceptor( |
+ JSReceiver* receiver, uint32_t index, bool continue_search) { |
+ Isolate* isolate = GetIsolate(); |
+ // Make sure that the top context does not change when doing |
+ // callbacks or interceptor calls. |
+ AssertNoContextChange ncc; |
+ HandleScope scope(isolate); |
+ Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); |
+ Handle<JSReceiver> hreceiver(receiver); |
+ Handle<JSObject> holder(this); |
+ CustomArguments args(isolate, interceptor->data(), receiver, this); |
+ v8::AccessorInfo info(args.end()); |
+ if (!interceptor->query()->IsUndefined()) { |
+ v8::IndexedPropertyQuery query = |
+ v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); |
+ LOG(isolate, |
+ ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); |
+ v8::Handle<v8::Integer> result; |
+ { |
+ // Leaving JavaScript. |
+ VMState state(isolate, EXTERNAL); |
+ result = query(index, info); |
+ } |
+ if (!result.IsEmpty()) |
+ return static_cast<PropertyAttributes>(result->Int32Value()); |
+ } else if (!interceptor->getter()->IsUndefined()) { |
+ v8::IndexedPropertyGetter getter = |
+ v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); |
+ LOG(isolate, |
+ ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index)); |
+ v8::Handle<v8::Value> result; |
+ { |
+ // Leaving JavaScript. |
+ VMState state(isolate, EXTERNAL); |
+ result = getter(index, info); |
+ } |
+ if (!result.IsEmpty()) return DONT_ENUM; |
+ } |
+ |
+ return holder->GetElementAttributeWithoutInterceptor( |
+ *hreceiver, index, continue_search); |
+} |
+ |
+ |
+PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor( |
+ JSReceiver* receiver, uint32_t index, bool continue_search) { |
+ Isolate* isolate = GetIsolate(); |
+ HandleScope scope(isolate); |
+ Handle<JSReceiver> hreceiver(receiver); |
+ Handle<JSObject> holder(this); |
+ PropertyAttributes attr = holder->GetElementsAccessor()->GetAttributes( |
+ *hreceiver, *holder, index); |
+ if (attr != ABSENT) return attr; |
+ |
+ if (holder->IsStringObjectWithCharacterAt(index)) { |
+ return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); |
+ } |
+ |
+ if (!continue_search) return ABSENT; |
+ |
+ Object* pt = holder->GetPrototype(); |
+ if (pt->IsJSProxy()) { |
+ // We need to follow the spec and simulate a call to [[GetOwnProperty]]. |
+ return JSProxy::cast(pt)->GetElementAttributeWithHandler(*hreceiver, index); |
+ } |
+ if (pt->IsNull()) return ABSENT; |
+ return JSObject::cast(pt)->GetElementAttributeWithReceiver( |
+ *hreceiver, index, true); |
} |
@@ -3980,15 +4133,39 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { |
return JSGlobalObject::cast(proto)->DeleteElement(index, mode); |
} |
- if (HasIndexedInterceptor()) { |
- // Skip interceptor if forcing deletion. |
- if (mode != FORCE_DELETION) { |
- return DeleteElementWithInterceptor(index); |
+ // From this point on everything needs to be handlified. |
+ HandleScope scope(isolate); |
+ Handle<JSObject> self(this); |
+ |
+ Handle<String> name; |
+ Handle<Object> old_value(isolate->heap()->the_hole_value()); |
+ bool preexists = false; |
+ if (FLAG_harmony_observation && map()->is_observed()) { |
+ name = isolate->factory()->Uint32ToString(index); |
+ preexists = self->HasLocalElement(index); |
+ if (preexists) { |
+ // TODO(observe): only read & set old_value if it's not an accessor |
+ old_value = Object::GetElement(self, index); |
} |
- mode = JSReceiver::FORCE_DELETION; |
} |
- return GetElementsAccessor()->Delete(this, index, mode); |
+ MaybeObject* result; |
+ // Skip interceptor if forcing deletion. |
+ if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) { |
+ result = self->DeleteElementWithInterceptor(index); |
+ } else { |
+ result = self->GetElementsAccessor()->Delete(*self, index, mode); |
+ } |
+ |
+ Handle<Object> hresult; |
+ if (!result->ToHandle(&hresult)) return result; |
+ |
+ if (FLAG_harmony_observation && map()->is_observed()) { |
+ if (preexists && !self->HasLocalElement(index)) |
+ self->EnqueueChangeRecord("deleted", name, old_value); |
+ } |
+ |
+ return *hresult; |
} |
@@ -4039,10 +4216,28 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { |
return isolate->heap()->false_value(); |
} |
+ // From this point on everything needs to be handlified. |
HandleScope scope(isolate); |
+ Handle<JSObject> self(this); |
+ Handle<String> hname(name); |
+ |
Handle<Object> old_value(isolate->heap()->the_hole_value()); |
if (FLAG_harmony_observation && map()->is_observed()) { |
- // TODO(observe): save oldValue |
+ switch (lookup.type()) { |
+ case NORMAL: |
+ case FIELD: |
+ case CONSTANT_FUNCTION: |
+ case INTERCEPTOR: { |
+ PropertyAttributes old_attributes; |
+ old_value = |
+ Object::GetProperty(self, self, &lookup, hname, &old_attributes); |
+ } |
+ case CALLBACKS: |
+ case TRANSITION: |
+ case HANDLER: |
+ case NONEXISTENT: |
+ break; |
+ } |
} |
MaybeObject* result; |
@@ -4050,24 +4245,25 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { |
if (lookup.IsInterceptor()) { |
// Skip interceptor if forcing a deletion. |
if (mode == FORCE_DELETION) { |
- result = DeletePropertyPostInterceptor(name, mode); |
+ result = self->DeletePropertyPostInterceptor(*hname, mode); |
} else { |
- result = DeletePropertyWithInterceptor(name); |
+ result = self->DeletePropertyWithInterceptor(*hname); |
} |
} else { |
// Normalize object if needed. |
Object* obj; |
- result = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); |
- if (!result->ToObject(&obj)) return result; |
+ result = self->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); |
+ if (!result->To(&obj)) return result; |
// Make sure the properties are normalized before removing the entry. |
- result = DeleteNormalizedProperty(name, mode); |
+ result = self->DeleteNormalizedProperty(*hname, mode); |
} |
Handle<Object> hresult; |
if (!result->ToHandle(&hresult)) return result; |
if (FLAG_harmony_observation && map()->is_observed()) { |
- this->EnqueueChangeRecord("deleted", handle(name), old_value); |
+ if (!self->HasLocalProperty(*hname)) |
+ self->EnqueueChangeRecord("deleted", hname, old_value); |
} |
return *hresult; |
@@ -4669,14 +4865,14 @@ void JSObject::DefineAccessor(Handle<JSObject> object, |
object->DefineAccessor(*name, *getter, *setter, attributes)); |
} |
-MaybeObject* JSObject::DefineAccessor(String* name, |
- Object* getter, |
- Object* setter, |
+MaybeObject* JSObject::DefineAccessor(String* name_raw, |
+ Object* getter_raw, |
+ Object* setter_raw, |
PropertyAttributes attributes) { |
Isolate* isolate = GetIsolate(); |
// Check access rights if needed. |
if (IsAccessCheckNeeded() && |
- !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { |
+ !isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { |
isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); |
return isolate->heap()->undefined_value(); |
} |
@@ -4686,7 +4882,7 @@ MaybeObject* JSObject::DefineAccessor(String* name, |
if (proto->IsNull()) return this; |
ASSERT(proto->IsJSGlobalObject()); |
return JSObject::cast(proto)->DefineAccessor( |
- name, getter, setter, attributes); |
+ name_raw, getter_raw, setter_raw, attributes); |
} |
// Make sure that the top context does not change when doing callbacks or |
@@ -4694,30 +4890,63 @@ MaybeObject* JSObject::DefineAccessor(String* name, |
AssertNoContextChange ncc; |
// Try to flatten before operating on the string. |
- name->TryFlatten(); |
+ name_raw->TryFlatten(); |
- if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); |
+ if (!CanSetCallback(name_raw)) return isolate->heap()->undefined_value(); |
+ |
+ // From this point on everything needs to be handlified. |
+ HandleScope scope(GetIsolate()); |
+ Handle<JSObject> self(this); |
+ Handle<String> name(name_raw); |
+ Handle<Object> getter(getter_raw); |
+ Handle<Object> setter(setter_raw); |
+ |
+ uint32_t index = 0; |
+ bool is_element = name->AsArrayIndex(&index); |
Handle<Object> old_value(isolate->heap()->the_hole_value()); |
bool preexists = false; |
if (FLAG_harmony_observation && map()->is_observed()) { |
- LookupResult result(isolate); |
- LocalLookup(name, &result); |
- preexists = result.IsFound(); |
- // TODO(observe): save oldValue |
+ if (is_element) { |
+ preexists = HasLocalElement(index); |
+ if (preexists) { |
+ // TODO(observe): distinguish the case where it's an accessor |
+ old_value = Object::GetElement(self, index); |
+ } |
+ } else { |
+ LookupResult lookup(isolate); |
+ LocalLookup(*name, &lookup); |
+ preexists = lookup.IsProperty(); |
+ if (preexists) { |
+ switch (lookup.type()) { |
+ case NORMAL: |
+ case FIELD: |
+ case CONSTANT_FUNCTION: |
+ case INTERCEPTOR: { |
+ PropertyAttributes old_attributes; |
+ old_value = |
+ Object::GetProperty(self, self, &lookup, name, &old_attributes); |
+ } |
+ case CALLBACKS: |
+ case TRANSITION: |
+ case HANDLER: |
+ case NONEXISTENT: |
+ break; |
+ } |
+ } |
+ } |
} |
- uint32_t index = 0; |
- MaybeObject* result = name->AsArrayIndex(&index) |
- ? DefineElementAccessor(index, getter, setter, attributes) |
- : DefinePropertyAccessor(name, getter, setter, attributes); |
+ MaybeObject* result = is_element ? |
+ self->DefineElementAccessor(index, *getter, *setter, attributes) : |
+ self->DefinePropertyAccessor(*name, *getter, *setter, attributes); |
Handle<Object> hresult; |
if (!result->ToHandle(&hresult)) return result; |
if (FLAG_harmony_observation && map()->is_observed()) { |
const char* type = preexists ? "reconfigured" : "new"; |
- this->EnqueueChangeRecord(type, handle(name), old_value); |
+ self->EnqueueChangeRecord(type, name, old_value); |
} |
return *hresult; |
@@ -9212,64 +9441,7 @@ MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, |
} |
-bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) { |
- Isolate* isolate = GetIsolate(); |
- // Make sure that the top context does not change when doing |
- // callbacks or interceptor calls. |
- AssertNoContextChange ncc; |
- HandleScope scope(isolate); |
- Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); |
- Handle<JSReceiver> receiver_handle(receiver); |
- Handle<JSObject> holder_handle(this); |
- CustomArguments args(isolate, interceptor->data(), receiver, this); |
- v8::AccessorInfo info(args.end()); |
- if (!interceptor->query()->IsUndefined()) { |
- v8::IndexedPropertyQuery query = |
- v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); |
- LOG(isolate, |
- ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); |
- v8::Handle<v8::Integer> result; |
- { |
- // Leaving JavaScript. |
- VMState state(isolate, EXTERNAL); |
- result = query(index, info); |
- } |
- if (!result.IsEmpty()) { |
- ASSERT(result->IsInt32()); |
- return true; // absence of property is signaled by empty handle. |
- } |
- } else if (!interceptor->getter()->IsUndefined()) { |
- v8::IndexedPropertyGetter getter = |
- v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); |
- LOG(isolate, |
- ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index)); |
- v8::Handle<v8::Value> result; |
- { |
- // Leaving JavaScript. |
- VMState state(isolate, EXTERNAL); |
- result = getter(index, info); |
- } |
- if (!result.IsEmpty()) return true; |
- } |
- |
- if (holder_handle->GetElementsAccessor()->HasElement( |
- *receiver_handle, *holder_handle, index)) { |
- return true; |
- } |
- |
- if (holder_handle->IsStringObjectWithCharacterAt(index)) return true; |
- Object* pt = holder_handle->GetPrototype(); |
- if (pt->IsJSProxy()) { |
- // We need to follow the spec and simulate a call to [[GetOwnProperty]]. |
- return JSProxy::cast(pt)->GetElementAttributeWithHandler( |
- receiver, index) != ABSENT; |
- } |
- if (pt->IsNull()) return false; |
- return JSObject::cast(pt)->HasElementWithReceiver(*receiver_handle, index); |
-} |
- |
- |
-JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { |
+JSObject::LocalElementType JSObject::GetLocalElementType(uint32_t index) { |
// Check access rights if needed. |
if (IsAccessCheckNeeded()) { |
Heap* heap = GetHeap(); |
@@ -9283,13 +9455,13 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { |
Object* proto = GetPrototype(); |
if (proto->IsNull()) return UNDEFINED_ELEMENT; |
ASSERT(proto->IsJSGlobalObject()); |
- return JSObject::cast(proto)->HasLocalElement(index); |
+ return JSObject::cast(proto)->GetLocalElementType(index); |
} |
// Check for lookup interceptor |
if (HasIndexedInterceptor()) { |
- return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT |
- : UNDEFINED_ELEMENT; |
+ return GetElementAttributeWithInterceptor(this, index, false) != ABSENT |
+ ? INTERCEPTED_ELEMENT : UNDEFINED_ELEMENT; |
} |
// Handle [] on String objects. |
@@ -9378,40 +9550,6 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { |
} |
-bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { |
- // Check access rights if needed. |
- if (IsAccessCheckNeeded()) { |
- Heap* heap = GetHeap(); |
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { |
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); |
- return false; |
- } |
- } |
- |
- // Check for lookup interceptor |
- if (HasIndexedInterceptor()) { |
- return HasElementWithInterceptor(receiver, index); |
- } |
- |
- ElementsAccessor* accessor = GetElementsAccessor(); |
- if (accessor->HasElement(receiver, this, index)) { |
- return true; |
- } |
- |
- // Handle [] on String objects. |
- if (this->IsStringObjectWithCharacterAt(index)) return true; |
- |
- Object* pt = GetPrototype(); |
- if (pt->IsNull()) return false; |
- if (pt->IsJSProxy()) { |
- // We need to follow the spec and simulate a call to [[GetOwnProperty]]. |
- return JSProxy::cast(pt)->GetElementAttributeWithHandler( |
- receiver, index) != ABSENT; |
- } |
- return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); |
-} |
- |
- |
MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index, |
Object* value, |
PropertyAttributes attributes, |
@@ -10009,28 +10147,31 @@ Handle<Object> JSObject::SetElement(Handle<JSObject> object, |
MaybeObject* JSObject::SetElement(uint32_t index, |
- Object* value, |
+ Object* value_raw, |
PropertyAttributes attributes, |
StrictModeFlag strict_mode, |
bool check_prototype, |
SetPropertyMode set_mode) { |
+ Isolate* isolate = GetIsolate(); |
+ HandleScope scope(isolate); |
+ Handle<JSObject> self(this); |
+ Handle<Object> value(value_raw); |
+ |
// Check access rights if needed. |
if (IsAccessCheckNeeded()) { |
Heap* heap = GetHeap(); |
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) { |
- HandleScope scope(heap->isolate()); |
- Handle<Object> value_handle(value); |
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET); |
- return *value_handle; |
+ if (!heap->isolate()->MayIndexedAccess(*self, index, v8::ACCESS_SET)) { |
+ heap->isolate()->ReportFailedAccessCheck(*self, v8::ACCESS_SET); |
+ return *value; |
} |
} |
if (IsJSGlobalProxy()) { |
Object* proto = GetPrototype(); |
- if (proto->IsNull()) return value; |
+ if (proto->IsNull()) return *value; |
ASSERT(proto->IsJSGlobalObject()); |
return JSObject::cast(proto)->SetElement(index, |
- value, |
+ *value, |
attributes, |
strict_mode, |
check_prototype, |
@@ -10039,10 +10180,8 @@ MaybeObject* JSObject::SetElement(uint32_t index, |
// Don't allow element properties to be redefined for external arrays. |
if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) { |
- Isolate* isolate = GetHeap()->isolate(); |
- Handle<Object> receiver(this); |
Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
- Handle<Object> args[] = { receiver, number }; |
+ Handle<Object> args[] = { self, number }; |
Handle<Object> error = isolate->factory()->NewTypeError( |
"redef_external_array_element", HandleVector(args, ARRAY_SIZE(args))); |
return isolate->Throw(*error); |
@@ -10057,22 +10196,45 @@ MaybeObject* JSObject::SetElement(uint32_t index, |
dictionary->set_requires_slow_elements(); |
} |
+ // From here on, everything has to be handlified. |
+ Handle<String> name; |
+ Handle<Object> old_value(isolate->heap()->the_hole_value()); |
+ PropertyAttributes old_attributes; |
+ bool preexists = false; |
+ if (FLAG_harmony_observation && map()->is_observed()) { |
+ name = isolate->factory()->Uint32ToString(index); |
+ preexists = self->HasLocalElement(index); |
+ if (preexists) { |
+ old_attributes = self->GetLocalPropertyAttribute(*name); |
+ // TODO(observe): only read & set old_value if we have a data property |
+ old_value = Object::GetElement(self, index); |
+ } |
+ } |
+ |
// Check for lookup interceptor |
- if (HasIndexedInterceptor()) { |
- return SetElementWithInterceptor(index, |
- value, |
- attributes, |
- strict_mode, |
- check_prototype, |
- set_mode); |
+ MaybeObject* result = self->HasIndexedInterceptor() |
+ ? self->SetElementWithInterceptor( |
+ index, *value, attributes, strict_mode, check_prototype, set_mode) |
+ : self->SetElementWithoutInterceptor( |
+ index, *value, attributes, strict_mode, check_prototype, set_mode); |
+ |
+ Handle<Object> hresult; |
+ if (!result->ToHandle(&hresult)) return result; |
+ |
+ if (FLAG_harmony_observation && map()->is_observed()) { |
+ PropertyAttributes new_attributes = self->GetLocalPropertyAttribute(*name); |
+ if (!preexists) { |
+ self->EnqueueChangeRecord("new", name, old_value); |
+ } else if (new_attributes != old_attributes || old_value->IsTheHole()) { |
+ self->EnqueueChangeRecord("reconfigured", name, old_value); |
+ } else { |
+ Handle<Object> newValue = Object::GetElement(self, index); |
+ if (!newValue->SameValue(*old_value)) |
+ self->EnqueueChangeRecord("updated", name, old_value); |
+ } |
} |
- return SetElementWithoutInterceptor(index, |
- value, |
- attributes, |
- strict_mode, |
- check_prototype, |
- set_mode); |
+ return *hresult; |
} |