blob: 84c7effcf8806a4f04410bd075d392df8f5b5735 [file] [log] [blame]
// 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 SwissNameDictionary 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;
}
constexpr AccessorComponent ToAccessorComponent(
ClassBoilerplate::ValueKind value_kind) {
return value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER
: ACCESSOR_SETTER;
}
template <typename IsolateT>
void AddToDescriptorArrayTemplate(
IsolateT* 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(ToAccessorComponent(value_kind), *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);
Tagged<Object> raw_accessor =
descriptor_array_template->GetStrongValue(entry);
Tagged<AccessorPair> pair;
if (IsAccessorPair(raw_accessor)) {
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(ToAccessorComponent(value_kind), *value, kReleaseStore);
}
}
}
template <typename IsolateT>
Handle<NameDictionary> DictionaryAddNoUpdateNextEnumerationIndex(
IsolateT* 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 IsolateT>
Handle<SwissNameDictionary> DictionaryAddNoUpdateNextEnumerationIndex(
IsolateT* isolate, Handle<SwissNameDictionary> dictionary,
Handle<Name> name, Handle<Object> value, PropertyDetails details,
InternalIndex* entry_out = nullptr) {
// SwissNameDictionary does not maintain the enumeration order in property
// details, so it's a normal Add().
return SwissNameDictionary::Add(isolate, dictionary, name, value, details);
}
template <typename IsolateT>
Handle<NumberDictionary> DictionaryAddNoUpdateNextEnumerationIndex(
IsolateT* 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, SwissNameDictionary>::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(Tagged<Object> value) {
return IsSmi(value) ? Smi::ToInt(value) : kAccessorNotDefined;
}
template <typename IsolateT, typename Dictionary, typename Key>
void AddToDictionaryTemplate(IsolateT* isolate, Handle<Dictionary> dictionary,
Key key, int key_index,
ClassBoilerplate::ValueKind value_kind,
Tagged<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, SwissNameDictionary>::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 ? PropertyKind::kAccessor
: PropertyKind::kData,
DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order);
if (value_kind == ClassBoilerplate::kData) {
value_handle = handle(value, isolate);
} else {
Handle<AccessorPair> pair(isolate->factory()->NewAccessorPair());
pair->set(ToAccessorComponent(value_kind), 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);
Tagged<Object> existing_value = dictionary->ValueAt(entry);
if (value_kind == ClassBoilerplate::kData) {
// Computed value is a normal method.
if (IsAccessorPair(existing_value)) {
Tagged<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(
PropertyKind::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(!IsSmi(existing_value), IsAccessorInfo(existing_value));
DCHECK_IMPLIES(!IsSmi(existing_value),
AccessorInfo::cast(existing_value)->name() ==
*isolate->factory()->length_string() ||
AccessorInfo::cast(existing_value)->name() ==
*isolate->factory()->name_string());
if (!IsSmi(existing_value) || Smi::ToInt(existing_value) < key_index) {
// Overwrite existing value because it was defined before the computed
// one (AccessorInfo "length" and "name" properties are always defined
// before).
PropertyDetails details(
PropertyKind::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(
PropertyKind::kData, DONT_ENUM,
PropertyDetails::kConstIfDictConstnessTracking,
enum_order_computed);
dictionary->DetailsAtPut(entry, details);
}
}
}
} else { // if (value_kind == ClassBoilerplate::kData) ends here
AccessorComponent component = ToAccessorComponent(value_kind);
if (IsAccessorPair(existing_value)) {
// Update respective component of existing AccessorPair.
Tagged<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, kReleaseStore);
} 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(
PropertyKind::kAccessor, DONT_ENUM,
PropertyDetails::kConstIfDictConstnessTracking,
enum_order_computed);
dictionary->DetailsAtPut(entry, details);
}
}
} else {
DCHECK(!IsAccessorPair(existing_value));
DCHECK(value_kind != ClassBoilerplate::kData);
if (!IsSmi(existing_value) || 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(
PropertyKind::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(
PropertyKind::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 IsolateT>
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(IsolateT* isolate) {
auto* factory = isolate->factory();
descriptor_array_template_ = factory->empty_descriptor_array();
if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
properties_dictionary_template_ =
factory->empty_swiss_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_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
properties_dictionary_template_ =
isolate->factory()->NewSwissNameDictionary(need_space_for,
AllocationType::kOld);
} 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(IsolateT* isolate, Handle<Name> name, Handle<Object> value,
PropertyAttributes attribs) {
bool is_accessor = IsAccessorInfo(*value);
DCHECK(!IsAccessorPair(*value));
if (HasDictionaryProperties()) {
PropertyKind kind =
is_accessor ? i::PropertyKind::kAccessor : i::PropertyKind::kData;
int enum_order = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
? kDummyEnumerationIndex
: next_enumeration_index_++;
PropertyDetails details(kind, attribs, PropertyCellType::kNoCell,
enum_order);
if (V8_ENABLE_SWISS_NAME_DICTIONARY_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(IsolateT* isolate, Handle<Name> name,
ClassBoilerplate::ValueKind value_kind,
int value_index) {
Tagged<Smi> value = Smi::FromInt(value_index);
if (HasDictionaryProperties()) {
UpdateNextEnumerationIndex(value_index);
if (V8_ENABLE_SWISS_NAME_DICTIONARY_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(IsolateT* isolate, uint32_t element,
ClassBoilerplate::ValueKind value_kind,
int value_index) {
Tagged<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(IsolateT* isolate) {
if (HasDictionaryProperties()) {
DCHECK_EQ(current_computed_index_, computed_properties_->length());
if (!V8_ENABLE_SWISS_NAME_DICTIONARY_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<SwissNameDictionary> properties_ordered_dictionary_template() const {
return Handle<SwissNameDictionary>::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 SwissNameDictionary.
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 IsolateT, typename PropertyDict>
void ClassBoilerplate::AddToPropertiesTemplate(
IsolateT* isolate, Handle<PropertyDict> dictionary, Handle<Name> name,
int key_index, ClassBoilerplate::ValueKind value_kind, Tagged<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, Tagged<Smi> value);
template void ClassBoilerplate::AddToPropertiesTemplate(
LocalIsolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name,
int key_index, ClassBoilerplate::ValueKind value_kind, Tagged<Smi> value);
template void ClassBoilerplate::AddToPropertiesTemplate(
Isolate* isolate, Handle<SwissNameDictionary> dictionary, Handle<Name> name,
int key_index, ClassBoilerplate::ValueKind value_kind, Tagged<Smi> value);
template <typename IsolateT>
void ClassBoilerplate::AddToElementsTemplate(
IsolateT* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
int key_index, ClassBoilerplate::ValueKind value_kind, Tagged<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, Tagged<Smi> value);
template void ClassBoilerplate::AddToElementsTemplate(
LocalIsolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
int key_index, ClassBoilerplate::ValueKind value_kind, Tagged<Smi> value);
// static
template <typename IsolateT>
Handle<ClassBoilerplate> ClassBoilerplate::New(IsolateT* isolate,
ClassLiteral* expr,
AllocationType allocation) {
// 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 IsolateT::HandleScopeType scope(isolate);
auto* factory = isolate->factory();
ObjectDescriptor<IsolateT> static_desc(kMinimumClassPropertiesCount);
ObjectDescriptor<IsolateT> instance_desc(kMinimumPrototypePropertiesCount);
for (int i = 0; i < expr->public_members()->length(); i++) {
ClassLiteral::Property* property = expr->public_members()->at(i);
ObjectDescriptor<IsolateT>& 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 name_accessor.
// All classes, even anonymous ones, have a name accessor.
PropertyAttributes attribs =
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
static_desc.AddConstant(isolate, factory->name_string(),
factory->function_name_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<IsolateT>& 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(IsInternalizedString(*name));
desc.AddNamedProperty(isolate, name, value_kind, value_index);
}
}
static_desc.Finalize(isolate);
instance_desc.Finalize(isolate);
auto result = Handle<ClassBoilerplate>::cast(
factory->NewStruct(CLASS_BOILERPLATE_TYPE, allocation));
result->set_arguments_count(dynamic_argument_index);
result->set_static_properties_template(*static_desc.properties_template());
result->set_static_elements_template(*static_desc.elements_template());
result->set_static_computed_properties(*static_desc.computed_properties());
result->set_instance_properties_template(
*instance_desc.properties_template());
result->set_instance_elements_template(*instance_desc.elements_template());
result->set_instance_computed_properties(
*instance_desc.computed_properties());
return scope.CloseAndEscape(result);
}
template Handle<ClassBoilerplate> ClassBoilerplate::New(
Isolate* isolate, ClassLiteral* expr, AllocationType allocation);
template Handle<ClassBoilerplate> ClassBoilerplate::New(
LocalIsolate* isolate, ClassLiteral* expr, AllocationType allocation);
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