From: adamk@chromium.org Date: Sat, 13 Jul 2013 00:20:40 +0000 (+0000) Subject: Add map transition for observed objects X-Git-Tag: upstream/4.7.83~13385 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=625a0e97594ee4e8ceb05f1c949d2625cad147c2;p=platform%2Fupstream%2Fv8.git 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 . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15650 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index d4d333e..9ce0583 100644 --- a/include/v8.h +++ b/include/v8.h @@ -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; diff --git a/src/heap.cc b/src/heap.cc index ebf1487..0b2fe16 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -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)); diff --git a/src/heap.h b/src/heap.h index 4bc2d30..2c97faf 100644 --- a/src/heap.h +++ b/src/heap.h @@ -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) \ diff --git a/src/object-observe.js b/src/object-observe.js index ada7919..90c9a69 100644 --- a/src/object-observe.js +++ b/src/object-observe.js @@ -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; } diff --git a/src/objects.cc b/src/objects.cc index 3e7a5e9..7b06b87 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4018,7 +4018,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( Handle 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(); diff --git a/src/objects.h b/src/objects.h index a4e70eb..bf04934 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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); diff --git a/src/runtime.cc b/src/runtime.cc index fbad080..3bc9439 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -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); } diff --git a/src/runtime.h b/src/runtime.h index 4777c3c..f730827 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -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) \