Commit e78081ca authored by rafaelw@chromium.org's avatar rafaelw@chromium.org

Make Object.freeze/seal/preventExtensions observable

Note: spec has been updated here: http://wiki.ecmascript.org/doku.php?id=harmony:observe_spec_changes.

R=rossberg@chromium.org, rossberg
BUG=v8:2975,v8:2941

Review URL: https://codereview.chromium.org/47703003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17481 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 5267d7b8
......@@ -132,7 +132,8 @@ var defaultAcceptTypes = TypeMapCreateFromList([
'updated',
'deleted',
'prototype',
'reconfigured'
'reconfigured',
'preventExtensions'
]);
// An Observer is a registration to observe an object by a callback with
......@@ -463,9 +464,20 @@ function NotifyChange(type, object, name, oldValue) {
if (!ObjectInfoHasActiveObservers(objectInfo))
return;
var changeRecord = (arguments.length < 4) ?
{ type: type, object: object, name: name } :
{ type: type, object: object, name: name, oldValue: oldValue };
var changeRecord;
if (arguments.length == 2) {
changeRecord = { type: type, object: object };
} else if (arguments.length == 3) {
changeRecord = { type: type, object: object, name: name };
} else {
changeRecord = {
type: type,
object: object,
name: name,
oldValue: oldValue
};
}
ObjectFreeze(changeRecord);
ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
}
......
......@@ -2175,11 +2175,13 @@ void JSObject::EnqueueChangeRecord(Handle<JSObject> object,
object = handle(JSGlobalObject::cast(*object)->global_receiver(), isolate);
}
Handle<Object> args[] = { type, object, name, old_value };
int argc = name.is_null() ? 2 : old_value->IsTheHole() ? 3 : 4;
bool threw;
Execution::Call(isolate,
Handle<JSFunction>(isolate->observers_notify_change()),
isolate->factory()->undefined_value(),
old_value->IsTheHole() ? 3 : 4, args,
argc, args,
&threw);
ASSERT(!threw);
}
......@@ -5442,6 +5444,9 @@ bool JSObject::ReferencesObject(Object* obj) {
Handle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
Isolate* isolate = object->GetIsolate();
if (!object->map()->is_extensible()) return object;
if (object->IsAccessCheckNeeded() &&
!isolate->MayNamedAccess(*object,
isolate->heap()->undefined_value(),
......@@ -5484,6 +5489,11 @@ Handle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
new_map->set_is_extensible(false);
object->set_map(*new_map);
ASSERT(!object->map()->is_extensible());
if (FLAG_harmony_observation && object->map()->is_observed()) {
EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(),
isolate->factory()->the_hole_value());
}
return object;
}
......@@ -5512,6 +5522,7 @@ static void FreezeDictionary(Dictionary* dictionary) {
Handle<Object> JSObject::Freeze(Handle<JSObject> object) {
// Freezing non-strict arguments should be handled elsewhere.
ASSERT(!object->HasNonStrictArgumentsElements());
ASSERT(!object->map()->is_observed());
if (object->map()->is_frozen()) return object;
......
......@@ -1249,7 +1249,7 @@ function ObjectFreeze(obj) {
throw MakeTypeError("called_on_non_object", ["Object.freeze"]);
}
var isProxy = %IsJSProxy(obj);
if (isProxy || %HasNonStrictArgumentsElements(obj)) {
if (isProxy || %HasNonStrictArgumentsElements(obj) || %IsObserved(obj)) {
if (isProxy) {
ProxyFix(obj);
}
......
......@@ -300,8 +300,103 @@ observer.assertCallbackRecords([
{ object: obj, type: 'deleted', name: '', oldValue: ' ' },
]);
// Object.preventExtensions
reset();
var obj = { foo: 'bar'};
Object.observe(obj, observer.callback);
obj.baz = 'bat';
Object.preventExtensions(obj);
Object.deliverChangeRecords(observer.callback);
observer.assertCallbackRecords([
{ object: obj, type: 'new', name: 'baz' },
{ object: obj, type: 'preventExtensions' },
]);
reset();
var obj = { foo: 'bar'};
Object.preventExtensions(obj);
Object.observe(obj, observer.callback);
Object.preventExtensions(obj);
Object.deliverChangeRecords(observer.callback);
observer.assertNotCalled();
// Object.freeze
reset();
var obj = { a: 'a' };
Object.defineProperty(obj, 'b', {
writable: false,
configurable: true,
value: 'b'
});
Object.defineProperty(obj, 'c', {
writable: true,
configurable: false,
value: 'c'
});
Object.defineProperty(obj, 'd', {
writable: false,
configurable: false,
value: 'd'
});
Object.observe(obj, observer.callback);
Object.freeze(obj);
Object.deliverChangeRecords(observer.callback);
observer.assertCallbackRecords([
{ object: obj, type: 'reconfigured', name: 'a' },
{ object: obj, type: 'reconfigured', name: 'b' },
{ object: obj, type: 'reconfigured', name: 'c' },
{ object: obj, type: 'preventExtensions' },
]);
reset();
var obj = { foo: 'bar'};
Object.freeze(obj);
Object.observe(obj, observer.callback);
Object.freeze(obj);
Object.deliverChangeRecords(observer.callback);
observer.assertNotCalled();
// Object.seal
reset();
var obj = { a: 'a' };
Object.defineProperty(obj, 'b', {
writable: false,
configurable: true,
value: 'b'
});
Object.defineProperty(obj, 'c', {
writable: true,
configurable: false,
value: 'c'
});
Object.defineProperty(obj, 'd', {
writable: false,
configurable: false,
value: 'd'
});
Object.observe(obj, observer.callback);
Object.seal(obj);
Object.deliverChangeRecords(observer.callback);
observer.assertCallbackRecords([
{ object: obj, type: 'reconfigured', name: 'a' },
{ object: obj, type: 'reconfigured', name: 'b' },
{ object: obj, type: 'preventExtensions' },
]);
reset();
var obj = { foo: 'bar'};
Object.seal(obj);
Object.observe(obj, observer.callback);
Object.seal(obj);
Object.deliverChangeRecords(observer.callback);
observer.assertNotCalled();
// Observing a continuous stream of changes, while itermittantly unobserving.
reset();
var obj = {};
Object.observe(obj, observer.callback);
Object.getNotifier(obj).notify({
type: 'updated',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment