// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/objects/literal-objects.h" #include "src/ast/ast.h" #include "src/base/logging.h" #include "src/builtins/accessors.h" #include "src/common/globals.h" #include "src/execution/isolate.h" #include "src/heap/factory.h" #include "src/heap/local-factory-inl.h" #include "src/objects/dictionary.h" #include "src/objects/hash-table-inl.h" #include "src/objects/js-regexp.h" #include "src/objects/literal-objects-inl.h" #include "src/objects/objects-inl.h" #include "src/objects/smi.h" #include "src/objects/struct-inl.h" namespace v8 { namespace internal { namespace { // The enumeration order index in the property details is unused if they are // stored in a OrderedNameDictionary or NumberDictionary (because they handle // propery ordering differently). We then use this dummy value instead. constexpr int kDummyEnumerationIndex = 0; inline int EncodeComputedEntry(ClassBoilerplate::ValueKind value_kind, unsigned key_index) { using Flags = ClassBoilerplate::ComputedEntryFlags; int flags = Flags::ValueKindBits::encode(value_kind) | Flags::KeyIndexBits::encode(key_index); return flags; } template <typename LocalIsolate> void AddToDescriptorArrayTemplate( LocalIsolate* isolate, Handle<DescriptorArray> descriptor_array_template, Handle<Name> name, ClassBoilerplate::ValueKind value_kind, Handle<Object> value) { InternalIndex entry = descriptor_array_template->Search( *name, descriptor_array_template->number_of_descriptors()); // TODO(ishell): deduplicate properties at AST level, this will allow us to // avoid creation of closures that will be overwritten anyway. if (entry.is_not_found()) { // Entry not found, add new one. Descriptor d; if (value_kind == ClassBoilerplate::kData) { d = Descriptor::DataConstant(name, value, DONT_ENUM); } else { DCHECK(value_kind == ClassBoilerplate::kGetter || value_kind == ClassBoilerplate::kSetter); Handle<AccessorPair> pair = isolate->factory()->NewAccessorPair(); pair->set(value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER : ACCESSOR_SETTER, *value); d = Descriptor::AccessorConstant(name, pair, DONT_ENUM); } descriptor_array_template->Append(&d); } else { // Entry found, update it. int sorted_index = descriptor_array_template->GetDetails(entry).pointer(); if (value_kind == ClassBoilerplate::kData) { Descriptor d = Descriptor::DataConstant(name, value, DONT_ENUM); d.SetSortedKeyIndex(sorted_index); descriptor_array_template->Set(entry, &d); } else { DCHECK(value_kind == ClassBoilerplate::kGetter || value_kind == ClassBoilerplate::kSetter); Object raw_accessor = descriptor_array_template->GetStrongValue(entry); AccessorPair pair; if (raw_accessor.IsAccessorPair()) { pair = AccessorPair::cast(raw_accessor); } else { Handle<AccessorPair> new_pair = isolate->factory()->NewAccessorPair(); Descriptor d = Descriptor::AccessorConstant(name, new_pair, DONT_ENUM); d.SetSortedKeyIndex(sorted_index); descriptor_array_template->Set(entry, &d); pair = *new_pair; } pair.set(value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER : ACCESSOR_SETTER, *value); } } } template <typename LocalIsolate> Handle<NameDictionary> DictionaryAddNoUpdateNextEnumerationIndex( LocalIsolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name, Handle<Object> value, PropertyDetails details, InternalIndex* entry_out = nullptr) { return NameDictionary::AddNoUpdateNextEnumerationIndex( isolate, dictionary, name, value, details, entry_out); } template <typename LocalIsolate> Handle<OrderedNameDictionary> DictionaryAddNoUpdateNextEnumerationIndex( LocalIsolate* isolate, Handle<OrderedNameDictionary> dictionary, Handle<Name> name, Handle<Object> value, PropertyDetails details, InternalIndex* entry_out = nullptr) { // OrderedNameDictionary does not maintain the enumeration order in property // details, so it's a normal Add(). return OrderedNameDictionary::Add(isolate, dictionary, name, value, details) .ToHandleChecked(); } template <typename LocalIsolate> Handle<NumberDictionary> DictionaryAddNoUpdateNextEnumerationIndex( LocalIsolate* isolate, Handle<NumberDictionary> dictionary, uint32_t element, Handle<Object> value, PropertyDetails details, InternalIndex* entry_out = nullptr) { // NumberDictionary does not maintain the enumeration order, so it's // a normal Add(). return NumberDictionary::Add(isolate, dictionary, element, value, details, entry_out); } template <typename Dictionary> void DictionaryUpdateMaxNumberKey(Handle<Dictionary> dictionary, Handle<Name> name) { STATIC_ASSERT((std::is_same<Dictionary, OrderedNameDictionary>::value || std::is_same<Dictionary, NameDictionary>::value)); // No-op for (ordered) name dictionaries. } void DictionaryUpdateMaxNumberKey(Handle<NumberDictionary> dictionary, uint32_t element) { dictionary->UpdateMaxNumberKey(element, Handle<JSObject>()); dictionary->set_requires_slow_elements(); } constexpr int ComputeEnumerationIndex(int value_index) { // We "shift" value indices to ensure that the enumeration index for the value // will not overlap with minimum properties set for both class and prototype // objects. return value_index + std::max({ClassBoilerplate::kMinimumClassPropertiesCount, ClassBoilerplate::kMinimumPrototypePropertiesCount}); } constexpr int kAccessorNotDefined = -1; inline int GetExistingValueIndex(Object value) { return value.IsSmi() ? Smi::ToInt(value) : kAccessorNotDefined; } template <typename LocalIsolate, typename Dictionary, typename Key> void AddToDictionaryTemplate(LocalIsolate* isolate, Handle<Dictionary> dictionary, Key key, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value) { InternalIndex entry = dictionary->FindEntry(isolate, key); const bool is_elements_dictionary = std::is_same<Dictionary, NumberDictionary>::value; STATIC_ASSERT(is_elements_dictionary != (std::is_same<Dictionary, NameDictionary>::value || std::is_same<Dictionary, OrderedNameDictionary>::value)); if (entry.is_not_found()) { // Entry not found, add new one. int enum_order = Dictionary::kIsOrderedDictionaryType || is_elements_dictionary ? kDummyEnumerationIndex : ComputeEnumerationIndex(key_index); Handle<Object> value_handle; PropertyDetails details( value_kind != ClassBoilerplate::kData ? kAccessor : kData, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order); if (value_kind == ClassBoilerplate::kData) { value_handle = handle(value, isolate); } else { AccessorComponent component = value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER : ACCESSOR_SETTER; Handle<AccessorPair> pair(isolate->factory()->NewAccessorPair()); pair->set(component, value); value_handle = pair; } // Add value to the dictionary without updating next enumeration index. Handle<Dictionary> dict = DictionaryAddNoUpdateNextEnumerationIndex( isolate, dictionary, key, value_handle, details, &entry); // It is crucial to avoid dictionary reallocations because it may remove // potential gaps in enumeration indices values that are necessary for // inserting computed properties into right places in the enumeration order. CHECK_EQ(*dict, *dictionary); DictionaryUpdateMaxNumberKey(dictionary, key); } else { // Entry found, update it. int enum_order_existing = Dictionary::kIsOrderedDictionaryType ? kDummyEnumerationIndex : dictionary->DetailsAt(entry).dictionary_index(); int enum_order_computed = Dictionary::kIsOrderedDictionaryType || is_elements_dictionary ? kDummyEnumerationIndex : ComputeEnumerationIndex(key_index); Object existing_value = dictionary->ValueAt(entry); if (value_kind == ClassBoilerplate::kData) { // Computed value is a normal method. if (existing_value.IsAccessorPair()) { AccessorPair current_pair = AccessorPair::cast(existing_value); int existing_getter_index = GetExistingValueIndex(current_pair.getter()); int existing_setter_index = GetExistingValueIndex(current_pair.setter()); // At least one of the accessors must already be defined. STATIC_ASSERT(kAccessorNotDefined < 0); DCHECK(existing_getter_index >= 0 || existing_setter_index >= 0); if (existing_getter_index < key_index && existing_setter_index < key_index) { // Either both getter and setter were defined before the computed // method or just one of them was defined before while the other one // was not defined yet, so overwrite property to kData. PropertyDetails details( kData, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order_existing); dictionary->DetailsAtPut(entry, details); dictionary->ValueAtPut(entry, value); } else if (existing_getter_index != kAccessorNotDefined && existing_getter_index < key_index) { DCHECK_LT(key_index, existing_setter_index); // Getter was defined and it was done before the computed method // and then it was overwritten by the current computed method which // in turn was later overwritten by the setter method. So we clear // the getter. current_pair.set_getter(*isolate->factory()->null_value()); } else if (existing_setter_index != kAccessorNotDefined && existing_setter_index < key_index) { DCHECK_LT(key_index, existing_getter_index); // Setter was defined and it was done before the computed method // and then it was overwritten by the current computed method which // in turn was later overwritten by the getter method. So we clear // the setter. current_pair.set_setter(*isolate->factory()->null_value()); } else { // One of the following cases holds: // The computed method was defined before ... // 1.) the getter and setter, both of which are defined, // 2.) the getter, and the setter isn't defined, // 3.) the setter, and the getter isn't defined. // Therefore, the computed value is overwritten, receiving the // computed property's enum index. DCHECK(key_index < existing_getter_index || existing_getter_index == kAccessorNotDefined); DCHECK(key_index < existing_setter_index || existing_setter_index == kAccessorNotDefined); DCHECK(existing_getter_index != kAccessorNotDefined || existing_setter_index != kAccessorNotDefined); if (!is_elements_dictionary) { // The enum index is unused by elements dictionaries, // which is why we don't need to update the property details if // |is_elements_dictionary| holds. PropertyDetails details = dictionary->DetailsAt(entry); details = details.set_index(enum_order_computed); dictionary->DetailsAtPut(entry, details); } } } else { // if (existing_value.IsAccessorPair()) ends here DCHECK(value_kind == ClassBoilerplate::kData); DCHECK_IMPLIES(!existing_value.IsSmi(), existing_value.IsAccessorInfo()); DCHECK_IMPLIES(!existing_value.IsSmi(), AccessorInfo::cast(existing_value).name() == *isolate->factory()->length_string()); if (!existing_value.IsSmi() || Smi::ToInt(existing_value) < key_index) { // Overwrite existing value because it was defined before the computed // one (AccessorInfo "length" property is always defined before). PropertyDetails details( kData, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order_existing); dictionary->DetailsAtPut(entry, details); dictionary->ValueAtPut(entry, value); } else { // The computed value appears before the existing one. Set the // existing entry's enum index to that of the computed one. if (!is_elements_dictionary) { // The enum index is unused by elements dictionaries, // which is why we don't need to update the property details if // |is_elements_dictionary| holds. PropertyDetails details( kData, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order_computed); dictionary->DetailsAtPut(entry, details); } } } } else { // if (value_kind == ClassBoilerplate::kData) ends here AccessorComponent component = value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER : ACCESSOR_SETTER; if (existing_value.IsAccessorPair()) { // Update respective component of existing AccessorPair. AccessorPair current_pair = AccessorPair::cast(existing_value); int existing_component_index = GetExistingValueIndex(current_pair.get(component)); if (existing_component_index < key_index) { current_pair.set(component, value); } else { // The existing accessor property overwrites the computed one, update // its enumeration order accordingly. if (!is_elements_dictionary) { // The enum index is unused by elements dictionaries, // which is why we don't need to update the property details if // |is_elements_dictionary| holds. PropertyDetails details( kAccessor, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order_computed); dictionary->DetailsAtPut(entry, details); } } } else { DCHECK(!existing_value.IsAccessorPair()); DCHECK(value_kind != ClassBoilerplate::kData); if (!existing_value.IsSmi() || Smi::ToInt(existing_value) < key_index) { // Overwrite the existing data property because it was defined before // the computed accessor property. Handle<AccessorPair> pair(isolate->factory()->NewAccessorPair()); pair->set(component, value); PropertyDetails details( kAccessor, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order_existing); dictionary->DetailsAtPut(entry, details); dictionary->ValueAtPut(entry, *pair); } else { // The computed accessor property appears before the existing data // property. Set the existing entry's enum index to that of the // computed one. if (!is_elements_dictionary) { // The enum index is unused by elements dictionaries, // which is why we don't need to update the property details if // |is_elements_dictionary| holds. PropertyDetails details( kData, DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order_computed); dictionary->DetailsAtPut(entry, details); } } } } } } } // namespace // Helper class that eases building of a properties, elements and computed // properties templates. template <typename LocalIsolate> class ObjectDescriptor { public: void IncComputedCount() { ++computed_count_; } void IncPropertiesCount() { ++property_count_; } void IncElementsCount() { ++element_count_; } explicit ObjectDescriptor(int property_slack) : property_slack_(property_slack) {} bool HasDictionaryProperties() const { return computed_count_ > 0 || (property_count_ + property_slack_) > kMaxNumberOfDescriptors; } Handle<Object> properties_template() const { return HasDictionaryProperties() ? properties_dictionary_template_ : Handle<Object>::cast(descriptor_array_template_); } Handle<NumberDictionary> elements_template() const { return elements_dictionary_template_; } Handle<FixedArray> computed_properties() const { return computed_properties_; } void CreateTemplates(LocalIsolate* isolate) { auto* factory = isolate->factory(); descriptor_array_template_ = factory->empty_descriptor_array(); if (V8_DICT_MODE_PROTOTYPES_BOOL) { properties_dictionary_template_ = factory->empty_ordered_property_dictionary(); } else { properties_dictionary_template_ = factory->empty_property_dictionary(); } if (property_count_ || computed_count_ || property_slack_) { if (HasDictionaryProperties()) { int need_space_for = property_count_ + computed_count_ + property_slack_; if (V8_DICT_MODE_PROTOTYPES_BOOL) { properties_dictionary_template_ = OrderedNameDictionary::Allocate(isolate, need_space_for, AllocationType::kOld) .ToHandleChecked(); } else { properties_dictionary_template_ = NameDictionary::New( isolate, need_space_for, AllocationType::kOld); } } else { descriptor_array_template_ = DescriptorArray::Allocate( isolate, 0, property_count_ + property_slack_, AllocationType::kOld); } } elements_dictionary_template_ = element_count_ || computed_count_ ? NumberDictionary::New(isolate, element_count_ + computed_count_, AllocationType::kOld) : factory->empty_slow_element_dictionary(); computed_properties_ = computed_count_ ? factory->NewFixedArray(computed_count_, AllocationType::kOld) : factory->empty_fixed_array(); temp_handle_ = handle(Smi::zero(), isolate); } void AddConstant(LocalIsolate* isolate, Handle<Name> name, Handle<Object> value, PropertyAttributes attribs) { bool is_accessor = value->IsAccessorInfo(); DCHECK(!value->IsAccessorPair()); if (HasDictionaryProperties()) { PropertyKind kind = is_accessor ? i::kAccessor : i::kData; int enum_order = V8_DICT_MODE_PROTOTYPES_BOOL ? kDummyEnumerationIndex : next_enumeration_index_++; PropertyDetails details(kind, attribs, PropertyCellType::kNoCell, enum_order); if (V8_DICT_MODE_PROTOTYPES_BOOL) { properties_dictionary_template_ = DictionaryAddNoUpdateNextEnumerationIndex( isolate, properties_ordered_dictionary_template(), name, value, details); } else { properties_dictionary_template_ = DictionaryAddNoUpdateNextEnumerationIndex( isolate, properties_dictionary_template(), name, value, details); } } else { Descriptor d = is_accessor ? Descriptor::AccessorConstant(name, value, attribs) : Descriptor::DataConstant(name, value, attribs); descriptor_array_template_->Append(&d); } } void AddNamedProperty(LocalIsolate* isolate, Handle<Name> name, ClassBoilerplate::ValueKind value_kind, int value_index) { Smi value = Smi::FromInt(value_index); if (HasDictionaryProperties()) { UpdateNextEnumerationIndex(value_index); if (V8_DICT_MODE_PROTOTYPES_BOOL) { AddToDictionaryTemplate(isolate, properties_ordered_dictionary_template(), name, value_index, value_kind, value); } else { AddToDictionaryTemplate(isolate, properties_dictionary_template(), name, value_index, value_kind, value); } } else { temp_handle_.PatchValue(value); AddToDescriptorArrayTemplate(isolate, descriptor_array_template_, name, value_kind, temp_handle_); } } void AddIndexedProperty(LocalIsolate* isolate, uint32_t element, ClassBoilerplate::ValueKind value_kind, int value_index) { Smi value = Smi::FromInt(value_index); AddToDictionaryTemplate(isolate, elements_dictionary_template_, element, value_index, value_kind, value); } void AddComputed(ClassBoilerplate::ValueKind value_kind, int key_index) { int value_index = key_index + 1; UpdateNextEnumerationIndex(value_index); int flags = EncodeComputedEntry(value_kind, key_index); computed_properties_->set(current_computed_index_++, Smi::FromInt(flags)); } void UpdateNextEnumerationIndex(int value_index) { int current_index = ComputeEnumerationIndex(value_index); DCHECK_LE(next_enumeration_index_, current_index); next_enumeration_index_ = current_index + 1; } void Finalize(LocalIsolate* isolate) { if (HasDictionaryProperties()) { DCHECK_EQ(current_computed_index_, computed_properties_->length()); if (!V8_DICT_MODE_PROTOTYPES_BOOL) { properties_dictionary_template()->set_next_enumeration_index( next_enumeration_index_); } } else { DCHECK(descriptor_array_template_->IsSortedNoDuplicates()); } } private: Handle<NameDictionary> properties_dictionary_template() const { return Handle<NameDictionary>::cast(properties_dictionary_template_); } Handle<OrderedNameDictionary> properties_ordered_dictionary_template() const { return Handle<OrderedNameDictionary>::cast(properties_dictionary_template_); } const int property_slack_; int property_count_ = 0; int next_enumeration_index_ = PropertyDetails::kInitialIndex; int element_count_ = 0; int computed_count_ = 0; int current_computed_index_ = 0; Handle<DescriptorArray> descriptor_array_template_; // Is either a NameDictionary or OrderedNameDictionary. Handle<HeapObject> properties_dictionary_template_; Handle<NumberDictionary> elements_dictionary_template_; Handle<FixedArray> computed_properties_; // This temporary handle is used for storing to descriptor array. Handle<Object> temp_handle_; }; template <typename LocalIsolate, typename PropertyDict> void ClassBoilerplate::AddToPropertiesTemplate( LocalIsolate* isolate, Handle<PropertyDict> dictionary, Handle<Name> name, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value) { AddToDictionaryTemplate(isolate, dictionary, name, key_index, value_kind, value); } template void ClassBoilerplate::AddToPropertiesTemplate( Isolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value); template void ClassBoilerplate::AddToPropertiesTemplate( LocalIsolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value); template void ClassBoilerplate::AddToPropertiesTemplate( Isolate* isolate, Handle<OrderedNameDictionary> dictionary, Handle<Name> name, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value); template <typename LocalIsolate> void ClassBoilerplate::AddToElementsTemplate( LocalIsolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value) { AddToDictionaryTemplate(isolate, dictionary, key, key_index, value_kind, value); } template void ClassBoilerplate::AddToElementsTemplate( Isolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value); template void ClassBoilerplate::AddToElementsTemplate( LocalIsolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key, int key_index, ClassBoilerplate::ValueKind value_kind, Smi value); template <typename LocalIsolate> Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate( LocalIsolate* isolate, ClassLiteral* expr) { // Create a non-caching handle scope to ensure that the temporary handle used // by ObjectDescriptor for passing Smis around does not corrupt handle cache // in CanonicalHandleScope. typename LocalIsolate::HandleScopeType scope(isolate); auto* factory = isolate->factory(); ObjectDescriptor<LocalIsolate> static_desc(kMinimumClassPropertiesCount); ObjectDescriptor<LocalIsolate> instance_desc( kMinimumPrototypePropertiesCount); for (int i = 0; i < expr->public_members()->length(); i++) { ClassLiteral::Property* property = expr->public_members()->at(i); ObjectDescriptor<LocalIsolate>& desc = property->is_static() ? static_desc : instance_desc; if (property->is_computed_name()) { if (property->kind() != ClassLiteral::Property::FIELD) { desc.IncComputedCount(); } } else { if (property->key()->AsLiteral()->IsPropertyName()) { desc.IncPropertiesCount(); } else { desc.IncElementsCount(); } } } // // Initialize class object template. // static_desc.CreateTemplates(isolate); STATIC_ASSERT(JSFunction::kLengthDescriptorIndex == 0); { // Add length_accessor. PropertyAttributes attribs = static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY); static_desc.AddConstant(isolate, factory->length_string(), factory->function_length_accessor(), attribs); } { // Add prototype_accessor. PropertyAttributes attribs = static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); static_desc.AddConstant(isolate, factory->prototype_string(), factory->function_prototype_accessor(), attribs); } { Handle<ClassPositions> class_positions = factory->NewClassPositions( expr->start_position(), expr->end_position()); static_desc.AddConstant(isolate, factory->class_positions_symbol(), class_positions, DONT_ENUM); } // // Initialize prototype object template. // instance_desc.CreateTemplates(isolate); { Handle<Object> value( Smi::FromInt(ClassBoilerplate::kConstructorArgumentIndex), isolate); instance_desc.AddConstant(isolate, factory->constructor_string(), value, DONT_ENUM); } // // Fill in class boilerplate. // int dynamic_argument_index = ClassBoilerplate::kFirstDynamicArgumentIndex; for (int i = 0; i < expr->public_members()->length(); i++) { ClassLiteral::Property* property = expr->public_members()->at(i); ClassBoilerplate::ValueKind value_kind; switch (property->kind()) { case ClassLiteral::Property::METHOD: value_kind = ClassBoilerplate::kData; break; case ClassLiteral::Property::GETTER: value_kind = ClassBoilerplate::kGetter; break; case ClassLiteral::Property::SETTER: value_kind = ClassBoilerplate::kSetter; break; case ClassLiteral::Property::FIELD: DCHECK_IMPLIES(property->is_computed_name(), !property->is_private()); if (property->is_computed_name()) { ++dynamic_argument_index; } continue; } ObjectDescriptor<LocalIsolate>& desc = property->is_static() ? static_desc : instance_desc; if (property->is_computed_name()) { int computed_name_index = dynamic_argument_index; dynamic_argument_index += 2; // Computed name and value indices. desc.AddComputed(value_kind, computed_name_index); continue; } int value_index = dynamic_argument_index++; Literal* key_literal = property->key()->AsLiteral(); uint32_t index; if (key_literal->AsArrayIndex(&index)) { desc.AddIndexedProperty(isolate, index, value_kind, value_index); } else { Handle<String> name = key_literal->AsRawPropertyName()->string(); DCHECK(name->IsInternalizedString()); desc.AddNamedProperty(isolate, name, value_kind, value_index); } } // All classes, even anonymous ones, have a name accessor. If static_desc is // in dictionary mode, the name accessor is installed at runtime in // DefineClass. if (!expr->has_name_static_property() && !static_desc.HasDictionaryProperties()) { // Set class name accessor if the "name" method was not added yet. PropertyAttributes attribs = static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY); static_desc.AddConstant(isolate, factory->name_string(), factory->function_name_accessor(), attribs); } static_desc.Finalize(isolate); instance_desc.Finalize(isolate); Handle<ClassBoilerplate> class_boilerplate = Handle<ClassBoilerplate>::cast( factory->NewFixedArray(kBoilerplateLength, AllocationType::kOld)); class_boilerplate->set_arguments_count(dynamic_argument_index); class_boilerplate->set_static_properties_template( *static_desc.properties_template()); class_boilerplate->set_static_elements_template( *static_desc.elements_template()); class_boilerplate->set_static_computed_properties( *static_desc.computed_properties()); class_boilerplate->set_instance_properties_template( *instance_desc.properties_template()); class_boilerplate->set_instance_elements_template( *instance_desc.elements_template()); class_boilerplate->set_instance_computed_properties( *instance_desc.computed_properties()); return scope.CloseAndEscape(class_boilerplate); } template Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate( Isolate* isolate, ClassLiteral* expr); template Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate( LocalIsolate* isolate, ClassLiteral* expr); void ArrayBoilerplateDescription::BriefPrintDetails(std::ostream& os) { os << " " << ElementsKindToString(elements_kind()) << ", " << Brief(constant_elements()); } void RegExpBoilerplateDescription::BriefPrintDetails(std::ostream& os) { // Note: keep boilerplate layout synced with JSRegExp layout. STATIC_ASSERT(JSRegExp::kDataOffset == JSObject::kHeaderSize); STATIC_ASSERT(JSRegExp::kSourceOffset == JSRegExp::kDataOffset + kTaggedSize); STATIC_ASSERT(JSRegExp::kFlagsOffset == JSRegExp::kSourceOffset + kTaggedSize); STATIC_ASSERT(JSRegExp::kHeaderSize == JSRegExp::kFlagsOffset + kTaggedSize); os << " " << Brief(data()) << ", " << Brief(source()) << ", " << flags(); } } // namespace internal } // namespace v8