2012-07-05 13:54:20 +00:00
|
|
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
2014-04-29 06:42:26 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
2012-07-05 13:54:20 +00:00
|
|
|
|
|
|
|
#ifndef V8_TRANSITIONS_H_
|
|
|
|
#define V8_TRANSITIONS_H_
|
|
|
|
|
2014-06-30 13:25:46 +00:00
|
|
|
#include "src/checks.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/elements-kind.h"
|
2014-08-05 08:18:22 +00:00
|
|
|
#include "src/heap/heap.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/isolate.h"
|
|
|
|
#include "src/objects.h"
|
2012-07-05 13:54:20 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
|
|
// TransitionArrays are fixed arrays used to hold map transitions for property,
|
2012-10-17 13:04:49 +00:00
|
|
|
// constant, and element changes. They can either be simple transition arrays
|
|
|
|
// that store a single property transition, or a full transition array that has
|
2013-07-31 17:08:50 +00:00
|
|
|
// prototype transitions and multiple property transitons. The details related
|
|
|
|
// to property transitions are accessed in the descriptor array of the target
|
|
|
|
// map. In the case of a simple transition, the key is also read from the
|
|
|
|
// descriptor array of the target map.
|
2012-10-17 13:04:49 +00:00
|
|
|
//
|
|
|
|
// The simple format of the these objects is:
|
|
|
|
// [0] Undefined or back pointer map
|
|
|
|
// [1] Single transition
|
|
|
|
//
|
|
|
|
// The full format is:
|
|
|
|
// [0] Undefined or back pointer map
|
2013-07-31 17:08:50 +00:00
|
|
|
// [1] Smi(0) or fixed array of prototype transitions
|
2014-11-03 16:44:58 +00:00
|
|
|
// [2] Number of transitions
|
|
|
|
// [3] First transition
|
|
|
|
// [3 + number of transitions * kTransitionSize]: start of slack
|
2012-07-05 13:54:20 +00:00
|
|
|
class TransitionArray: public FixedArray {
|
|
|
|
public:
|
|
|
|
// Accessors for fetching instance transition at transition number.
|
2013-03-04 15:00:57 +00:00
|
|
|
inline Name* GetKey(int transition_number);
|
|
|
|
inline void SetKey(int transition_number, Name* value);
|
2012-07-10 07:53:00 +00:00
|
|
|
inline Object** GetKeySlot(int transition_number);
|
2012-08-27 13:47:34 +00:00
|
|
|
int GetSortedKeyIndex(int transition_number) { return transition_number; }
|
|
|
|
|
2013-03-04 15:00:57 +00:00
|
|
|
Name* GetSortedKey(int transition_number) {
|
2012-08-27 13:47:34 +00:00
|
|
|
return GetKey(transition_number);
|
|
|
|
}
|
2012-07-10 07:53:00 +00:00
|
|
|
|
2012-07-16 14:02:50 +00:00
|
|
|
inline Map* GetTarget(int transition_number);
|
|
|
|
inline void SetTarget(int transition_number, Map* target);
|
2012-07-10 07:53:00 +00:00
|
|
|
|
2012-07-05 13:54:20 +00:00
|
|
|
inline PropertyDetails GetTargetDetails(int transition_number);
|
2014-10-23 11:31:33 +00:00
|
|
|
inline Object* GetTargetValue(int transition_number);
|
2012-07-10 07:53:00 +00:00
|
|
|
|
|
|
|
inline bool HasElementsTransition();
|
|
|
|
|
2012-08-13 08:43:16 +00:00
|
|
|
inline Object* back_pointer_storage();
|
|
|
|
inline void set_back_pointer_storage(
|
|
|
|
Object* back_pointer,
|
|
|
|
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
|
|
|
|
|
2012-07-10 07:53:00 +00:00
|
|
|
inline FixedArray* GetPrototypeTransitions();
|
|
|
|
inline void SetPrototypeTransitions(
|
|
|
|
FixedArray* prototype_transitions,
|
|
|
|
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
|
|
|
|
inline Object** GetPrototypeTransitionsSlot();
|
|
|
|
inline bool HasPrototypeTransitions();
|
2012-07-05 13:54:20 +00:00
|
|
|
|
|
|
|
// Returns the number of transitions in the array.
|
|
|
|
int number_of_transitions() {
|
2012-09-19 09:54:10 +00:00
|
|
|
if (IsSimpleTransition()) return 1;
|
2014-11-03 16:44:58 +00:00
|
|
|
if (length() <= kFirstIndex) return 0;
|
|
|
|
return Smi::cast(get(kTransitionLengthIndex))->value();
|
2012-07-05 13:54:20 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 16:44:58 +00:00
|
|
|
int number_of_transitions_storage() {
|
|
|
|
if (IsSimpleTransition()) return 1;
|
|
|
|
if (length() <= kFirstIndex) return 0;
|
|
|
|
return (length() - kFirstIndex) / kTransitionSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NumberOfSlackTransitions() {
|
|
|
|
return number_of_transitions_storage() - number_of_transitions();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void SetNumberOfTransitions(int number_of_transitions);
|
2012-07-05 13:54:20 +00:00
|
|
|
inline int number_of_entries() { return number_of_transitions(); }
|
|
|
|
|
2014-04-11 14:25:00 +00:00
|
|
|
// Creates a FullTransitionArray from a SimpleTransitionArray in
|
|
|
|
// containing_map.
|
2014-04-11 12:13:53 +00:00
|
|
|
static Handle<TransitionArray> ExtendToFullTransitionArray(
|
2014-04-11 14:25:00 +00:00
|
|
|
Handle<Map> containing_map);
|
2012-09-19 09:54:10 +00:00
|
|
|
|
2014-11-03 16:44:58 +00:00
|
|
|
// Return a transition array, using the array from the owning map if it
|
|
|
|
// already has one (copying into a larger array if necessary), otherwise
|
|
|
|
// creating a new one according to flag.
|
2012-07-05 13:54:20 +00:00
|
|
|
// TODO(verwaest): This should not cause an existing transition to be
|
|
|
|
// overwritten.
|
2014-11-03 16:44:58 +00:00
|
|
|
static Handle<TransitionArray> Insert(Handle<Map> map, Handle<Name> name,
|
|
|
|
Handle<Map> target,
|
|
|
|
SimpleTransitionFlag flag);
|
2012-07-05 13:54:20 +00:00
|
|
|
|
2014-11-13 15:30:53 +00:00
|
|
|
// Search a transition for a given property name.
|
2014-11-24 14:31:31 +00:00
|
|
|
inline int Search(Name* name, int* out_insertion_index = NULL);
|
2012-07-05 13:54:20 +00:00
|
|
|
|
|
|
|
// Allocates a TransitionArray.
|
2014-11-03 16:44:58 +00:00
|
|
|
static Handle<TransitionArray> Allocate(Isolate* isolate,
|
|
|
|
int number_of_transitions,
|
|
|
|
int slack = 0);
|
2012-07-05 13:54:20 +00:00
|
|
|
|
2013-07-31 17:08:50 +00:00
|
|
|
bool IsSimpleTransition() {
|
|
|
|
return length() == kSimpleTransitionSize &&
|
|
|
|
get(kSimpleTransitionTarget)->IsHeapObject() &&
|
|
|
|
// The IntrusivePrototypeTransitionIterator may have set the map of the
|
|
|
|
// prototype transitions array to a smi. In that case, there are
|
|
|
|
// prototype transitions, hence this transition array is a full
|
|
|
|
// transition array.
|
|
|
|
HeapObject::cast(get(kSimpleTransitionTarget))->map()->IsMap() &&
|
|
|
|
get(kSimpleTransitionTarget)->IsMap();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsFullTransitionArray() {
|
|
|
|
return length() > kFirstIndex ||
|
|
|
|
(length() == kFirstIndex && !IsSimpleTransition());
|
|
|
|
}
|
2012-09-19 09:54:10 +00:00
|
|
|
|
2012-07-05 13:54:20 +00:00
|
|
|
// Casting.
|
|
|
|
static inline TransitionArray* cast(Object* obj);
|
|
|
|
|
|
|
|
// Constant for denoting key was not found.
|
|
|
|
static const int kNotFound = -1;
|
|
|
|
|
2012-10-17 13:04:49 +00:00
|
|
|
static const int kBackPointerStorageIndex = 0;
|
2012-09-19 09:54:10 +00:00
|
|
|
|
|
|
|
// Layout for full transition arrays.
|
2013-07-31 17:08:50 +00:00
|
|
|
static const int kPrototypeTransitionsIndex = 1;
|
2014-11-03 16:44:58 +00:00
|
|
|
static const int kTransitionLengthIndex = 2;
|
|
|
|
static const int kFirstIndex = 3;
|
2012-07-05 13:54:20 +00:00
|
|
|
|
2012-09-19 09:54:10 +00:00
|
|
|
// Layout for simple transition arrays.
|
2012-10-17 13:04:49 +00:00
|
|
|
static const int kSimpleTransitionTarget = 1;
|
|
|
|
static const int kSimpleTransitionSize = 2;
|
2012-09-19 09:54:10 +00:00
|
|
|
static const int kSimpleTransitionIndex = 0;
|
|
|
|
STATIC_ASSERT(kSimpleTransitionIndex != kNotFound);
|
|
|
|
|
2012-10-17 13:04:49 +00:00
|
|
|
static const int kBackPointerStorageOffset = FixedArray::kHeaderSize;
|
2012-09-19 09:54:10 +00:00
|
|
|
|
|
|
|
// Layout for the full transition array header.
|
2013-07-31 17:08:50 +00:00
|
|
|
static const int kPrototypeTransitionsOffset = kBackPointerStorageOffset +
|
2012-07-10 07:53:00 +00:00
|
|
|
kPointerSize;
|
2014-11-03 16:44:58 +00:00
|
|
|
static const int kTransitionLengthOffset =
|
|
|
|
kPrototypeTransitionsOffset + kPointerSize;
|
2012-07-05 13:54:20 +00:00
|
|
|
|
2012-09-19 09:54:10 +00:00
|
|
|
// Layout of map transition entries in full transition arrays.
|
2012-07-05 13:54:20 +00:00
|
|
|
static const int kTransitionKey = 0;
|
2012-07-16 14:02:50 +00:00
|
|
|
static const int kTransitionTarget = 1;
|
2012-07-05 13:54:20 +00:00
|
|
|
static const int kTransitionSize = 2;
|
|
|
|
|
|
|
|
#ifdef OBJECT_PRINT
|
2014-10-23 11:31:33 +00:00
|
|
|
// For our gdb macros, we should perhaps change these in the future.
|
|
|
|
void Print();
|
|
|
|
|
2012-07-05 13:54:20 +00:00
|
|
|
// Print all the transitions.
|
2014-10-23 11:31:33 +00:00
|
|
|
void PrintTransitions(std::ostream& os, bool print_header = true); // NOLINT
|
2012-07-05 13:54:20 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
Sharing of descriptor arrays.
This CL adds multiple things:
Transition arrays do not directly point at their descriptor array anymore, but rather do so via an indirect pointer (a JSGlobalPropertyCell).
An ownership bit is added to maps indicating whether it owns its own descriptor array or not.
Maps owning a descriptor array can pass on ownership if a transition from that map is generated; but only if the descriptor array stays exactly the same; or if a descriptor is added.
Maps that don't have ownership get ownership back if their direct child to which ownership was passed is cleared in ClearNonLiveTransitions.
To detect which descriptors in an array are valid, each map knows its own NumberOfOwnDescriptors. Since the descriptors are sorted in order of addition, if we search and find a descriptor with index bigger than this number, it is not valid for the given map.
We currently still build up an enumeration cache (although this may disappear). The enumeration cache is always built for the entire descriptor array, even if not all descriptors are owned by the map. Once a descriptor array has an enumeration cache for a given map; this invariant will always be true, even if the descriptor array was extended. The extended array will inherit the enumeration cache from the smaller descriptor array. If a map with more descriptors needs an enumeration cache, it's EnumLength will still be set to invalid, so it will have to recompute the enumeration cache. This new cache will also be valid for smaller maps since they have their own enumlength; and use this to loop over the cache. If the EnumLength is still invalid, but there is already a cache present that is big enough; we just initialize the EnumLength field for the map.
When we apply ClearNonLiveTransitions and descriptor ownership is passed back to a parent map, the descriptor array is trimmed in-place and resorted. At the same time, the enumeration cache is trimmed in-place.
Only transition arrays contain descriptor arrays. If we transition to a map and pass ownership of the descriptor array along, the child map will not store the descriptor array it owns. Rather its parent will keep the pointer. So for every leaf-map, we find the descriptor array by following the back pointer, reading out the transition array, and fetching the descriptor array from the JSGlobalPropertyCell. If a map has a transition array, we fetch it from there. If a map has undefined as its back-pointer and has no transition array; it is considered to have an empty descriptor array.
When we modify properties, we cannot share the descriptor array. To accommodate this, the child map will get its own transition array; even if there are not necessarily any transitions leaving from the child map. This is necessary since it's the only way to store its own descriptor array.
Review URL: https://chromiumcodereview.appspot.com/10909007
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-09-12 16:43:57 +00:00
|
|
|
bool IsSortedNoDuplicates(int valid_entries = -1);
|
2012-07-05 13:54:20 +00:00
|
|
|
bool IsConsistentWithBackPointers(Map* current_map);
|
|
|
|
bool IsEqualTo(TransitionArray* other);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// The maximum number of transitions we want in a transition array (should
|
|
|
|
// fit in a page).
|
|
|
|
static const int kMaxNumberOfTransitions = 1024 + 512;
|
|
|
|
|
2014-11-03 16:44:58 +00:00
|
|
|
// Returns the fixed array length required to hold number_of_transitions
|
|
|
|
// transitions.
|
|
|
|
static int LengthFor(int number_of_transitions) {
|
|
|
|
return ToKeyIndex(number_of_transitions);
|
|
|
|
}
|
|
|
|
|
2012-07-05 13:54:20 +00:00
|
|
|
private:
|
|
|
|
// Conversion from transition number to array indices.
|
|
|
|
static int ToKeyIndex(int transition_number) {
|
|
|
|
return kFirstIndex +
|
|
|
|
(transition_number * kTransitionSize) +
|
|
|
|
kTransitionKey;
|
|
|
|
}
|
|
|
|
|
2012-07-16 14:02:50 +00:00
|
|
|
static int ToTargetIndex(int transition_number) {
|
2012-07-05 13:54:20 +00:00
|
|
|
return kFirstIndex +
|
|
|
|
(transition_number * kTransitionSize) +
|
2012-07-16 14:02:50 +00:00
|
|
|
kTransitionTarget;
|
2012-07-05 13:54:20 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 12:13:53 +00:00
|
|
|
static Handle<TransitionArray> AllocateSimple(
|
|
|
|
Isolate* isolate, Handle<Map> target);
|
|
|
|
|
|
|
|
// Allocate a new transition array with a single entry.
|
|
|
|
static Handle<TransitionArray> NewWith(Handle<Map> map,
|
|
|
|
Handle<Name> name,
|
|
|
|
Handle<Map> target,
|
|
|
|
SimpleTransitionFlag flag);
|
|
|
|
|
2012-09-14 15:10:31 +00:00
|
|
|
inline void NoIncrementalWriteBarrierSet(int transition_number,
|
2013-03-04 15:00:57 +00:00
|
|
|
Name* key,
|
2012-09-14 15:10:31 +00:00
|
|
|
Map* target);
|
2012-07-05 13:54:20 +00:00
|
|
|
|
2014-04-11 12:13:53 +00:00
|
|
|
// Copy a single transition from the origin array.
|
|
|
|
inline void NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin,
|
|
|
|
int origin_transition,
|
|
|
|
int target_transition);
|
|
|
|
|
2012-07-05 13:54:20 +00:00
|
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} } // namespace v8::internal
|
|
|
|
|
|
|
|
#endif // V8_TRANSITIONS_H_
|