Revision: 13213
Author: [email protected]
Date: Thu Dec 13 01:31:44 2012
Log: Object.observe: Make array length and other magic data properties
work correctly.
Also, disable TestFastElementsLength test for now, since it flakes on
buildbots for yet unknown reasons.
[email protected]
BUG=v8:2409
Review URL: https://codereview.chromium.org/11554019
http://code.google.com/p/v8/source/detail?r=13213
Modified:
/branches/bleeding_edge/src/object-observe.js
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/property.h
/branches/bleeding_edge/src/v8natives.js
/branches/bleeding_edge/test/mjsunit/harmony/object-observe.js
=======================================
--- /branches/bleeding_edge/src/object-observe.js Wed Dec 5 04:03:57 2012
+++ /branches/bleeding_edge/src/object-observe.js Thu Dec 13 01:31:44 2012
@@ -84,8 +84,8 @@
var objectInfo = objectInfoMap.get(object);
if (IS_UNDEFINED(objectInfo)) {
objectInfo = CreateObjectInfo(object);
- %SetIsObserved(object, true);
}
+ %SetIsObserved(object, true);
var changeObservers = objectInfo.changeObservers;
if (changeObservers.indexOf(callback) < 0)
@@ -106,8 +106,11 @@
var changeObservers = objectInfo.changeObservers;
var index = changeObservers.indexOf(callback);
- if (index >= 0)
+ if (index >= 0) {
changeObservers.splice(index, 1);
+ if (changeObservers.length === 0)
+ %SetIsObserved(object, false);
+ }
return object;
}
=======================================
--- /branches/bleeding_edge/src/objects.cc Wed Dec 12 03:38:24 2012
+++ /branches/bleeding_edge/src/objects.cc Thu Dec 13 01:31:44 2012
@@ -2943,8 +2943,9 @@
}
Handle<Object> old_value(heap->the_hole_value(), isolate);
- if (FLAG_harmony_observation && map()->is_observed()) {
- old_value = handle(lookup->GetLazyValue(), isolate);
+ if (FLAG_harmony_observation &&
+ map()->is_observed() && lookup->IsDataProperty()) {
+ old_value = Object::GetProperty(self, name);
}
// This is a real property that is not read-only, or it is a
@@ -3030,8 +3031,8 @@
} else {
LookupResult new_lookup(isolate);
self->LocalLookup(*name, &new_lookup, true);
- ASSERT(!new_lookup.GetLazyValue()->IsTheHole());
- if (!new_lookup.GetLazyValue()->SameValue(*old_value)) {
+ if (new_lookup.IsDataProperty() &&
+ !Object::GetProperty(self, name)->SameValue(*old_value)) {
EnqueueChangeRecord(self, "updated", name, old_value);
}
}
@@ -3110,15 +3111,7 @@
PropertyAttributes old_attributes = ABSENT;
bool is_observed = FLAG_harmony_observation &&
self->map()->is_observed();
if (is_observed) {
- // Function prototypes are stored specially
- if (self->IsJSFunction() &&
- JSFunction::cast(*self)->should_have_prototype() &&
- name->Equals(isolate->heap()->prototype_symbol())) {
- MaybeObject* maybe = Accessors::FunctionGetPrototype(*self, NULL);
- if (!maybe->ToHandle(&old_value, isolate)) return maybe;
- } else {
- old_value = handle(lookup.GetLazyValue(), isolate);
- }
+ if (lookup.IsDataProperty()) old_value = Object::GetProperty(self,
name);
old_attributes = lookup.GetAttributes();
}
@@ -3188,11 +3181,11 @@
} else {
LookupResult new_lookup(isolate);
self->LocalLookup(*name, &new_lookup, true);
- ASSERT(!new_lookup.GetLazyValue()->IsTheHole());
if (old_value->IsTheHole() ||
new_lookup.GetAttributes() != old_attributes) {
EnqueueChangeRecord(self, "reconfigured", name, old_value);
- } else if (!new_lookup.GetLazyValue()->SameValue(*old_value)) {
+ } else if (new_lookup.IsDataProperty() &&
+ !Object::GetProperty(self, name)->SameValue(*old_value)) {
EnqueueChangeRecord(self, "updated", name, old_value);
}
}
@@ -4255,8 +4248,8 @@
Handle<Object> old_value(isolate->heap()->the_hole_value());
bool is_observed = FLAG_harmony_observation &&
self->map()->is_observed();
- if (is_observed) {
- old_value = handle(lookup.GetLazyValue(), isolate);
+ if (is_observed && lookup.IsDataProperty()) {
+ old_value = Object::GetProperty(self, hname);
}
MaybeObject* result;
@@ -4947,7 +4940,9 @@
LookupResult lookup(isolate);
LocalLookup(*name, &lookup, true);
preexists = lookup.IsProperty();
- if (preexists) old_value = handle(lookup.GetLazyValue(), isolate);
+ if (preexists && lookup.IsDataProperty()) {
+ old_value = Object::GetProperty(self, name);
+ }
}
}
=======================================
--- /branches/bleeding_edge/src/property.h Tue Nov 13 03:07:04 2012
+++ /branches/bleeding_edge/src/property.h Thu Dec 13 01:31:44 2012
@@ -309,6 +309,26 @@
bool IsProperty() {
return IsFound() && !IsTransition();
}
+
+ bool IsDataProperty() {
+ switch (type()) {
+ case FIELD:
+ case NORMAL:
+ case CONSTANT_FUNCTION:
+ return true;
+ case CALLBACKS: {
+ Object* callback = GetCallbackObject();
+ return callback->IsAccessorInfo() || callback->IsForeign();
+ }
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ return false;
+ }
+ UNREACHABLE();
+ return false;
+ }
bool IsCacheable() { return cacheable_; }
void DisallowCaching() { cacheable_ = false; }
@@ -327,9 +347,15 @@
}
case CONSTANT_FUNCTION:
return GetConstantFunction();
- default:
+ case CALLBACKS:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
return Isolate::Current()->heap()->the_hole_value();
}
+ UNREACHABLE();
+ return NULL;
}
Map* GetTransitionTarget() {
=======================================
--- /branches/bleeding_edge/src/v8natives.js Fri Dec 7 04:18:50 2012
+++ /branches/bleeding_edge/src/v8natives.js Thu Dec 13 01:31:44 2012
@@ -893,16 +893,35 @@
}
// Make sure the below call to DefineObjectProperty() doesn't overwrite
// any magic "length" property by removing the value.
+ // TODO(mstarzinger): This hack should be removed once we have
addressed the
+ // respective TODO in Runtime_DefineOrRedefineDataProperty.
+ // For the time being, we need a hack to prevent Object.observe from
+ // generating two change records.
+ var isObserved = %IsObserved(obj);
+ if (isObserved) %SetIsObserved(obj, false);
obj.length = new_length;
desc.value_ = void 0;
desc.hasValue_ = false;
- if (!DefineObjectProperty(obj, "length", desc, should_throw) || threw)
{
+ threw = !DefineObjectProperty(obj, "length", desc, should_throw) ||
threw;
+ if (isObserved) %SetIsObserved(obj, true);
+ if (threw) {
if (should_throw) {
throw MakeTypeError("redefine_disallowed", [p]);
} else {
return false;
}
}
+ if (isObserved) {
+ var new_desc = GetOwnProperty(obj, "length");
+ var updated = length_desc.value_ !== new_desc.value_;
+ var reconfigured = length_desc.writable_ !== new_desc.writable_ ||
+ length_desc.configurable_ !== new_desc.configurable_ ||
+ length_desc.enumerable_ !== new_desc.configurable_;
+ if (updated || reconfigured) {
+ NotifyChange(reconfigured ? "reconfigured" : "updated",
+ obj, "length", length_desc.value_);
+ }
+ }
return true;
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Wed Dec
12 03:38:24 2012
+++ /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Thu Dec
13 01:31:44 2012
@@ -536,16 +536,13 @@
createProxy(Proxy.create, null),
createProxy(Proxy.createFunction, function(){}),
];
-var properties = ["a", "1", 1, "length", "prototype"];
+var properties = ["a", "1", 1, "length", "prototype", "name", "caller"];
// Cases that yield non-standard results.
-// TODO(observe): ...or don't work yet.
function blacklisted(obj, prop) {
return (obj instanceof Int32Array && prop == 1) ||
(obj instanceof Int32Array && prop === "length") ||
- (obj instanceof ArrayBuffer && prop == 1) ||
- // TODO(observe): oldValue when reconfiguring array length
- (obj instanceof Array && prop === "length")
+ (obj instanceof ArrayBuffer && prop == 1)
}
for (var i in objects) for (var j in properties) {
@@ -581,8 +578,10 @@
arr.length = 2;
arr.length = 0;
arr.length = 10;
+Object.defineProperty(arr, 'length', {writable: false});
arr2.length = 0;
arr2.length = 1; // no change expected
+Object.defineProperty(arr2, 'length', {value: 1, writable: false});
arr3.length = 0;
Object.defineProperty(arr3, 'length', {value: 5});
Object.defineProperty(arr3, 'length', {value: 10, writable: false});
@@ -594,15 +593,15 @@
{ object: arr, name: '1', type: 'deleted', oldValue: 'b' },
{ object: arr, name: 'length', type: 'updated', oldValue: 2 },
{ object: arr, name: 'length', type: 'updated', oldValue: 1 },
+ { object: arr, name: 'length', type: 'reconfigured', oldValue: 10 },
{ object: arr2, name: '1', type: 'deleted', oldValue: 'beta' },
{ object: arr2, name: 'length', type: 'updated', oldValue: 2 },
+ { object: arr2, name: 'length', type: 'reconfigured', oldValue: 1 },
{ object: arr3, name: '2', type: 'deleted', oldValue: 'goodbye' },
{ object: arr3, name: '0', type: 'deleted', oldValue: 'hello' },
{ object: arr3, name: 'length', type: 'updated', oldValue: 6 },
{ object: arr3, name: 'length', type: 'updated', oldValue: 0 },
- { object: arr3, name: 'length', type: 'updated', oldValue: 5 },
- // TODO(adamk): This record should be merged with the above
- { object: arr3, name: 'length', type: 'reconfigured' },
+ { object: arr3, name: 'length', type: 'reconfigured', oldValue: 5 },
]);
// Assignments in loops (checking different IC states).
@@ -944,8 +943,11 @@
}
}
+// TODO(rossberg): Still flaky on buildbots, disable for now...
+/*
for (var b1 = 0; b1 < 2; ++b1)
for (var b2 = 0; b2 < 2; ++b2)
for (var n1 = 0; n1 < 3; ++n1)
for (var n2 = 0; n2 < 3; ++n2)
TestFastElementsLength(b1 != 0, b2 != 0, 20*n1, 20*n2);
+*/
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev