Revision: 12900
Author: [email protected]
Date: Thu Nov 8 04:58:08 2012
Log: Object.observe: generate change records for indexed properties.
Details:
- Extend ElementAccessors with GetAttributes method.
- Add HasLocalElement, Get[Local]ElementAttribute methods to
JSReceiver/JSObject.
- Otherwise, mirror implementation for named properties.
Cannot correctly handle the cases yet where an accessor is redefined or
deleted.
Also fixed handling of object info table.
(Based on CL https://codereview.chromium.org/11362115/)
[email protected],[email protected]
BUG=
Review URL: https://codereview.chromium.org/11365111
http://code.google.com/p/v8/source/detail?r=12900
Modified:
/branches/bleeding_edge/src/elements.cc
/branches/bleeding_edge/src/elements.h
/branches/bleeding_edge/src/object-observe.js
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/test/mjsunit/harmony/object-observe.js
=======================================
--- /branches/bleeding_edge/src/elements.cc Fri Nov 2 03:24:56 2012
+++ /branches/bleeding_edge/src/elements.cc Thu Nov 8 04:58:08 2012
@@ -563,6 +563,29 @@
? backing_store->get(key)
: backing_store->GetHeap()->the_hole_value();
}
+
+ MUST_USE_RESULT virtual PropertyAttributes GetAttributes(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (backing_store == NULL) {
+ backing_store = holder->elements();
+ }
+ return ElementsAccessorSubclass::GetAttributesImpl(
+ receiver, holder, key, BackingStore::cast(backing_store));
+ }
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ BackingStore* backing_store) {
+ if (key >= ElementsAccessorSubclass::GetCapacityImpl(backing_store)) {
+ return ABSENT;
+ }
+ return backing_store->is_the_hole(key) ? ABSENT : NONE;
+ }
MUST_USE_RESULT virtual MaybeObject* SetLength(JSArray* array,
Object* length) {
@@ -1142,6 +1165,16 @@
? backing_store->get(key)
: backing_store->GetHeap()->undefined_value();
}
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ BackingStore* backing_store) {
+ return
+ key <
ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store)
+ ? NONE : ABSENT;
+ }
MUST_USE_RESULT static MaybeObject* SetLengthImpl(
JSObject* obj,
@@ -1430,6 +1463,18 @@
}
return obj->GetHeap()->the_hole_value();
}
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ SeededNumberDictionary* backing_store) {
+ int entry = backing_store->FindEntry(key);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ return backing_store->DetailsAt(entry).attributes();
+ }
+ return ABSENT;
+ }
static bool HasElementImpl(Object* receiver,
JSObject* holder,
@@ -1489,6 +1534,22 @@
}
}
}
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArray* parameter_map) {
+ Object* probe = GetParameterMapArg(obj, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ return NONE;
+ } else {
+ // If not aliased, check the arguments.
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ return ElementsAccessor::ForArray(arguments)->GetAttributes(
+ receiver, obj, key, arguments);
+ }
+ }
MUST_USE_RESULT static MaybeObject* SetLengthImpl(
JSObject* obj,
=======================================
--- /branches/bleeding_edge/src/elements.h Wed May 23 07:24:29 2012
+++ /branches/bleeding_edge/src/elements.h Thu Nov 8 04:58:08 2012
@@ -71,6 +71,17 @@
uint32_t key,
FixedArrayBase* backing_store = NULL) = 0;
+ // Returns an element's attributes, or ABSENT if there is no such
+ // element. This method doesn't iterate up the prototype chain. The
caller
+ // can optionally pass in the backing store to use for the check, which
must
+ // be compatible with the ElementsKind of the ElementsAccessor. If
+ // backing_store is NULL, the holder->elements() is used as the backing
store.
+ MUST_USE_RESULT virtual PropertyAttributes GetAttributes(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store = NULL) = 0;
+
// Modifies the length data property as specified for JSArrays and
resizes the
// underlying backing store accordingly. The method honors the semantics
of
// changing array sizes as defined in EcmaScript 5.1 15.4.5.2, i.e.
array that
=======================================
--- /branches/bleeding_edge/src/object-observe.js Tue Nov 6 08:47:15 2012
+++ /branches/bleeding_edge/src/object-observe.js Thu Nov 8 04:58:08 2012
@@ -45,7 +45,7 @@
return %ObjectHashTableGet(this.table, key);
},
set: function(key, value) {
- return %ObjectHashTableSet(this.table, key, value);
+ this.table = %ObjectHashTableSet(this.table, key, value);
},
has: function(key) {
return %ObjectHashTableHas(this.table, key);
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Thu Nov 8 04:14:29 2012
+++ /branches/bleeding_edge/src/objects-inl.h Thu Nov 8 04:58:08 2012
@@ -5051,6 +5051,11 @@
PropertyAttributes JSReceiver::GetPropertyAttribute(String* key) {
return GetPropertyAttributeWithReceiver(this, key);
}
+
+
+PropertyAttributes JSReceiver::GetElementAttribute(uint32_t index) {
+ return GetElementAttributeWithReceiver(this, index, true);
+}
// TODO(504): this may be useful in other places too where JSGlobalProxy
@@ -5077,7 +5082,34 @@
if (IsJSProxy()) {
return JSProxy::cast(this)->HasElementWithHandler(index);
}
- return JSObject::cast(this)->HasElementWithReceiver(this, index);
+ return JSObject::cast(this)->GetElementAttribute(index) != ABSENT;
+}
+
+
+bool JSReceiver::HasLocalElement(uint32_t index) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->HasElementWithHandler(index);
+ }
+ return JSObject::cast(this)->GetLocalElementAttribute(index) != ABSENT;
+}
+
+
+PropertyAttributes JSReceiver::GetElementAttributeWithReceiver(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->GetElementAttributeWithHandler(receiver,
index);
+ }
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ receiver, index, continue_search);
+}
+
+
+PropertyAttributes JSReceiver::GetLocalElementAttribute(uint32_t index) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->GetElementAttributeWithHandler(this,
index);
+ }
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ this, index, false);
}
=======================================
--- /branches/bleeding_edge/src/objects.cc Thu Nov 8 04:18:11 2012
+++ /branches/bleeding_edge/src/objects.cc Thu Nov 8 04:58:08 2012
@@ -3227,8 +3227,8 @@
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 lookup(GetIsolate());
@@ -3278,14 +3278,114 @@
// 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 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);
+}
MaybeObject* NormalizedMapCache::Get(JSObject* obj,
@@ -4009,15 +4109,39 @@
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;
+ }
+
+ 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 GetElementsAccessor()->Delete(this, index, mode);
+ return *hresult;
}
@@ -9410,64 +9534,7 @@
}
-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();
@@ -9481,13 +9548,13 @@
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.
@@ -9574,40 +9641,6 @@
return UNDEFINED_ELEMENT;
}
-
-
-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,
@@ -10207,28 +10240,31 @@
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,
@@ -10237,10 +10273,8 @@
// 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);
@@ -10254,23 +10288,46 @@
// Make sure that we never go back to fast case.
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;
}
=======================================
--- /branches/bleeding_edge/src/objects.h Thu Nov 8 04:18:11 2012
+++ /branches/bleeding_edge/src/objects.h Thu Nov 8 04:58:08 2012
@@ -1483,10 +1483,18 @@
String* name);
PropertyAttributes GetLocalPropertyAttribute(String* name);
+ inline PropertyAttributes GetElementAttribute(uint32_t index);
+ inline PropertyAttributes GetElementAttributeWithReceiver(
+ JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
+ inline PropertyAttributes GetLocalElementAttribute(uint32_t index);
+
// Can cause a GC.
inline bool HasProperty(String* name);
inline bool HasLocalProperty(String* name);
inline bool HasElement(uint32_t index);
+ inline bool HasLocalElement(uint32_t index);
// Return the object's prototype (might be Heap::null_value()).
inline Object* GetPrototype();
@@ -1701,6 +1709,9 @@
LookupResult* result,
String* name,
bool continue_search);
+ PropertyAttributes GetElementAttributeWithReceiver(JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
static void DefineAccessor(Handle<JSObject> object,
Handle<String> name,
@@ -1822,9 +1833,6 @@
// be represented as a double and not a Smi.
bool ShouldConvertToFastDoubleElements(bool* has_smi_only_elements);
- // Tells whether the index'th element is present.
- bool HasElementWithReceiver(JSReceiver* receiver, uint32_t index);
-
// Computes the new capacity when expanding the elements of a JSObject.
static int NewElementsCapacity(int old_capacity) {
// (old_capacity + 50%) + 16
@@ -1849,9 +1857,7 @@
DICTIONARY_ELEMENT
};
- LocalElementType HasLocalElement(uint32_t index);
-
- bool HasElementWithInterceptor(JSReceiver* receiver, uint32_t index);
+ LocalElementType GetLocalElementType(uint32_t index);
MUST_USE_RESULT MaybeObject* SetFastElement(uint32_t index,
Object* value,
@@ -2209,6 +2215,14 @@
Object* structure,
uint32_t index,
Object* holder);
+ MUST_USE_RESULT PropertyAttributes GetElementAttributeWithInterceptor(
+ JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
+ MUST_USE_RESULT PropertyAttributes GetElementAttributeWithoutInterceptor(
+ JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
MUST_USE_RESULT MaybeObject* SetElementWithCallback(
Object* structure,
uint32_t index,
=======================================
--- /branches/bleeding_edge/src/runtime.cc Tue Nov 6 10:14:45 2012
+++ /branches/bleeding_edge/src/runtime.cc Thu Nov 8 04:58:08 2012
@@ -1091,7 +1091,7 @@
// This could be an element.
uint32_t index;
if (name->AsArrayIndex(&index)) {
- switch (obj->HasLocalElement(index)) {
+ switch (obj->GetLocalElementType(index)) {
case JSObject::UNDEFINED_ELEMENT:
return heap->undefined_value();
@@ -4699,7 +4699,7 @@
uint32_t index;
if (key->AsArrayIndex(&index)) {
- JSObject::LocalElementType type = object->HasLocalElement(index);
+ JSObject::LocalElementType type = object->GetLocalElementType(index);
switch (type) {
case JSObject::UNDEFINED_ELEMENT:
case JSObject::STRING_CHARACTER_ELEMENT:
@@ -13291,8 +13291,7 @@
CONVERT_ARG_HANDLE_CHECKED(ObjectHashTable, table, 0);
Handle<Object> key = args.at<Object>(1);
Handle<Object> value = args.at<Object>(2);
- PutIntoObjectHashTable(table, key, value);
- return isolate->heap()->undefined_value();
+ return *PutIntoObjectHashTable(table, key, value);
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Wed Nov
7 06:14:50 2012
+++ /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Thu Nov
8 04:58:08 2012
@@ -227,19 +227,20 @@
Object.observe(obj3, observer.callback);
Object.observe(obj2, observer.callback);
Object.notify(obj, {
- type: 'foo',
+ type: 'foo1',
});
Object.notify(obj2, {
- type: 'foo',
+ type: 'foo2',
});
Object.notify(obj3, {
- type: 'foo',
+ type: 'foo3',
});
+Object.observe(obj3, observer.callback);
Object.deliverChangeRecords(observer.callback);
observer.assertCallbackRecords([
- { object: obj, type: 'foo' },
- { object: obj2, type: 'foo' },
- { object: obj3, type: 'foo' }
+ { object: obj, type: 'foo1' },
+ { object: obj2, type: 'foo2' },
+ { object: obj3, type: 'foo3' }
]);
// Observing named properties.
@@ -286,3 +287,48 @@
{ object: obj, name: "a", type: "deleted", oldValue: 10 },
{ object: obj, name: "a", type: "new" },
]);
+
+// Observing indexed properties.
+reset();
+var obj = {'1': 1}
+Object.observe(obj, observer.callback);
+obj[1] = 2;
+obj[1] = 3;
+delete obj[1];
+obj[1] = 4;
+obj[1] = 4; // ignored
+obj[1] = 5;
+Object.defineProperty(obj, "1", {value: 6});
+Object.defineProperty(obj, "1", {writable: false});
+obj[1] = 7; // ignored
+Object.defineProperty(obj, "1", {value: 8});
+Object.defineProperty(obj, "1", {value: 7, writable: true});
+Object.defineProperty(obj, "1", {get: function() {}});
+delete obj[1];
+delete obj[1];
+Object.defineProperty(obj, "1", {get: function() {}, configurable: true});
+Object.defineProperty(obj, "1", {value: 9, writable: true});
+obj[1] = 10;
+delete obj[1];
+Object.defineProperty(obj, "1", {value: 11, configurable: true});
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+ { object: obj, name: "1", type: "updated", oldValue: 1 },
+ { object: obj, name: "1", type: "updated", oldValue: 2 },
+ { object: obj, name: "1", type: "deleted", oldValue: 3 },
+ { object: obj, name: "1", type: "new" },
+ { object: obj, name: "1", type: "updated", oldValue: 4 },
+ { object: obj, name: "1", type: "updated", oldValue: 5 },
+ { object: obj, name: "1", type: "reconfigured", oldValue: 6 },
+ { object: obj, name: "1", type: "updated", oldValue: 6 },
+ { object: obj, name: "1", type: "reconfigured", oldValue: 8 },
+ { object: obj, name: "1", type: "reconfigured", oldValue: 7 },
+ // TODO(observe): oldValue should not be present below.
+ { object: obj, name: "1", type: "deleted", oldValue: undefined },
+ { object: obj, name: "1", type: "new" },
+ // TODO(observe): oldValue should be absent below, and type
= "reconfigured".
+ { object: obj, name: "1", type: "updated", oldValue: undefined },
+ { object: obj, name: "1", type: "updated", oldValue: 9 },
+ { object: obj, name: "1", type: "deleted", oldValue: 10 },
+ { object: obj, name: "1", type: "new" },
+]);
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev