1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/objects/literal-objects.h"
6
7#include "src/ast/ast.h"
8#include "src/base/logging.h"
9#include "src/builtins/accessors.h"
10#include "src/common/globals.h"
11#include "src/execution/isolate.h"
12#include "src/heap/factory.h"
13#include "src/heap/local-factory-inl.h"
14#include "src/objects/dictionary.h"
15#include "src/objects/hash-table-inl.h"
16#include "src/objects/js-regexp.h"
17#include "src/objects/literal-objects-inl.h"
18#include "src/objects/objects-inl.h"
19#include "src/objects/smi.h"
20#include "src/objects/struct-inl.h"
21
22namespace v8 {
23namespace internal {
24
25namespace {
26
27// The enumeration order index in the property details is unused if they are
28// stored in a SwissNameDictionary or NumberDictionary (because they handle
29// propery ordering differently). We then use this dummy value instead.
30constexpr int kDummyEnumerationIndex = 0;
31
32inline int EncodeComputedEntry(ClassBoilerplate::ValueKind value_kind,
33                               unsigned key_index) {
34  using Flags = ClassBoilerplate::ComputedEntryFlags;
35  int flags = Flags::ValueKindBits::encode(value_kind) |
36              Flags::KeyIndexBits::encode(key_index);
37  return flags;
38}
39
40constexpr AccessorComponent ToAccessorComponent(
41    ClassBoilerplate::ValueKind value_kind) {
42  return value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER
43                                                 : ACCESSOR_SETTER;
44}
45
46template <typename IsolateT>
47void AddToDescriptorArrayTemplate(
48    IsolateT* isolate, Handle<DescriptorArray> descriptor_array_template,
49    Handle<Name> name, ClassBoilerplate::ValueKind value_kind,
50    Handle<Object> value) {
51  InternalIndex entry = descriptor_array_template->Search(
52      *name, descriptor_array_template->number_of_descriptors());
53  // TODO(ishell): deduplicate properties at AST level, this will allow us to
54  // avoid creation of closures that will be overwritten anyway.
55  if (entry.is_not_found()) {
56    // Entry not found, add new one.
57    Descriptor d;
58    if (value_kind == ClassBoilerplate::kData) {
59      d = Descriptor::DataConstant(name, value, DONT_ENUM);
60    } else {
61      DCHECK(value_kind == ClassBoilerplate::kGetter ||
62             value_kind == ClassBoilerplate::kSetter);
63      Handle<AccessorPair> pair = isolate->factory()->NewAccessorPair();
64      pair->set(ToAccessorComponent(value_kind), *value);
65      d = Descriptor::AccessorConstant(name, pair, DONT_ENUM);
66    }
67    descriptor_array_template->Append(&d);
68
69  } else {
70    // Entry found, update it.
71    int sorted_index = descriptor_array_template->GetDetails(entry).pointer();
72    if (value_kind == ClassBoilerplate::kData) {
73      Descriptor d = Descriptor::DataConstant(name, value, DONT_ENUM);
74      d.SetSortedKeyIndex(sorted_index);
75      descriptor_array_template->Set(entry, &d);
76    } else {
77      DCHECK(value_kind == ClassBoilerplate::kGetter ||
78             value_kind == ClassBoilerplate::kSetter);
79      Object raw_accessor = descriptor_array_template->GetStrongValue(entry);
80      AccessorPair pair;
81      if (raw_accessor.IsAccessorPair()) {
82        pair = AccessorPair::cast(raw_accessor);
83      } else {
84        Handle<AccessorPair> new_pair = isolate->factory()->NewAccessorPair();
85        Descriptor d = Descriptor::AccessorConstant(name, new_pair, DONT_ENUM);
86        d.SetSortedKeyIndex(sorted_index);
87        descriptor_array_template->Set(entry, &d);
88        pair = *new_pair;
89      }
90      pair.set(ToAccessorComponent(value_kind), *value, kReleaseStore);
91    }
92  }
93}
94
95template <typename IsolateT>
96Handle<NameDictionary> DictionaryAddNoUpdateNextEnumerationIndex(
97    IsolateT* isolate, Handle<NameDictionary> dictionary, Handle<Name> name,
98    Handle<Object> value, PropertyDetails details,
99    InternalIndex* entry_out = nullptr) {
100  return NameDictionary::AddNoUpdateNextEnumerationIndex(
101      isolate, dictionary, name, value, details, entry_out);
102}
103
104template <typename IsolateT>
105Handle<SwissNameDictionary> DictionaryAddNoUpdateNextEnumerationIndex(
106    IsolateT* isolate, Handle<SwissNameDictionary> dictionary,
107    Handle<Name> name, Handle<Object> value, PropertyDetails details,
108    InternalIndex* entry_out = nullptr) {
109  // SwissNameDictionary does not maintain the enumeration order in property
110  // details, so it's a normal Add().
111  return SwissNameDictionary::Add(isolate, dictionary, name, value, details);
112}
113
114template <typename IsolateT>
115Handle<NumberDictionary> DictionaryAddNoUpdateNextEnumerationIndex(
116    IsolateT* isolate, Handle<NumberDictionary> dictionary, uint32_t element,
117    Handle<Object> value, PropertyDetails details,
118    InternalIndex* entry_out = nullptr) {
119  // NumberDictionary does not maintain the enumeration order, so it's
120  // a normal Add().
121  return NumberDictionary::Add(isolate, dictionary, element, value, details,
122                               entry_out);
123}
124
125template <typename Dictionary>
126void DictionaryUpdateMaxNumberKey(Handle<Dictionary> dictionary,
127                                  Handle<Name> name) {
128  STATIC_ASSERT((std::is_same<Dictionary, SwissNameDictionary>::value ||
129                 std::is_same<Dictionary, NameDictionary>::value));
130  // No-op for (ordered) name dictionaries.
131}
132
133void DictionaryUpdateMaxNumberKey(Handle<NumberDictionary> dictionary,
134                                  uint32_t element) {
135  dictionary->UpdateMaxNumberKey(element, Handle<JSObject>());
136  dictionary->set_requires_slow_elements();
137}
138
139constexpr int ComputeEnumerationIndex(int value_index) {
140  // We "shift" value indices to ensure that the enumeration index for the value
141  // will not overlap with minimum properties set for both class and prototype
142  // objects.
143  return value_index +
144         std::max({ClassBoilerplate::kMinimumClassPropertiesCount,
145                   ClassBoilerplate::kMinimumPrototypePropertiesCount});
146}
147
148constexpr int kAccessorNotDefined = -1;
149
150inline int GetExistingValueIndex(Object value) {
151  return value.IsSmi() ? Smi::ToInt(value) : kAccessorNotDefined;
152}
153
154template <typename IsolateT, typename Dictionary, typename Key>
155void AddToDictionaryTemplate(IsolateT* isolate, Handle<Dictionary> dictionary,
156                             Key key, int key_index,
157                             ClassBoilerplate::ValueKind value_kind,
158                             Smi value) {
159  InternalIndex entry = dictionary->FindEntry(isolate, key);
160
161  const bool is_elements_dictionary =
162      std::is_same<Dictionary, NumberDictionary>::value;
163  STATIC_ASSERT(is_elements_dictionary !=
164                (std::is_same<Dictionary, NameDictionary>::value ||
165                 std::is_same<Dictionary, SwissNameDictionary>::value));
166
167  if (entry.is_not_found()) {
168    // Entry not found, add new one.
169    int enum_order =
170        Dictionary::kIsOrderedDictionaryType || is_elements_dictionary
171            ? kDummyEnumerationIndex
172            : ComputeEnumerationIndex(key_index);
173    Handle<Object> value_handle;
174    PropertyDetails details(
175        value_kind != ClassBoilerplate::kData ? PropertyKind::kAccessor
176                                              : PropertyKind::kData,
177        DONT_ENUM, PropertyDetails::kConstIfDictConstnessTracking, enum_order);
178    if (value_kind == ClassBoilerplate::kData) {
179      value_handle = handle(value, isolate);
180    } else {
181      Handle<AccessorPair> pair(isolate->factory()->NewAccessorPair());
182      pair->set(ToAccessorComponent(value_kind), value);
183      value_handle = pair;
184    }
185
186    // Add value to the dictionary without updating next enumeration index.
187    Handle<Dictionary> dict = DictionaryAddNoUpdateNextEnumerationIndex(
188        isolate, dictionary, key, value_handle, details, &entry);
189    // It is crucial to avoid dictionary reallocations because it may remove
190    // potential gaps in enumeration indices values that are necessary for
191    // inserting computed properties into right places in the enumeration order.
192    CHECK_EQ(*dict, *dictionary);
193
194    DictionaryUpdateMaxNumberKey(dictionary, key);
195
196  } else {
197    // Entry found, update it.
198    int enum_order_existing =
199        Dictionary::kIsOrderedDictionaryType
200            ? kDummyEnumerationIndex
201            : dictionary->DetailsAt(entry).dictionary_index();
202    int enum_order_computed =
203        Dictionary::kIsOrderedDictionaryType || is_elements_dictionary
204            ? kDummyEnumerationIndex
205            : ComputeEnumerationIndex(key_index);
206
207    Object existing_value = dictionary->ValueAt(entry);
208    if (value_kind == ClassBoilerplate::kData) {
209      // Computed value is a normal method.
210      if (existing_value.IsAccessorPair()) {
211        AccessorPair current_pair = AccessorPair::cast(existing_value);
212
213        int existing_getter_index =
214            GetExistingValueIndex(current_pair.getter());
215        int existing_setter_index =
216            GetExistingValueIndex(current_pair.setter());
217        // At least one of the accessors must already be defined.
218        STATIC_ASSERT(kAccessorNotDefined < 0);
219        DCHECK(existing_getter_index >= 0 || existing_setter_index >= 0);
220        if (existing_getter_index < key_index &&
221            existing_setter_index < key_index) {
222          // Either both getter and setter were defined before the computed
223          // method or just one of them was defined before while the other one
224          // was not defined yet, so overwrite property to kData.
225          PropertyDetails details(
226              PropertyKind::kData, DONT_ENUM,
227              PropertyDetails::kConstIfDictConstnessTracking,
228              enum_order_existing);
229          dictionary->DetailsAtPut(entry, details);
230          dictionary->ValueAtPut(entry, value);
231
232        } else if (existing_getter_index != kAccessorNotDefined &&
233                   existing_getter_index < key_index) {
234          DCHECK_LT(key_index, existing_setter_index);
235          // Getter was defined and it was done before the computed method
236          // and then it was overwritten by the current computed method which
237          // in turn was later overwritten by the setter method. So we clear
238          // the getter.
239          current_pair.set_getter(*isolate->factory()->null_value());
240
241        } else if (existing_setter_index != kAccessorNotDefined &&
242                   existing_setter_index < key_index) {
243          DCHECK_LT(key_index, existing_getter_index);
244          // Setter was defined and it was done before the computed method
245          // and then it was overwritten by the current computed method which
246          // in turn was later overwritten by the getter method. So we clear
247          // the setter.
248          current_pair.set_setter(*isolate->factory()->null_value());
249
250        } else {
251          // One of the following cases holds:
252          // The computed method was defined before ...
253          // 1.) the getter and setter, both of which are defined,
254          // 2.) the getter, and the setter isn't defined,
255          // 3.) the setter, and the getter isn't defined.
256          // Therefore, the computed value is overwritten, receiving the
257          // computed property's enum index.
258          DCHECK(key_index < existing_getter_index ||
259                 existing_getter_index == kAccessorNotDefined);
260          DCHECK(key_index < existing_setter_index ||
261                 existing_setter_index == kAccessorNotDefined);
262          DCHECK(existing_getter_index != kAccessorNotDefined ||
263                 existing_setter_index != kAccessorNotDefined);
264          if (!is_elements_dictionary) {
265            // The enum index is unused by elements dictionaries,
266            // which is why we don't need to update the property details if
267            // |is_elements_dictionary| holds.
268            PropertyDetails details = dictionary->DetailsAt(entry);
269            details = details.set_index(enum_order_computed);
270            dictionary->DetailsAtPut(entry, details);
271          }
272        }
273      } else {  // if (existing_value.IsAccessorPair()) ends here
274        DCHECK(value_kind == ClassBoilerplate::kData);
275
276        DCHECK_IMPLIES(!existing_value.IsSmi(),
277                       existing_value.IsAccessorInfo());
278        DCHECK_IMPLIES(!existing_value.IsSmi(),
279                       AccessorInfo::cast(existing_value).name() ==
280                               *isolate->factory()->length_string() ||
281                           AccessorInfo::cast(existing_value).name() ==
282                               *isolate->factory()->name_string());
283        if (!existing_value.IsSmi() || Smi::ToInt(existing_value) < key_index) {
284          // Overwrite existing value because it was defined before the computed
285          // one (AccessorInfo "length" and "name" properties are always defined
286          // before).
287          PropertyDetails details(
288              PropertyKind::kData, DONT_ENUM,
289              PropertyDetails::kConstIfDictConstnessTracking,
290              enum_order_existing);
291          dictionary->DetailsAtPut(entry, details);
292          dictionary->ValueAtPut(entry, value);
293        } else {
294          // The computed value appears before the existing one. Set the
295          // existing entry's enum index to that of the computed one.
296          if (!is_elements_dictionary) {
297            // The enum index is unused by elements dictionaries,
298            // which is why we don't need to update the property details if
299            // |is_elements_dictionary| holds.
300            PropertyDetails details(
301                PropertyKind::kData, DONT_ENUM,
302                PropertyDetails::kConstIfDictConstnessTracking,
303                enum_order_computed);
304
305            dictionary->DetailsAtPut(entry, details);
306          }
307        }
308      }
309    } else {  // if (value_kind == ClassBoilerplate::kData) ends here
310      AccessorComponent component = ToAccessorComponent(value_kind);
311      if (existing_value.IsAccessorPair()) {
312        // Update respective component of existing AccessorPair.
313        AccessorPair current_pair = AccessorPair::cast(existing_value);
314
315        int existing_component_index =
316            GetExistingValueIndex(current_pair.get(component));
317        if (existing_component_index < key_index) {
318          current_pair.set(component, value, kReleaseStore);
319        } else {
320          // The existing accessor property overwrites the computed one, update
321          // its enumeration order accordingly.
322
323          if (!is_elements_dictionary) {
324            // The enum index is unused by elements dictionaries,
325            // which is why we don't need to update the property details if
326            // |is_elements_dictionary| holds.
327
328            PropertyDetails details(
329                PropertyKind::kAccessor, DONT_ENUM,
330                PropertyDetails::kConstIfDictConstnessTracking,
331                enum_order_computed);
332            dictionary->DetailsAtPut(entry, details);
333          }
334        }
335
336      } else {
337        DCHECK(!existing_value.IsAccessorPair());
338        DCHECK(value_kind != ClassBoilerplate::kData);
339
340        if (!existing_value.IsSmi() || Smi::ToInt(existing_value) < key_index) {
341          // Overwrite the existing data property because it was defined before
342          // the computed accessor property.
343          Handle<AccessorPair> pair(isolate->factory()->NewAccessorPair());
344          pair->set(component, value);
345          PropertyDetails details(
346              PropertyKind::kAccessor, DONT_ENUM,
347              PropertyDetails::kConstIfDictConstnessTracking,
348              enum_order_existing);
349          dictionary->DetailsAtPut(entry, details);
350          dictionary->ValueAtPut(entry, *pair);
351        } else {
352          // The computed accessor property appears before the existing data
353          // property. Set the existing entry's enum index to that of the
354          // computed one.
355
356          if (!is_elements_dictionary) {
357            // The enum index is unused by elements dictionaries,
358            // which is why we don't need to update the property details if
359            // |is_elements_dictionary| holds.
360            PropertyDetails details(
361                PropertyKind::kData, DONT_ENUM,
362                PropertyDetails::kConstIfDictConstnessTracking,
363                enum_order_computed);
364
365            dictionary->DetailsAtPut(entry, details);
366          }
367        }
368      }
369    }
370  }
371}
372
373}  // namespace
374
375// Helper class that eases building of a properties, elements and computed
376// properties templates.
377template <typename IsolateT>
378class ObjectDescriptor {
379 public:
380  void IncComputedCount() { ++computed_count_; }
381  void IncPropertiesCount() { ++property_count_; }
382  void IncElementsCount() { ++element_count_; }
383
384  explicit ObjectDescriptor(int property_slack)
385      : property_slack_(property_slack) {}
386
387  bool HasDictionaryProperties() const {
388    return computed_count_ > 0 ||
389           (property_count_ + property_slack_) > kMaxNumberOfDescriptors;
390  }
391
392  Handle<Object> properties_template() const {
393    return HasDictionaryProperties()
394               ? properties_dictionary_template_
395               : Handle<Object>::cast(descriptor_array_template_);
396  }
397
398  Handle<NumberDictionary> elements_template() const {
399    return elements_dictionary_template_;
400  }
401
402  Handle<FixedArray> computed_properties() const {
403    return computed_properties_;
404  }
405
406  void CreateTemplates(IsolateT* isolate) {
407    auto* factory = isolate->factory();
408    descriptor_array_template_ = factory->empty_descriptor_array();
409    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
410      properties_dictionary_template_ =
411          factory->empty_swiss_property_dictionary();
412    } else {
413      properties_dictionary_template_ = factory->empty_property_dictionary();
414    }
415    if (property_count_ || computed_count_ || property_slack_) {
416      if (HasDictionaryProperties()) {
417        int need_space_for =
418            property_count_ + computed_count_ + property_slack_;
419        if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
420          properties_dictionary_template_ =
421              isolate->factory()->NewSwissNameDictionary(need_space_for,
422                                                         AllocationType::kOld);
423
424        } else {
425          properties_dictionary_template_ = NameDictionary::New(
426              isolate, need_space_for, AllocationType::kOld);
427        }
428      } else {
429        descriptor_array_template_ = DescriptorArray::Allocate(
430            isolate, 0, property_count_ + property_slack_,
431            AllocationType::kOld);
432      }
433    }
434    elements_dictionary_template_ =
435        element_count_ || computed_count_
436            ? NumberDictionary::New(isolate, element_count_ + computed_count_,
437                                    AllocationType::kOld)
438            : factory->empty_slow_element_dictionary();
439
440    computed_properties_ =
441        computed_count_
442            ? factory->NewFixedArray(computed_count_, AllocationType::kOld)
443            : factory->empty_fixed_array();
444
445    temp_handle_ = handle(Smi::zero(), isolate);
446  }
447
448  void AddConstant(IsolateT* isolate, Handle<Name> name, Handle<Object> value,
449                   PropertyAttributes attribs) {
450    bool is_accessor = value->IsAccessorInfo();
451    DCHECK(!value->IsAccessorPair());
452    if (HasDictionaryProperties()) {
453      PropertyKind kind =
454          is_accessor ? i::PropertyKind::kAccessor : i::PropertyKind::kData;
455      int enum_order = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
456                           ? kDummyEnumerationIndex
457                           : next_enumeration_index_++;
458      PropertyDetails details(kind, attribs, PropertyCellType::kNoCell,
459                              enum_order);
460      if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
461        properties_dictionary_template_ =
462            DictionaryAddNoUpdateNextEnumerationIndex(
463                isolate, properties_ordered_dictionary_template(), name, value,
464                details);
465      } else {
466        properties_dictionary_template_ =
467            DictionaryAddNoUpdateNextEnumerationIndex(
468                isolate, properties_dictionary_template(), name, value,
469                details);
470      }
471    } else {
472      Descriptor d = is_accessor
473                         ? Descriptor::AccessorConstant(name, value, attribs)
474                         : Descriptor::DataConstant(name, value, attribs);
475      descriptor_array_template_->Append(&d);
476    }
477  }
478
479  void AddNamedProperty(IsolateT* isolate, Handle<Name> name,
480                        ClassBoilerplate::ValueKind value_kind,
481                        int value_index) {
482    Smi value = Smi::FromInt(value_index);
483    if (HasDictionaryProperties()) {
484      UpdateNextEnumerationIndex(value_index);
485      if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
486        AddToDictionaryTemplate(isolate,
487                                properties_ordered_dictionary_template(), name,
488                                value_index, value_kind, value);
489      } else {
490        AddToDictionaryTemplate(isolate, properties_dictionary_template(), name,
491                                value_index, value_kind, value);
492      }
493    } else {
494      temp_handle_.PatchValue(value);
495      AddToDescriptorArrayTemplate(isolate, descriptor_array_template_, name,
496                                   value_kind, temp_handle_);
497    }
498  }
499
500  void AddIndexedProperty(IsolateT* isolate, uint32_t element,
501                          ClassBoilerplate::ValueKind value_kind,
502                          int value_index) {
503    Smi value = Smi::FromInt(value_index);
504    AddToDictionaryTemplate(isolate, elements_dictionary_template_, element,
505                            value_index, value_kind, value);
506  }
507
508  void AddComputed(ClassBoilerplate::ValueKind value_kind, int key_index) {
509    int value_index = key_index + 1;
510    UpdateNextEnumerationIndex(value_index);
511
512    int flags = EncodeComputedEntry(value_kind, key_index);
513    computed_properties_->set(current_computed_index_++, Smi::FromInt(flags));
514  }
515
516  void UpdateNextEnumerationIndex(int value_index) {
517    int current_index = ComputeEnumerationIndex(value_index);
518    DCHECK_LE(next_enumeration_index_, current_index);
519    next_enumeration_index_ = current_index + 1;
520  }
521
522  void Finalize(IsolateT* isolate) {
523    if (HasDictionaryProperties()) {
524      DCHECK_EQ(current_computed_index_, computed_properties_->length());
525      if (!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
526        properties_dictionary_template()->set_next_enumeration_index(
527            next_enumeration_index_);
528      }
529    } else {
530      DCHECK(descriptor_array_template_->IsSortedNoDuplicates());
531    }
532  }
533
534 private:
535  Handle<NameDictionary> properties_dictionary_template() const {
536    return Handle<NameDictionary>::cast(properties_dictionary_template_);
537  }
538
539  Handle<SwissNameDictionary> properties_ordered_dictionary_template() const {
540    return Handle<SwissNameDictionary>::cast(properties_dictionary_template_);
541  }
542
543  const int property_slack_;
544  int property_count_ = 0;
545  int next_enumeration_index_ = PropertyDetails::kInitialIndex;
546  int element_count_ = 0;
547  int computed_count_ = 0;
548  int current_computed_index_ = 0;
549
550  Handle<DescriptorArray> descriptor_array_template_;
551
552  // Is either a NameDictionary or SwissNameDictionary.
553  Handle<HeapObject> properties_dictionary_template_;
554
555  Handle<NumberDictionary> elements_dictionary_template_;
556  Handle<FixedArray> computed_properties_;
557  // This temporary handle is used for storing to descriptor array.
558  Handle<Object> temp_handle_;
559};
560
561template <typename IsolateT, typename PropertyDict>
562void ClassBoilerplate::AddToPropertiesTemplate(
563    IsolateT* isolate, Handle<PropertyDict> dictionary, Handle<Name> name,
564    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value) {
565  AddToDictionaryTemplate(isolate, dictionary, name, key_index, value_kind,
566                          value);
567}
568template void ClassBoilerplate::AddToPropertiesTemplate(
569    Isolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name,
570    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
571template void ClassBoilerplate::AddToPropertiesTemplate(
572    LocalIsolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name,
573    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
574template void ClassBoilerplate::AddToPropertiesTemplate(
575    Isolate* isolate, Handle<SwissNameDictionary> dictionary, Handle<Name> name,
576    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
577
578template <typename IsolateT>
579void ClassBoilerplate::AddToElementsTemplate(
580    IsolateT* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
581    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value) {
582  AddToDictionaryTemplate(isolate, dictionary, key, key_index, value_kind,
583                          value);
584}
585template void ClassBoilerplate::AddToElementsTemplate(
586    Isolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
587    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
588template void ClassBoilerplate::AddToElementsTemplate(
589    LocalIsolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
590    int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
591
592template <typename IsolateT>
593Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate(
594    IsolateT* isolate, ClassLiteral* expr) {
595  // Create a non-caching handle scope to ensure that the temporary handle used
596  // by ObjectDescriptor for passing Smis around does not corrupt handle cache
597  // in CanonicalHandleScope.
598  typename IsolateT::HandleScopeType scope(isolate);
599  auto* factory = isolate->factory();
600  ObjectDescriptor<IsolateT> static_desc(kMinimumClassPropertiesCount);
601  ObjectDescriptor<IsolateT> instance_desc(kMinimumPrototypePropertiesCount);
602
603  for (int i = 0; i < expr->public_members()->length(); i++) {
604    ClassLiteral::Property* property = expr->public_members()->at(i);
605    ObjectDescriptor<IsolateT>& desc =
606        property->is_static() ? static_desc : instance_desc;
607    if (property->is_computed_name()) {
608      if (property->kind() != ClassLiteral::Property::FIELD) {
609        desc.IncComputedCount();
610      }
611    } else {
612      if (property->key()->AsLiteral()->IsPropertyName()) {
613        desc.IncPropertiesCount();
614      } else {
615        desc.IncElementsCount();
616      }
617    }
618  }
619
620  //
621  // Initialize class object template.
622  //
623  static_desc.CreateTemplates(isolate);
624  STATIC_ASSERT(JSFunction::kLengthDescriptorIndex == 0);
625  {
626    // Add length_accessor.
627    PropertyAttributes attribs =
628        static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
629    static_desc.AddConstant(isolate, factory->length_string(),
630                            factory->function_length_accessor(), attribs);
631  }
632  {
633    // Add name_accessor.
634    // All classes, even anonymous ones, have a name accessor.
635    PropertyAttributes attribs =
636        static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
637    static_desc.AddConstant(isolate, factory->name_string(),
638                            factory->function_name_accessor(), attribs);
639  }
640  {
641    // Add prototype_accessor.
642    PropertyAttributes attribs =
643        static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
644    static_desc.AddConstant(isolate, factory->prototype_string(),
645                            factory->function_prototype_accessor(), attribs);
646  }
647  {
648    Handle<ClassPositions> class_positions = factory->NewClassPositions(
649        expr->start_position(), expr->end_position());
650    static_desc.AddConstant(isolate, factory->class_positions_symbol(),
651                            class_positions, DONT_ENUM);
652  }
653
654  //
655  // Initialize prototype object template.
656  //
657  instance_desc.CreateTemplates(isolate);
658  {
659    Handle<Object> value(
660        Smi::FromInt(ClassBoilerplate::kConstructorArgumentIndex), isolate);
661    instance_desc.AddConstant(isolate, factory->constructor_string(), value,
662                              DONT_ENUM);
663  }
664
665  //
666  // Fill in class boilerplate.
667  //
668  int dynamic_argument_index = ClassBoilerplate::kFirstDynamicArgumentIndex;
669
670  for (int i = 0; i < expr->public_members()->length(); i++) {
671    ClassLiteral::Property* property = expr->public_members()->at(i);
672    ClassBoilerplate::ValueKind value_kind;
673    switch (property->kind()) {
674      case ClassLiteral::Property::METHOD:
675        value_kind = ClassBoilerplate::kData;
676        break;
677      case ClassLiteral::Property::GETTER:
678        value_kind = ClassBoilerplate::kGetter;
679        break;
680      case ClassLiteral::Property::SETTER:
681        value_kind = ClassBoilerplate::kSetter;
682        break;
683      case ClassLiteral::Property::FIELD:
684        DCHECK_IMPLIES(property->is_computed_name(), !property->is_private());
685        if (property->is_computed_name()) {
686          ++dynamic_argument_index;
687        }
688        continue;
689    }
690
691    ObjectDescriptor<IsolateT>& desc =
692        property->is_static() ? static_desc : instance_desc;
693    if (property->is_computed_name()) {
694      int computed_name_index = dynamic_argument_index;
695      dynamic_argument_index += 2;  // Computed name and value indices.
696      desc.AddComputed(value_kind, computed_name_index);
697      continue;
698    }
699    int value_index = dynamic_argument_index++;
700
701    Literal* key_literal = property->key()->AsLiteral();
702    uint32_t index;
703    if (key_literal->AsArrayIndex(&index)) {
704      desc.AddIndexedProperty(isolate, index, value_kind, value_index);
705
706    } else {
707      Handle<String> name = key_literal->AsRawPropertyName()->string();
708      DCHECK(name->IsInternalizedString());
709      desc.AddNamedProperty(isolate, name, value_kind, value_index);
710    }
711  }
712
713  static_desc.Finalize(isolate);
714  instance_desc.Finalize(isolate);
715
716  Handle<ClassBoilerplate> class_boilerplate = Handle<ClassBoilerplate>::cast(
717      factory->NewFixedArray(kBoilerplateLength, AllocationType::kOld));
718
719  class_boilerplate->set_arguments_count(dynamic_argument_index);
720
721  class_boilerplate->set_static_properties_template(
722      *static_desc.properties_template());
723  class_boilerplate->set_static_elements_template(
724      *static_desc.elements_template());
725  class_boilerplate->set_static_computed_properties(
726      *static_desc.computed_properties());
727
728  class_boilerplate->set_instance_properties_template(
729      *instance_desc.properties_template());
730  class_boilerplate->set_instance_elements_template(
731      *instance_desc.elements_template());
732  class_boilerplate->set_instance_computed_properties(
733      *instance_desc.computed_properties());
734
735  return scope.CloseAndEscape(class_boilerplate);
736}
737
738template Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate(
739    Isolate* isolate, ClassLiteral* expr);
740template Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate(
741    LocalIsolate* isolate, ClassLiteral* expr);
742
743void ArrayBoilerplateDescription::BriefPrintDetails(std::ostream& os) {
744  os << " " << ElementsKindToString(elements_kind()) << ", "
745     << Brief(constant_elements());
746}
747
748void RegExpBoilerplateDescription::BriefPrintDetails(std::ostream& os) {
749  // Note: keep boilerplate layout synced with JSRegExp layout.
750  STATIC_ASSERT(JSRegExp::kDataOffset == JSObject::kHeaderSize);
751  STATIC_ASSERT(JSRegExp::kSourceOffset == JSRegExp::kDataOffset + kTaggedSize);
752  STATIC_ASSERT(JSRegExp::kFlagsOffset ==
753                JSRegExp::kSourceOffset + kTaggedSize);
754  STATIC_ASSERT(JSRegExp::kHeaderSize == JSRegExp::kFlagsOffset + kTaggedSize);
755  os << " " << Brief(data()) << ", " << Brief(source()) << ", " << flags();
756}
757
758}  // namespace internal
759}  // namespace v8
760