Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(464)

Unified Diff: src/objects.cc

Issue 11365111: Object.observe: generate change records for indexed properties. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressing comments; simplifications. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
« no previous file with comments | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698