static const int kNodeIsIndependentShift = 3;
static const int kNodeIsPartiallyDependentShift = 4;
- static const int kJSObjectType = 0xbd;
+ static const int kJSObjectType = 0xbe;
static const int kFirstNonstringType = 0x80;
static const int kOddballType = 0x83;
static const int kForeignType = 0x87;
ApiNatives::GlobalObjectType);
}
+ js_global_object_function->initial_map()->set_is_prototype_map(true);
js_global_object_function->initial_map()->set_is_hidden_prototype();
js_global_object_function->initial_map()->set_dictionary_map(true);
Handle<GlobalObject> global_object =
}
+Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
+ Handle<PrototypeInfo> result =
+ Handle<PrototypeInfo>::cast(NewStruct(PROTOTYPE_INFO_TYPE));
+ result->set_prototype_users(WeakFixedArray::Empty());
+ result->set_validity_cell(Smi::FromInt(0));
+ return result;
+}
+
+
Handle<Oddball> Factory::NewOddball(Handle<Map> map,
const char* to_string,
Handle<Object> to_number,
// Create a new boxed value.
Handle<Box> NewBox(Handle<Object> value);
+ // Create a new PrototypeInfo struct.
+ Handle<PrototypeInfo> NewPrototypeInfo();
+
// Create a pre-tenured empty AccessorPair.
Handle<AccessorPair> NewAccessorPair();
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
- Object* raw_transitions = map->raw_transitions();
- if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
+ Object* raw_transitions_or_prototype_info = map->raw_transitions();
+ if (TransitionArray::IsFullTransitionArray(
+ raw_transitions_or_prototype_info)) {
+ TransitionArray* transitions =
+ TransitionArray::cast(raw_transitions_or_prototype_info);
int transitions_entry = GetEntry(transitions)->index();
if (FLAG_collect_maps && map->CanTransition()) {
TagObject(transitions, "(transition array)");
SetInternalReference(map, entry, "transitions", transitions,
- Map::kTransitionsOffset);
- } else if (TransitionArray::IsSimpleTransition(raw_transitions)) {
- TagObject(raw_transitions, "(transition)");
- SetInternalReference(map, entry, "transition", raw_transitions,
- Map::kTransitionsOffset);
+ Map::kTransitionsOrPrototypeInfoOffset);
+ } else if (TransitionArray::IsSimpleTransition(
+ raw_transitions_or_prototype_info)) {
+ TagObject(raw_transitions_or_prototype_info, "(transition)");
+ SetInternalReference(map, entry, "transition",
+ raw_transitions_or_prototype_info,
+ Map::kTransitionsOrPrototypeInfoOffset);
+ } else if (map->is_prototype_map()) {
+ TagObject(raw_transitions_or_prototype_info, "prototype_info");
+ SetInternalReference(map, entry, "prototype_info",
+ raw_transitions_or_prototype_info,
+ Map::kTransitionsOrPrototypeInfoOffset);
}
DescriptorArray* descriptors = map->instance_descriptors();
TagObject(descriptors, "(map descriptors)");
V(frozen_symbol) \
V(nonexistent_symbol) \
V(elements_transition_symbol) \
- V(prototype_users_symbol) \
V(observed_symbol) \
V(uninitialized_symbol) \
V(megamorphic_symbol) \
void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
Map* map) {
Object* raw_transitions = map->raw_transitions();
- if (TransitionArray::IsSimpleTransition(raw_transitions)) {
- StaticVisitor::VisitPointer(
- heap, HeapObject::RawField(map, Map::kTransitionsOffset));
- }
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
MarkTransitionArray(heap, TransitionArray::cast(raw_transitions));
}
bool Isolate::IsInternallyUsedPropertyName(Handle<Object> name) {
- return name.is_identical_to(factory()->hidden_string()) ||
- name.is_identical_to(factory()->prototype_users_symbol());
+ return name.is_identical_to(factory()->hidden_string());
}
bool Isolate::IsInternallyUsedPropertyName(Object* name) {
- return name == heap()->hidden_string() ||
- name == heap()->prototype_users_symbol();
+ return name == heap()->hidden_string();
}
}
+void PrototypeInfo::PrototypeInfoVerify() {
+ CHECK(IsPrototypeInfo());
+ if (prototype_users()->IsWeakFixedArray()) {
+ WeakFixedArray::cast(prototype_users())->FixedArrayVerify();
+ } else {
+ CHECK(prototype_users()->IsSmi());
+ }
+ CHECK(validity_cell()->IsCell() || validity_cell()->IsSmi());
+}
+
+
void AccessorInfo::AccessorInfoVerify() {
VerifyPointer(name());
VerifyPointer(flag());
}
-byte Map::bit_field() {
- return READ_BYTE_FIELD(this, kBitFieldOffset);
-}
+byte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }
void Map::set_bit_field(byte value) {
}
-byte Map::bit_field2() {
- return READ_BYTE_FIELD(this, kBitField2Offset);
-}
+byte Map::bit_field2() const { return READ_BYTE_FIELD(this, kBitField2Offset); }
void Map::set_bit_field2(byte value) {
set_bit_field2(IsPrototypeMapBits::update(bit_field2(), value));
}
-bool Map::is_prototype_map() {
+bool Map::is_prototype_map() const {
return IsPrototypeMapBits::decode(bit_field2());
}
}
-uint32_t Map::bit_field3() {
+uint32_t Map::bit_field3() const {
return READ_UINT32_FIELD(this, kBitField3Offset);
}
}
-ACCESSORS(Map, raw_transitions, Object, kTransitionsOffset)
+ACCESSORS(Map, raw_transitions, Object, kTransitionsOrPrototypeInfoOffset)
+
+
+Object* Map::prototype_info() const {
+ DCHECK(is_prototype_map());
+ return READ_FIELD(this, Map::kTransitionsOrPrototypeInfoOffset);
+}
+
+
+void Map::set_prototype_info(Object* value, WriteBarrierMode mode) {
+ DCHECK(is_prototype_map());
+ WRITE_FIELD(this, Map::kTransitionsOrPrototypeInfoOffset, value);
+ CONDITIONAL_WRITE_BARRIER(
+ GetHeap(), this, Map::kTransitionsOrPrototypeInfoOffset, value, mode);
+}
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
ACCESSORS(Box, value, Object, kValueOffset)
+ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset)
+ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
+
ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
os << " - unused property fields: " << unused_property_fields() << "\n";
if (is_deprecated()) os << " - deprecated_map\n";
if (is_dictionary_map()) os << " - dictionary_map\n";
- if (is_prototype_map()) os << " - prototype_map\n";
+ if (is_prototype_map()) {
+ os << " - prototype_map\n";
+ os << " - prototype info: " << Brief(prototype_info());
+ } else {
+ os << " - back pointer: " << Brief(GetBackPointer());
+ }
if (is_hidden_prototype()) os << " - hidden_prototype\n";
if (has_named_interceptor()) os << " - named_interceptor\n";
if (has_indexed_interceptor()) os << " - indexed_interceptor\n";
if (is_access_check_needed()) os << " - access_check_needed\n";
if (!is_extensible()) os << " - non-extensible\n";
if (is_observed()) os << " - observed\n";
- os << " - back pointer: " << Brief(GetBackPointer());
os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
<< "#" << NumberOfOwnDescriptors() << ": "
<< Brief(instance_descriptors());
}
+void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) { // NOLINT
+ HeapObject::PrintHeader(os, "PrototypeInfo");
+ os << "\n - prototype users: " << Brief(prototype_users());
+ os << "\n - validity cell: " << Brief(validity_cell());
+ os << "\n";
+}
+
+
void AccessorPair::AccessorPairPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "AccessorPair");
os << "\n - getter: " << Brief(getter());
}
-void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
+void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
+ int expected_additional_properties) {
if (object->map() == *new_map) return;
+ Handle<Map> old_map(object->map());
if (object->HasFastProperties()) {
if (!new_map->is_dictionary_map()) {
- Handle<Map> old_map(object->map());
MigrateFastToFast(object, new_map);
if (old_map->is_prototype_map()) {
// Clear out the old descriptor array to avoid problems to sharing
DCHECK(new_map->GetBackPointer()->IsUndefined());
}
} else {
- MigrateFastToSlow(object, new_map, 0);
+ MigrateFastToSlow(object, new_map, expected_additional_properties);
}
} else {
- // For slow-to-fast migrations JSObject::TransformToFastProperties()
+ // For slow-to-fast migrations JSObject::MigrateSlowToFast()
// must be used instead.
CHECK(new_map->is_dictionary_map());
// Slow-to-slow migration is trivial.
object->set_map(*new_map);
}
+ if (old_map->is_prototype_map()) {
+ new_map->set_prototype_info(old_map->prototype_info());
+ old_map->set_prototype_info(Smi::FromInt(0));
+ }
}
Handle<Map> map(object->map());
Handle<Map> new_map = Map::Normalize(map, mode, reason);
- MigrateFastToSlow(object, new_map, expected_additional_properties);
+ MigrateToMap(object, new_map, expected_additional_properties);
}
}
+// static
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
PrototypeOptimizationMode mode) {
if (object->IsGlobalObject()) return;
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
"NormalizeAsPrototype");
}
- bool has_just_copied_map = false;
+ Handle<Map> previous_map(object->map());
if (!object->HasFastProperties()) {
JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
- has_just_copied_map = true;
}
- if (mode == FAST_PROTOTYPE && object->HasFastProperties() &&
- !object->map()->is_prototype_map()) {
- if (!has_just_copied_map) {
+ if (!object->map()->is_prototype_map()) {
+ if (object->map() == *previous_map) {
Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
JSObject::MigrateToMap(object, new_map);
}
}
+// static
void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
if (!object->map()->is_prototype_map()) return;
OptimizeAsPrototype(object, FAST_PROTOTYPE);
}
+// static
void JSObject::RegisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user) {
DCHECK(FLAG_track_prototype_users);
Isolate* isolate = prototype->GetIsolate();
- Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
-
- // Get prototype users array, create it if it doesn't exist yet.
- Handle<Object> maybe_array =
- JSObject::GetProperty(prototype, symbol).ToHandleChecked();
-
- Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_array, user);
- if (!maybe_array.is_identical_to(new_array)) {
- JSObject::SetOwnPropertyIgnoreAttributes(prototype, symbol, new_array,
- DONT_ENUM).Assert();
+ if (prototype->IsJSGlobalProxy()) {
+ PrototypeIterator iter(isolate, prototype);
+ prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
+ }
+ Handle<PrototypeInfo> proto_info;
+ Object* maybe_proto_info = prototype->map()->prototype_info();
+ if (maybe_proto_info->IsPrototypeInfo()) {
+ proto_info = handle(PrototypeInfo::cast(maybe_proto_info), isolate);
+ } else {
+ proto_info = isolate->factory()->NewPrototypeInfo();
+ prototype->map()->set_prototype_info(*proto_info);
+ }
+ Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
+ Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_registry, user);
+ if (!maybe_registry.is_identical_to(new_array)) {
+ proto_info->set_prototype_users(*new_array);
}
}
+// static
void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user) {
Isolate* isolate = prototype->GetIsolate();
- Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
-
- Handle<Object> maybe_array =
- JSObject::GetProperty(prototype, symbol).ToHandleChecked();
- if (!maybe_array->IsWeakFixedArray()) return;
- Handle<WeakFixedArray>::cast(maybe_array)->Remove(user);
+ if (prototype->IsJSGlobalProxy()) {
+ PrototypeIterator iter(isolate, prototype);
+ prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
+ }
+ DCHECK(prototype->map()->is_prototype_map());
+ Object* maybe_proto_info = prototype->map()->prototype_info();
+ if (!maybe_proto_info->IsPrototypeInfo()) return;
+ Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(maybe_proto_info),
+ isolate);
+ Object* maybe_registry = proto_info->prototype_users();
+ if (!maybe_registry->IsWeakFixedArray()) return;
+ WeakFixedArray::cast(maybe_registry)->Remove(user);
}
bool Map::ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype) {
if (!FLAG_track_prototype_users) return false;
if (this->is_prototype_map()) return true;
- if (this->is_dictionary_map()) return false;
Object* back = GetBackPointer();
if (!back->IsMap()) return true;
if (Map::cast(back)->prototype() != *prototype) return true;
}
-bool Map::CanUseOptimizationsBasedOnPrototypeRegistry() {
- if (!FLAG_track_prototype_users) return false;
- if (this->is_prototype_map()) return true;
- if (GetBackPointer()->IsMap()) return true;
- return false;
-}
-
-
Handle<Object> CacheInitialJSArrayMaps(
Handle<Context> native_context, Handle<Map> initial_map) {
// Replace all of the cached initial array maps in the native context with
// - DebugInfo
// - BreakPointInfo
// - CodeCache
+// - PrototypeInfo
// - WeakCell
//
// Formats of Object*:
V(TYPE_FEEDBACK_INFO_TYPE) \
V(ALIASED_ARGUMENTS_ENTRY_TYPE) \
V(BOX_TYPE) \
+ V(PROTOTYPE_INFO_TYPE) \
\
V(FIXED_ARRAY_TYPE) \
V(FIXED_DOUBLE_ARRAY_TYPE) \
// Note that for subtle reasons related to the ordering or numerical values of
// type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST
// manually.
-#define STRUCT_LIST(V) \
- V(BOX, Box, box) \
- V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo, executable_accessor_info)\
- V(ACCESSOR_PAIR, AccessorPair, accessor_pair) \
- V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
- V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
- V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \
- V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
- V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
- V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
- V(SCRIPT, Script, script) \
- V(ALLOCATION_SITE, AllocationSite, allocation_site) \
- V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento) \
- V(CODE_CACHE, CodeCache, code_cache) \
- V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \
- V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
- V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
- V(DEBUG_INFO, DebugInfo, debug_info) \
- V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)
+#define STRUCT_LIST(V) \
+ V(BOX, Box, box) \
+ V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo, \
+ executable_accessor_info) \
+ V(ACCESSOR_PAIR, AccessorPair, accessor_pair) \
+ V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
+ V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
+ V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \
+ V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
+ V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
+ V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
+ V(SCRIPT, Script, script) \
+ V(ALLOCATION_SITE, AllocationSite, allocation_site) \
+ V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento) \
+ V(CODE_CACHE, CodeCache, code_cache) \
+ V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \
+ V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
+ V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
+ V(DEBUG_INFO, DebugInfo, debug_info) \
+ V(BREAK_POINT_INFO, BreakPointInfo, break_point_info) \
+ V(PROTOTYPE_INFO, PrototypeInfo, prototype_info)
// We use the full 8 bits of the instance_type field to encode heap object
// instance types. The high-order bit (bit 7) is set if the object is not a
SHARED_FUNCTION_INFO_TYPE,
WEAK_CELL_TYPE,
PROPERTY_CELL_TYPE,
+ PROTOTYPE_INFO_TYPE,
// All the following types are subtypes of JSReceiver, which corresponds to
// objects in the JS sense. The first and the last type in this range are
static void TransitionElementsKind(Handle<JSObject> object,
ElementsKind to_kind);
- static void MigrateToMap(Handle<JSObject> object, Handle<Map> new_map);
+ // Always use this to migrate an object to a new map.
+ // |expected_additional_properties| is only used for fast-to-slow transitions
+ // and ignored otherwise.
+ static void MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
+ int expected_additional_properties = 0);
// Convert the object to use the canonical dictionary
// representation. If the object is expected to have additional properties
inline Object* Get(int index) const;
inline int Length() const;
+ static Object* Empty() { return Smi::FromInt(0); }
+
DECLARE_CAST(WeakFixedArray)
private:
inline void set_unused_property_fields(int value);
// Bit field.
- inline byte bit_field();
+ inline byte bit_field() const;
inline void set_bit_field(byte value);
// Bit field 2.
- inline byte bit_field2();
+ inline byte bit_field2() const;
inline void set_bit_field2(byte value);
// Bit field 3.
- inline uint32_t bit_field3();
+ inline uint32_t bit_field3() const;
inline void set_bit_field3(uint32_t bits);
class EnumLengthBits: public BitField<int,
inline void set_is_extensible(bool value);
inline bool is_extensible();
inline void set_is_prototype_map(bool value);
- inline bool is_prototype_map();
+ inline bool is_prototype_map() const;
inline void set_elements_kind(ElementsKind elements_kind) {
DCHECK(static_cast<int>(elements_kind) < kElementsKindCount);
// Don't call set_raw_transitions() directly to overwrite transitions, use
// the TransitionArray::ReplaceTransitions() wrapper instead!
DECL_ACCESSORS(raw_transitions, Object)
+ // [prototype_info]: Per-prototype metadata. Aliased with transitions
+ // (which prototype maps don't have).
+ DECL_ACCESSORS(prototype_info, Object)
Map* FindRootMap();
Map* FindFieldOwner(int descriptor);
// otherwise a transition array is used.
// For prototype maps, this slot is used to store a pointer to the prototype
// object using this map.
- static const int kTransitionsOffset =
+ static const int kTransitionsOrPrototypeInfoOffset =
kConstructorOrBackPointerOffset + kPointerSize;
- static const int kDescriptorsOffset = kTransitionsOffset + kPointerSize;
+ static const int kDescriptorsOffset =
+ kTransitionsOrPrototypeInfoOffset + kPointerSize;
#if V8_DOUBLE_FIELDS_UNBOXING
static const int kLayoutDecriptorOffset = kDescriptorsOffset + kPointerSize;
static const int kCodeCacheOffset = kLayoutDecriptorOffset + kPointerSize;
};
+// Container for metadata stored on each prototype map.
+class PrototypeInfo : public Struct {
+ public:
+ // [prototype_users]: WeakFixedArray containing maps using this prototype,
+ // or Smi(0) if uninitialized.
+ DECL_ACCESSORS(prototype_users, Object)
+ // [validity_cell]: Cell containing the validity bit for prototype chains
+ // going through this object, or Smi(0) if uninitialized.
+ DECL_ACCESSORS(validity_cell, Object)
+
+ DECLARE_CAST(PrototypeInfo)
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(PrototypeInfo)
+ DECLARE_VERIFIER(PrototypeInfo)
+
+ static const int kPrototypeObjectOffset = HeapObject::kHeaderSize;
+ static const int kPrototypeUsersOffset =
+ kPrototypeObjectOffset + kPointerSize;
+ static const int kValidityCellOffset = kPrototypeUsersOffset + kPointerSize;
+ static const int kSize = kValidityCellOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PrototypeInfo);
+};
+
+
// Script describes a script which has been added to the VM.
class Script: public Struct {
public:
int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
if (CanStoreSimpleTransition(raw_transitions)) return 0;
if (IsSimpleTransition(raw_transitions)) return 1;
+ // Prototype maps don't have transitions.
+ if (raw_transitions->IsPrototypeInfo()) return 0;
DCHECK(IsFullTransitionArray(raw_transitions));
return TransitionArray::cast(raw_transitions)->number_of_transitions();
}
}
-function test(use_new, add_first, set__proto__, same_map_as) {
+function test(use_new, add_first, set__proto__) {
var proto = use_new ? new Super() : {};
// New object is fast.
// Still fast
assertTrue(%HasFastProperties(proto));
AddProps(proto);
- if (set__proto__) {
- // After we add all those properties it went slow mode again :-(
- assertFalse(%HasFastProperties(proto));
- } else {
- // .prototype keeps it fast.
- assertTrue(%HasFastProperties(proto));
- }
- }
- if (same_map_as && !add_first && set__proto__) {
- assertTrue(%HaveSameMap(same_map_as, proto));
+ // Still fast.
+ assertTrue(%HasFastProperties(proto));
}
return proto;
}
var use_new = ((i & 2) != 0);
test(use_new, true, set__proto__);
-
- var last = test(use_new, false, set__proto__);
- test(use_new, false, set__proto__, last);
+ test(use_new, false, set__proto__);
}
__f_0(__v_1, __v_6);
assertTrue(%HasFastProperties(__v_1));
__f_4(__v_1);
- assertFalse(%HasFastProperties(__v_1));
+ assertTrue(%HasFastProperties(__v_1));
}
}
gc();