Add map transition for observed objects
This patch enables objects to undergo a single transition when they become observed, avoiding the need to create a new map for every observed objects. Observed objects which become unobserved does not cause another map transition and unobserved does not clear the observed bit on the map. The unobserved object. R=verwaest@chromium.org Review URL: https://codereview.chromium.org/18221006 Patch from Rafael Weinstein <rafaelw@chromium.org>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15650 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
f5ab1b2a4a
commit
625a0e9759
@ -5399,7 +5399,7 @@ class Internals {
|
||||
static const int kNullValueRootIndex = 7;
|
||||
static const int kTrueValueRootIndex = 8;
|
||||
static const int kFalseValueRootIndex = 9;
|
||||
static const int kEmptyStringRootIndex = 131;
|
||||
static const int kEmptyStringRootIndex = 132;
|
||||
|
||||
static const int kNodeClassIdOffset = 1 * kApiPointerSize;
|
||||
static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3;
|
||||
|
@ -3172,6 +3172,11 @@ bool Heap::CreateInitialObjects() {
|
||||
SeededNumberDictionary::cast(obj)->set_requires_slow_elements();
|
||||
set_empty_slow_element_dictionary(SeededNumberDictionary::cast(obj));
|
||||
|
||||
{ MaybeObject* maybe_obj = AllocateSymbol();
|
||||
if (!maybe_obj->ToObject(&obj)) return false;
|
||||
}
|
||||
set_observed_symbol(Symbol::cast(obj));
|
||||
|
||||
// Handling of script id generation is in Factory::NewScript.
|
||||
set_last_script_id(Smi::FromInt(v8::Script::kNoScriptId));
|
||||
|
||||
|
@ -187,7 +187,8 @@ namespace internal {
|
||||
V(Map, external_map, ExternalMap) \
|
||||
V(Symbol, frozen_symbol, FrozenSymbol) \
|
||||
V(SeededNumberDictionary, empty_slow_element_dictionary, \
|
||||
EmptySlowElementDictionary)
|
||||
EmptySlowElementDictionary) \
|
||||
V(Symbol, observed_symbol, ObservedSymbol)
|
||||
|
||||
#define ROOT_LIST(V) \
|
||||
STRONG_ROOT_LIST(V) \
|
||||
|
@ -216,8 +216,10 @@ function ObjectObserve(object, callback, accept) {
|
||||
}
|
||||
|
||||
var objectInfo = objectInfoMap.get(object);
|
||||
if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
|
||||
%SetIsObserved(object, true);
|
||||
if (IS_UNDEFINED(objectInfo)) {
|
||||
objectInfo = CreateObjectInfo(object);
|
||||
%SetIsObserved(object);
|
||||
}
|
||||
|
||||
EnsureObserverRemoved(objectInfo, callback);
|
||||
|
||||
@ -241,12 +243,6 @@ function ObjectUnobserve(object, callback) {
|
||||
return object;
|
||||
|
||||
EnsureObserverRemoved(objectInfo, callback);
|
||||
|
||||
if (objectInfo.changeObservers.length === 0 &&
|
||||
objectInfo.inactiveObservers.length === 0) {
|
||||
%SetIsObserved(object, false);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
@ -4018,7 +4018,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
||||
Handle<Object> old_value(isolate->heap()->the_hole_value(), isolate);
|
||||
PropertyAttributes old_attributes = ABSENT;
|
||||
bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
|
||||
if (is_observed) {
|
||||
if (is_observed && lookup.IsProperty()) {
|
||||
if (lookup.IsDataProperty()) old_value = Object::GetProperty(self, name);
|
||||
old_attributes = lookup.GetAttributes();
|
||||
}
|
||||
@ -5557,6 +5557,40 @@ MUST_USE_RESULT MaybeObject* JSObject::Freeze(Isolate* isolate) {
|
||||
}
|
||||
|
||||
|
||||
MUST_USE_RESULT MaybeObject* JSObject::SetObserved(Isolate* isolate) {
|
||||
if (map()->is_observed())
|
||||
return isolate->heap()->undefined_value();
|
||||
|
||||
Heap* heap = isolate->heap();
|
||||
|
||||
if (!HasExternalArrayElements()) {
|
||||
// Go to dictionary mode, so that we don't skip map checks.
|
||||
MaybeObject* maybe = NormalizeElements();
|
||||
if (maybe->IsFailure()) return maybe;
|
||||
ASSERT(!HasFastElements());
|
||||
}
|
||||
|
||||
LookupResult result(isolate);
|
||||
map()->LookupTransition(this, heap->observed_symbol(), &result);
|
||||
|
||||
Map* new_map;
|
||||
if (result.IsTransition()) {
|
||||
new_map = result.GetTransitionTarget();
|
||||
ASSERT(new_map->is_observed());
|
||||
} else if (map()->CanHaveMoreTransitions()) {
|
||||
MaybeObject* maybe_new_map = map()->CopyForObserved();
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
} else {
|
||||
MaybeObject* maybe_copy = map()->Copy();
|
||||
if (!maybe_copy->To(&new_map)) return maybe_copy;
|
||||
new_map->set_is_observed(true);
|
||||
}
|
||||
set_map(new_map);
|
||||
|
||||
return heap->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
MUST_USE_RESULT MaybeObject* JSObject::DeepCopy(Isolate* isolate) {
|
||||
StackLimitCheck check(isolate);
|
||||
if (check.HasOverflowed()) return isolate->StackOverflow();
|
||||
@ -6680,6 +6714,39 @@ MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) {
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* Map::CopyForObserved() {
|
||||
ASSERT(!is_observed());
|
||||
|
||||
// In case the map owned its own descriptors, share the descriptors and
|
||||
// transfer ownership to the new map.
|
||||
Map* new_map;
|
||||
MaybeObject* maybe_new_map;
|
||||
if (owns_descriptors()) {
|
||||
maybe_new_map = CopyDropDescriptors();
|
||||
} else {
|
||||
maybe_new_map = Copy();
|
||||
}
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
|
||||
TransitionArray* transitions;
|
||||
MaybeObject* maybe_transitions = AddTransition(GetHeap()->observed_symbol(),
|
||||
new_map,
|
||||
FULL_TRANSITION);
|
||||
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
|
||||
set_transitions(transitions);
|
||||
|
||||
new_map->set_is_observed(true);
|
||||
|
||||
if (owns_descriptors()) {
|
||||
new_map->InitializeDescriptors(instance_descriptors());
|
||||
set_owns_descriptors(false);
|
||||
}
|
||||
|
||||
new_map->SetBackPointer(this);
|
||||
return new_map;
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() {
|
||||
if (pre_allocated_property_fields() == 0) return CopyDropDescriptors();
|
||||
|
||||
|
@ -2333,6 +2333,10 @@ class JSObject: public JSReceiver {
|
||||
// ES5 Object.freeze
|
||||
MUST_USE_RESULT MaybeObject* Freeze(Isolate* isolate);
|
||||
|
||||
|
||||
// Called the first time an object is observed with ES7 Object.observe.
|
||||
MUST_USE_RESULT MaybeObject* SetObserved(Isolate* isolate);
|
||||
|
||||
// Copy object
|
||||
MUST_USE_RESULT MaybeObject* DeepCopy(Isolate* isolate);
|
||||
|
||||
@ -5477,8 +5481,10 @@ class Map: public HeapObject {
|
||||
int index,
|
||||
TransitionFlag flag);
|
||||
MUST_USE_RESULT MaybeObject* AsElementsKind(ElementsKind kind);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* CopyAsElementsKind(ElementsKind kind,
|
||||
TransitionFlag flag);
|
||||
MUST_USE_RESULT MaybeObject* CopyForObserved();
|
||||
|
||||
MUST_USE_RESULT MaybeObject* CopyNormalized(PropertyNormalizationMode mode,
|
||||
NormalizedMapSharingMode sharing);
|
||||
|
@ -13786,9 +13786,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsObserved) {
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetIsObserved) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 2);
|
||||
ASSERT(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(JSReceiver, obj, 0);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(is_observed, 1);
|
||||
if (obj->IsJSGlobalProxy()) {
|
||||
Object* proto = obj->GetPrototype();
|
||||
if (proto->IsNull()) return isolate->heap()->undefined_value();
|
||||
@ -13797,21 +13796,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetIsObserved) {
|
||||
}
|
||||
ASSERT(!(obj->map()->is_observed() && obj->IsJSObject() &&
|
||||
JSObject::cast(obj)->HasFastElements()));
|
||||
if (obj->map()->is_observed() != is_observed) {
|
||||
if (is_observed && obj->IsJSObject() &&
|
||||
!JSObject::cast(obj)->HasExternalArrayElements()) {
|
||||
// Go to dictionary mode, so that we don't skip map checks.
|
||||
MaybeObject* maybe = JSObject::cast(obj)->NormalizeElements();
|
||||
if (maybe->IsFailure()) return maybe;
|
||||
ASSERT(!JSObject::cast(obj)->HasFastElements());
|
||||
}
|
||||
MaybeObject* maybe = obj->map()->Copy();
|
||||
Map* map;
|
||||
if (!maybe->To(&map)) return maybe;
|
||||
map->set_is_observed(is_observed);
|
||||
obj->set_map(map);
|
||||
}
|
||||
return isolate->heap()->undefined_value();
|
||||
ASSERT(obj->IsJSObject());
|
||||
return JSObject::cast(obj)->SetObserved(isolate);
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,7 +352,7 @@ namespace internal {
|
||||
\
|
||||
/* Harmony observe */ \
|
||||
F(IsObserved, 1, 1) \
|
||||
F(SetIsObserved, 2, 1) \
|
||||
F(SetIsObserved, 1, 1) \
|
||||
F(SetObserverDeliveryPending, 0, 1) \
|
||||
F(GetObservationState, 0, 1) \
|
||||
F(ObservationWeakMapCreate, 0, 1) \
|
||||
|
Loading…
Reference in New Issue
Block a user