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