Skip to content

Commit

Permalink
Reland "Enable Object.observe by default"
Browse files Browse the repository at this point in the history
Original Issue: https://codereview.chromium.org/183683022/

TBR=rossberg
BUG=v8:2409
LOG=Y

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@19736 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
  • Loading branch information
rafaelw@chromium.org committed Mar 8, 2014
1 parent d6db16d commit dc0c208
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 217 deletions.
5 changes: 1 addition & 4 deletions src/accessors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -600,10 +600,7 @@ MaybeObject* Accessors::FunctionSetPrototype(Isolate* isolate,
}

Handle<Object> old_value;
bool is_observed =
FLAG_harmony_observation &&
*function == *object &&
function->map()->is_observed();
bool is_observed = *function == *object && function->map()->is_observed();
if (is_observed) {
if (function->has_prototype())
old_value = handle(function->prototype(), isolate);
Expand Down
15 changes: 6 additions & 9 deletions src/bootstrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,12 @@ void Genesis::InstallNativeFunctions() {
INSTALL_NATIVE(JSObject, "functionCache", function_cache);
INSTALL_NATIVE(JSFunction, "ToCompletePropertyDescriptor",
to_complete_property_descriptor);
INSTALL_NATIVE(JSFunction, "NotifyChange", observers_notify_change);
INSTALL_NATIVE(JSFunction, "EnqueueSpliceRecord", observers_enqueue_splice);
INSTALL_NATIVE(JSFunction, "BeginPerformSplice",
observers_begin_perform_splice);
INSTALL_NATIVE(JSFunction, "EndPerformSplice",
observers_end_perform_splice);
}


Expand All @@ -1591,14 +1597,6 @@ void Genesis::InstallExperimentalNativeFunctions() {
INSTALL_NATIVE(JSFunction, "DerivedSetTrap", derived_set_trap);
INSTALL_NATIVE(JSFunction, "ProxyEnumerate", proxy_enumerate);
}
if (FLAG_harmony_observation) {
INSTALL_NATIVE(JSFunction, "NotifyChange", observers_notify_change);
INSTALL_NATIVE(JSFunction, "EnqueueSpliceRecord", observers_enqueue_splice);
INSTALL_NATIVE(JSFunction, "BeginPerformSplice",
observers_begin_perform_splice);
INSTALL_NATIVE(JSFunction, "EndPerformSplice",
observers_end_perform_splice);
}
}

