• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 namespace v8 {
23 namespace internal {
24 
25 namespace {
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.
30 constexpr int kDummyEnumerationIndex = 0;
31 
EncodeComputedEntry(ClassBoilerplate::ValueKind value_kind,unsigned key_index)32 inline 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 
ToAccessorComponent(ClassBoilerplate::ValueKind value_kind)40 constexpr AccessorComponent ToAccessorComponent(
41     ClassBoilerplate::ValueKind value_kind) {
42   return value_kind == ClassBoilerplate::kGetter ? ACCESSOR_GETTER
43                                                  : ACCESSOR_SETTER;
44 }
45 
46 template <typename IsolateT>
AddToDescriptorArrayTemplate(IsolateT * isolate,Handle<DescriptorArray> descriptor_array_template,Handle<Name> name,ClassBoilerplate::ValueKind value_kind,Handle<Object> value)47 void 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 
95 template <typename IsolateT>
DictionaryAddNoUpdateNextEnumerationIndex(IsolateT * isolate,Handle<NameDictionary> dictionary,Handle<Name> name,Handle<Object> value,PropertyDetails details,InternalIndex * entry_out=nullptr)96 Handle<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 
104 template <typename IsolateT>
DictionaryAddNoUpdateNextEnumerationIndex(IsolateT * isolate,Handle<SwissNameDictionary> dictionary,Handle<Name> name,Handle<Object> value,PropertyDetails details,InternalIndex * entry_out=nullptr)105 Handle<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 
114 template <typename IsolateT>
DictionaryAddNoUpdateNextEnumerationIndex(IsolateT * isolate,Handle<NumberDictionary> dictionary,uint32_t element,Handle<Object> value,PropertyDetails details,InternalIndex * entry_out=nullptr)115 Handle<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 
125 template <typename Dictionary>
DictionaryUpdateMaxNumberKey(Handle<Dictionary> dictionary,Handle<Name> name)126 void 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 
DictionaryUpdateMaxNumberKey(Handle<NumberDictionary> dictionary,uint32_t element)133 void DictionaryUpdateMaxNumberKey(Handle<NumberDictionary> dictionary,
134                                   uint32_t element) {
135   dictionary->UpdateMaxNumberKey(element, Handle<JSObject>());
136   dictionary->set_requires_slow_elements();
137 }
138 
ComputeEnumerationIndex(int value_index)139 constexpr 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 
148 constexpr int kAccessorNotDefined = -1;
149 
GetExistingValueIndex(Object value)150 inline int GetExistingValueIndex(Object value) {
151   return value.IsSmi() ? Smi::ToInt(value) : kAccessorNotDefined;
152 }
153 
154 template <typename IsolateT, typename Dictionary, typename Key>
AddToDictionaryTemplate(IsolateT * isolate,Handle<Dictionary> dictionary,Key key,int key_index,ClassBoilerplate::ValueKind value_kind,Smi value)155 void 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.
377 template <typename IsolateT>
378 class ObjectDescriptor {
379  public:
IncComputedCount()380   void IncComputedCount() { ++computed_count_; }
IncPropertiesCount()381   void IncPropertiesCount() { ++property_count_; }
IncElementsCount()382   void IncElementsCount() { ++element_count_; }
383 
ObjectDescriptor(int property_slack)384   explicit ObjectDescriptor(int property_slack)
385       : property_slack_(property_slack) {}
386 
HasDictionaryProperties() const387   bool HasDictionaryProperties() const {
388     return computed_count_ > 0 ||
389            (property_count_ + property_slack_) > kMaxNumberOfDescriptors;
390   }
391 
properties_template() const392   Handle<Object> properties_template() const {
393     return HasDictionaryProperties()
394                ? properties_dictionary_template_
395                : Handle<Object>::cast(descriptor_array_template_);
396   }
397 
elements_template() const398   Handle<NumberDictionary> elements_template() const {
399     return elements_dictionary_template_;
400   }
401 
computed_properties() const402   Handle<FixedArray> computed_properties() const {
403     return computed_properties_;
404   }
405 
CreateTemplates(IsolateT * isolate)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 
AddConstant(IsolateT * isolate,Handle<Name> name,Handle<Object> value,PropertyAttributes attribs)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 
AddNamedProperty(IsolateT * isolate,Handle<Name> name,ClassBoilerplate::ValueKind value_kind,int value_index)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 
AddIndexedProperty(IsolateT * isolate,uint32_t element,ClassBoilerplate::ValueKind value_kind,int value_index)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 
AddComputed(ClassBoilerplate::ValueKind value_kind,int key_index)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 
UpdateNextEnumerationIndex(int value_index)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 
Finalize(IsolateT * isolate)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:
properties_dictionary_template() const535   Handle<NameDictionary> properties_dictionary_template() const {
536     return Handle<NameDictionary>::cast(properties_dictionary_template_);
537   }
538 
properties_ordered_dictionary_template() const539   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 
561 template <typename IsolateT, typename PropertyDict>
AddToPropertiesTemplate(IsolateT * isolate,Handle<PropertyDict> dictionary,Handle<Name> name,int key_index,ClassBoilerplate::ValueKind value_kind,Smi value)562 void 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 }
568 template void ClassBoilerplate::AddToPropertiesTemplate(
569     Isolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name,
570     int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
571 template void ClassBoilerplate::AddToPropertiesTemplate(
572     LocalIsolate* isolate, Handle<NameDictionary> dictionary, Handle<Name> name,
573     int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
574 template void ClassBoilerplate::AddToPropertiesTemplate(
575     Isolate* isolate, Handle<SwissNameDictionary> dictionary, Handle<Name> name,
576     int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
577 
578 template <typename IsolateT>
AddToElementsTemplate(IsolateT * isolate,Handle<NumberDictionary> dictionary,uint32_t key,int key_index,ClassBoilerplate::ValueKind value_kind,Smi value)579 void 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 }
585 template void ClassBoilerplate::AddToElementsTemplate(
586     Isolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
587     int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
588 template void ClassBoilerplate::AddToElementsTemplate(
589     LocalIsolate* isolate, Handle<NumberDictionary> dictionary, uint32_t key,
590     int key_index, ClassBoilerplate::ValueKind value_kind, Smi value);
591 
592 template <typename IsolateT>
BuildClassBoilerplate(IsolateT * isolate,ClassLiteral * expr)593 Handle<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 
738 template Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate(
739     Isolate* isolate, ClassLiteral* expr);
740 template Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate(
741     LocalIsolate* isolate, ClassLiteral* expr);
742 
BriefPrintDetails(std::ostream & os)743 void ArrayBoilerplateDescription::BriefPrintDetails(std::ostream& os) {
744   os << " " << ElementsKindToString(elements_kind()) << ", "
745      << Brief(constant_elements());
746 }
747 
BriefPrintDetails(std::ostream & os)748 void 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