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 d4d333e43..9ce05831b 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 ebf1487e1..0b2fe1648 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 4bc2d307e..2c97fafcb 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 ada7919d6..90c9a699e 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 3e7a5e9c4..7b06b8732 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 a4e70eb8e..bf0493460 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 fbad08034..3bc943947 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 4777c3cba..f73082788 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) \