#undef INSTALL_NATIVE
Expand Down Expand Up @@ -2042,7 +2040,6 @@ bool Genesis::InstallExperimentalNatives() {
INSTALL_EXPERIMENTAL_NATIVE(i, symbols, "symbol.js")
INSTALL_EXPERIMENTAL_NATIVE(i, proxies, "proxy.js")
INSTALL_EXPERIMENTAL_NATIVE(i, collections, "collection.js")
INSTALL_EXPERIMENTAL_NATIVE(i, observation, "object-observe.js")
INSTALL_EXPERIMENTAL_NATIVE(i, promises, "promise.js")
INSTALL_EXPERIMENTAL_NATIVE(i, generators, "generator.js")
INSTALL_EXPERIMENTAL_NATIVE(i, iteration, "array-iterator.js")
Expand Down
4 changes: 0 additions & 4 deletions src/flag-definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,6 @@ DEFINE_bool(harmony_promises, false, "enable harmony promises")
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
DEFINE_bool(harmony_collections, false,
"enable harmony collections (sets, maps, and weak maps)")
DEFINE_bool(harmony_observation, false,
"enable harmony object observation (implies harmony collections")
DEFINE_bool(harmony_generators, false, "enable harmony generators")
DEFINE_bool(harmony_iteration, false, "enable harmony iteration (for-of)")
DEFINE_bool(harmony_numeric_literals, false,
Expand All @@ -196,7 +194,6 @@ DEFINE_implication(harmony, harmony_symbols)
DEFINE_implication(harmony, harmony_promises)
DEFINE_implication(harmony, harmony_proxies)
DEFINE_implication(harmony, harmony_collections)
DEFINE_implication(harmony, harmony_observation)
DEFINE_implication(harmony, harmony_generators)
DEFINE_implication(harmony, harmony_iteration)
DEFINE_implication(harmony, harmony_numeric_literals)
Expand All @@ -205,7 +202,6 @@ DEFINE_implication(harmony, harmony_arrays)
DEFINE_implication(harmony, harmony_maths)
DEFINE_implication(harmony_promises, harmony_collections)
DEFINE_implication(harmony_modules, harmony_scoping)
DEFINE_implication(harmony_observation, harmony_collections)

// Flags for experimental implementation features.
DEFINE_bool(packed_arrays, true, "optimizes arrays that have no holes")
Expand Down
4 changes: 2 additions & 2 deletions src/ic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ MaybeObject* StoreIC::Store(Handle<Object> object,
}

// Observed objects are always modified through the runtime.
if (FLAG_harmony_observation && receiver->map()->is_observed()) {
if (receiver->map()->is_observed()) {
Handle<Object> result = JSReceiver::SetProperty(
receiver, name, value, NONE, strict_mode(), store_mode);
RETURN_IF_EMPTY_HANDLE(isolate(), result);
Expand Down Expand Up @@ -1671,7 +1671,7 @@ MaybeObject* KeyedStoreIC::Store(Handle<Object> object,
if (maybe_object->IsFailure()) return maybe_object;
} else {
bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded() &&
!(FLAG_harmony_observation && object->IsJSObject() &&
!(object->IsJSObject() &&
JSObject::cast(*object)->map()->is_observed());
if (use_ic && !object->IsSmi()) {
// Don't use ICs for maps of the objects in Array's prototype chain. We
Expand Down
148 changes: 97 additions & 51 deletions src/object-observe.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,40 +56,86 @@
// implementation of (1) and (2) have "optimized" states which represent
// common cases which can be handled more efficiently.

var observationState = %GetObservationState();
if (IS_UNDEFINED(observationState.callbackInfoMap)) {
observationState.callbackInfoMap = %ObservationWeakMapCreate();
observationState.objectInfoMap = %ObservationWeakMapCreate();
observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
observationState.pendingObservers = null;
observationState.nextCallbackPriority = 0;
}

function ObservationWeakMap(map) {
this.map_ = map;
}

ObservationWeakMap.prototype = {
get: function(key) {
key = %UnwrapGlobalProxy(key);
if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
return %WeakCollectionGet(this.map_, key);
},
set: function(key, value) {
key = %UnwrapGlobalProxy(key);
if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
%WeakCollectionSet(this.map_, key, value);
},
has: function(key) {
return !IS_UNDEFINED(this.get(key));
var observationState;

function GetObservationState() {
if (IS_UNDEFINED(observationState))
observationState = %GetObservationState();

if (IS_UNDEFINED(observationState.callbackInfoMap)) {
observationState.callbackInfoMap = %ObservationWeakMapCreate();
observationState.objectInfoMap = %ObservationWeakMapCreate();
observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
observationState.pendingObservers = null;
observationState.nextCallbackPriority = 0;
}

return observationState;
}

function GetWeakMapWrapper() {
function MapWrapper(map) {
this.map_ = map;
};

MapWrapper.prototype = {
get: function(key) {
key = %UnwrapGlobalProxy(key);
if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
return %WeakCollectionGet(this.map_, key);
},
set: function(key, value) {
key = %UnwrapGlobalProxy(key);
if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
%WeakCollectionSet(this.map_, key, value);
},
has: function(key) {
return !IS_UNDEFINED(this.get(key));
}
};

return MapWrapper;
}

var contextMaps;

function GetContextMaps() {
if (IS_UNDEFINED(contextMaps)) {
var map = GetWeakMapWrapper();
var observationState = GetObservationState();
contextMaps = {
callbackInfoMap: new map(observationState.callbackInfoMap),
objectInfoMap: new map(observationState.objectInfoMap),
notifierObjectInfoMap: new map(observationState.notifierObjectInfoMap)
};
}
};

var callbackInfoMap =
new ObservationWeakMap(observationState.callbackInfoMap);
var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap);
var notifierObjectInfoMap =
new ObservationWeakMap(observationState.notifierObjectInfoMap);
return contextMaps;
}

function GetCallbackInfoMap() {
return GetContextMaps().callbackInfoMap;
}

function GetObjectInfoMap() {
return GetContextMaps().objectInfoMap;
}

function GetNotifierObjectInfoMap() {
return GetContextMaps().notifierObjectInfoMap;
}

function GetPendingObservers() {
return GetObservationState().pendingObservers;
}

function SetPendingObservers(pendingObservers) {
GetObservationState().pendingObservers = pendingObservers;
}

function GetNextCallbackPriority() {
return GetObservationState().nextCallbackPriority++;
}

function nullProtoObject() {
return { __proto__: null };
Expand Down Expand Up @@ -180,23 +226,23 @@ function ObjectInfoGetOrCreate(object) {
performing: null,
performingCount: 0,
};
objectInfoMap.set(object, objectInfo);
GetObjectInfoMap().set(object, objectInfo);
}
return objectInfo;
}

function ObjectInfoGet(object) {
return objectInfoMap.get(object);
return GetObjectInfoMap().get(object);
}

function ObjectInfoGetFromNotifier(notifier) {
return notifierObjectInfoMap.get(notifier);
return GetNotifierObjectInfoMap().get(notifier);
}

function ObjectInfoGetNotifier(objectInfo) {
if (IS_NULL(objectInfo.notifier)) {
objectInfo.notifier = { __proto__: notifierPrototype };
notifierObjectInfoMap.set(objectInfo.notifier, objectInfo);
GetNotifierObjectInfoMap().set(objectInfo.notifier, objectInfo);
}

return objectInfo.notifier;
Expand Down Expand Up @@ -302,16 +348,16 @@ function AcceptArgIsValid(arg) {
// priority. When a change record must be enqueued for the callback, it
// normalizes. When delivery clears any pending change records, it re-optimizes.
function CallbackInfoGet(callback) {
return callbackInfoMap.get(callback);
return GetCallbackInfoMap().get(callback);
}

function CallbackInfoGetOrCreate(callback) {
var callbackInfo = callbackInfoMap.get(callback);
var callbackInfo = GetCallbackInfoMap().get(callback);
if (!IS_UNDEFINED(callbackInfo))
return callbackInfo;

var priority = observationState.nextCallbackPriority++
callbackInfoMap.set(callback, priority);
var priority = GetNextCallbackPriority();
GetCallbackInfoMap().set(callback, priority);
return priority;
}

Expand All @@ -323,12 +369,12 @@ function CallbackInfoGetPriority(callbackInfo) {
}

function CallbackInfoNormalize(callback) {
var callbackInfo = callbackInfoMap.get(callback);
var callbackInfo = GetCallbackInfoMap().get(callback);
if (IS_NUMBER(callbackInfo)) {
var priority = callbackInfo;
callbackInfo = new InternalArray;
callbackInfo.priority = priority;
callbackInfoMap.set(callback, callbackInfo);
GetCallbackInfoMap().set(callback, callbackInfo);
}
return callbackInfo;
}
Expand Down Expand Up @@ -390,12 +436,12 @@ function ObserverEnqueueIfActive(observer, objectInfo, changeRecord,
}

var callbackInfo = CallbackInfoNormalize(callback);
if (IS_NULL(observationState.pendingObservers)) {
observationState.pendingObservers = nullProtoObject();
if (IS_NULL(GetPendingObservers())) {
SetPendingObservers(nullProtoObject())
GetMicrotaskQueue().push(ObserveMicrotaskRunner);
%SetMicrotaskPending(true);
}
observationState.pendingObservers[callbackInfo.priority] = callback;
GetPendingObservers()[callbackInfo.priority] = callback;
callbackInfo.push(changeRecord);
}

Expand Down Expand Up @@ -548,17 +594,17 @@ function ObjectGetNotifier(object) {
}

function CallbackDeliverPending(callback) {
var callbackInfo = callbackInfoMap.get(callback);
var callbackInfo = GetCallbackInfoMap().get(callback);
if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
return false;

// Clear the pending change records from callback and return it to its
// "optimized" state.
var priority = callbackInfo.priority;
callbackInfoMap.set(callback, priority);
GetCallbackInfoMap().set(callback, priority);

if (observationState.pendingObservers)
delete observationState.pendingObservers[priority];
if (GetPendingObservers())
delete GetPendingObservers()[priority];

var delivered = [];
%MoveArrayContents(callbackInfo, delivered);
Expand All @@ -577,9 +623,9 @@ function ObjectDeliverChangeRecords(callback) {
}

function ObserveMicrotaskRunner() {
var pendingObservers = observationState.pendingObservers;
var pendingObservers = GetPendingObservers();
if (pendingObservers) {
observationState.pendingObservers = null;
SetPendingObservers(null);
for (var i in pendingObservers) {
CallbackDeliverPending(pendingObservers[i]);
}
Expand Down
Loading

0 comments on commit dc0c208

Please sign in to comment.