1 // Copyright 2014 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/lookup.h"
6
7 #include "src/bootstrapper.h"
8 #include "src/deoptimizer.h"
9 #include "src/elements.h"
10 #include "src/field-type.h"
11 #include "src/isolate-inl.h"
12
13 namespace v8 {
14 namespace internal {
15
16 // static
PropertyOrElement(Isolate * isolate,Handle<Object> receiver,Handle<Object> key,bool * success,Configuration configuration)17 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
18 Handle<Object> receiver,
19 Handle<Object> key,
20 bool* success,
21 Configuration configuration) {
22 uint32_t index = 0;
23 if (key->ToArrayIndex(&index)) {
24 *success = true;
25 return LookupIterator(isolate, receiver, index, configuration);
26 }
27
28 Handle<Name> name;
29 *success = Object::ToName(isolate, key).ToHandle(&name);
30 if (!*success) {
31 DCHECK(isolate->has_pending_exception());
32 // Return an unusable dummy.
33 return LookupIterator(receiver, isolate->factory()->empty_string());
34 }
35
36 if (name->AsArrayIndex(&index)) {
37 LookupIterator it(isolate, receiver, index, configuration);
38 // Here we try to avoid having to rebuild the string later
39 // by storing it on the indexed LookupIterator.
40 it.name_ = name;
41 return it;
42 }
43
44 return LookupIterator(receiver, name, configuration);
45 }
46
47 template <bool is_element>
Start()48 void LookupIterator::Start() {
49 DisallowHeapAllocation no_gc;
50
51 has_property_ = false;
52 state_ = NOT_FOUND;
53 holder_ = initial_holder_;
54
55 JSReceiver* holder = *holder_;
56 Map* map = holder->map();
57
58 state_ = LookupInHolder<is_element>(map, holder);
59 if (IsFound()) return;
60
61 NextInternal<is_element>(map, holder);
62 }
63
64 template void LookupIterator::Start<true>();
65 template void LookupIterator::Start<false>();
66
Next()67 void LookupIterator::Next() {
68 DCHECK_NE(JSPROXY, state_);
69 DCHECK_NE(TRANSITION, state_);
70 DisallowHeapAllocation no_gc;
71 has_property_ = false;
72
73 JSReceiver* holder = *holder_;
74 Map* map = holder->map();
75
76 if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
77 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
78 : LookupInSpecialHolder<false>(map, holder);
79 if (IsFound()) return;
80 }
81
82 IsElement() ? NextInternal<true>(map, holder)
83 : NextInternal<false>(map, holder);
84 }
85
86 template <bool is_element>
NextInternal(Map * map,JSReceiver * holder)87 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
88 do {
89 JSReceiver* maybe_holder = NextHolder(map);
90 if (maybe_holder == nullptr) {
91 if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
92 RestartLookupForNonMaskingInterceptors<is_element>();
93 return;
94 }
95 state_ = NOT_FOUND;
96 if (holder != *holder_) holder_ = handle(holder, isolate_);
97 return;
98 }
99 holder = maybe_holder;
100 map = holder->map();
101 state_ = LookupInHolder<is_element>(map, holder);
102 } while (!IsFound());
103
104 holder_ = handle(holder, isolate_);
105 }
106
107 template <bool is_element>
RestartInternal(InterceptorState interceptor_state)108 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
109 interceptor_state_ = interceptor_state;
110 property_details_ = PropertyDetails::Empty();
111 number_ = DescriptorArray::kNotFound;
112 Start<is_element>();
113 }
114
115 template void LookupIterator::RestartInternal<true>(InterceptorState);
116 template void LookupIterator::RestartInternal<false>(InterceptorState);
117
118 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> receiver,uint32_t index)119 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
120 Isolate* isolate, Handle<Object> receiver, uint32_t index) {
121 // Strings are the only objects with properties (only elements) directly on
122 // the wrapper. Hence we can skip generating the wrapper for all other cases.
123 if (index != kMaxUInt32 && receiver->IsString() &&
124 index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
125 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
126 // context, ensuring that we don't leak it into JS?
127 Handle<JSFunction> constructor = isolate->string_function();
128 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
129 Handle<JSValue>::cast(result)->set_value(*receiver);
130 return result;
131 }
132 auto root =
133 handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
134 if (root->IsNull(isolate)) {
135 unsigned int magic = 0xbbbbbbbb;
136 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
137 }
138 return Handle<JSReceiver>::cast(root);
139 }
140
141
GetReceiverMap() const142 Handle<Map> LookupIterator::GetReceiverMap() const {
143 if (receiver_->IsNumber()) return factory()->heap_number_map();
144 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
145 }
146
HasAccess() const147 bool LookupIterator::HasAccess() const {
148 DCHECK_EQ(ACCESS_CHECK, state_);
149 return isolate_->MayAccess(handle(isolate_->context()),
150 GetHolder<JSObject>());
151 }
152
153 template <bool is_element>
ReloadPropertyInformation()154 void LookupIterator::ReloadPropertyInformation() {
155 state_ = BEFORE_PROPERTY;
156 interceptor_state_ = InterceptorState::kUninitialized;
157 state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
158 DCHECK(IsFound() || !holder_->HasFastProperties());
159 }
160
InternalUpdateProtector()161 void LookupIterator::InternalUpdateProtector() {
162 if (isolate_->bootstrapper()->IsActive()) return;
163
164 if (*name_ == heap()->constructor_string()) {
165 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
166 // Setting the constructor property could change an instance's @@species
167 if (holder_->IsJSArray()) {
168 isolate_->CountUsage(
169 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
170 isolate_->InvalidateArraySpeciesProtector();
171 } else if (holder_->map()->is_prototype_map()) {
172 DisallowHeapAllocation no_gc;
173 // Setting the constructor of Array.prototype of any realm also needs
174 // to invalidate the species protector
175 if (isolate_->IsInAnyContext(*holder_,
176 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
177 isolate_->CountUsage(v8::Isolate::UseCounterFeature::
178 kArrayPrototypeConstructorModified);
179 isolate_->InvalidateArraySpeciesProtector();
180 }
181 }
182 } else if (*name_ == heap()->species_symbol()) {
183 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
184 // Setting the Symbol.species property of any Array constructor invalidates
185 // the species protector
186 if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
187 isolate_->CountUsage(
188 v8::Isolate::UseCounterFeature::kArraySpeciesModified);
189 isolate_->InvalidateArraySpeciesProtector();
190 }
191 } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
192 if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
193 isolate_->InvalidateIsConcatSpreadableProtector();
194 } else if (*name_ == heap()->has_instance_symbol()) {
195 if (!isolate_->IsHasInstanceLookupChainIntact()) return;
196 isolate_->InvalidateHasInstanceProtector();
197 } else if (*name_ == heap()->iterator_symbol()) {
198 if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
199 if (holder_->IsJSArray()) {
200 isolate_->InvalidateArrayIteratorProtector();
201 }
202 }
203 }
204
PrepareForDataProperty(Handle<Object> value)205 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
206 DCHECK(state_ == DATA || state_ == ACCESSOR);
207 DCHECK(HolderIsReceiverOrHiddenPrototype());
208
209 Handle<JSObject> holder = GetHolder<JSObject>();
210
211 if (IsElement()) {
212 ElementsKind kind = holder->GetElementsKind();
213 ElementsKind to = value->OptimalElementsKind();
214 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
215 to = GetMoreGeneralElementsKind(kind, to);
216
217 if (kind != to) {
218 JSObject::TransitionElementsKind(holder, to);
219 }
220
221 // Copy the backing store if it is copy-on-write.
222 if (IsFastSmiOrObjectElementsKind(to)) {
223 JSObject::EnsureWritableFastElements(holder);
224 }
225 return;
226 }
227
228 if (holder->IsJSGlobalObject()) {
229 Handle<GlobalDictionary> dictionary(holder->global_dictionary());
230 Handle<PropertyCell> cell(
231 PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
232 DCHECK(!cell->IsTheHole(isolate_));
233 property_details_ = cell->property_details();
234 PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
235 property_details_);
236 return;
237 }
238 if (!holder->HasFastProperties()) return;
239
240 Handle<Map> old_map(holder->map(), isolate_);
241 Handle<Map> new_map =
242 Map::PrepareForDataProperty(old_map, descriptor_number(), value);
243
244 if (old_map.is_identical_to(new_map)) {
245 // Update the property details if the representation was None.
246 if (representation().IsNone()) {
247 property_details_ =
248 new_map->instance_descriptors()->GetDetails(descriptor_number());
249 }
250 return;
251 }
252
253 JSObject::MigrateToMap(holder, new_map);
254 ReloadPropertyInformation<false>();
255 }
256
257
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)258 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
259 PropertyAttributes attributes) {
260 DCHECK(state_ == DATA || state_ == ACCESSOR);
261 DCHECK(HolderIsReceiverOrHiddenPrototype());
262 Handle<JSObject> holder = GetHolder<JSObject>();
263 if (IsElement()) {
264 DCHECK(!holder->HasFixedTypedArrayElements());
265 DCHECK(attributes != NONE || !holder->HasFastElements());
266 Handle<FixedArrayBase> elements(holder->elements());
267 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
268 attributes);
269 ReloadPropertyInformation<true>();
270 } else if (holder->HasFastProperties()) {
271 Handle<Map> old_map(holder->map(), isolate_);
272 Handle<Map> new_map = Map::ReconfigureExistingProperty(
273 old_map, descriptor_number(), i::kData, attributes);
274 new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), value);
275 JSObject::MigrateToMap(holder, new_map);
276 ReloadPropertyInformation<false>();
277 } else {
278 PropertyDetails details(attributes, v8::internal::DATA, 0,
279 PropertyCellType::kMutable);
280 if (holder->IsJSGlobalObject()) {
281 Handle<GlobalDictionary> dictionary(holder->global_dictionary());
282
283 Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
284 dictionary, dictionary_entry(), value, details);
285 cell->set_value(*value);
286 property_details_ = cell->property_details();
287 } else {
288 Handle<NameDictionary> dictionary(holder->property_dictionary());
289 PropertyDetails original_details =
290 dictionary->DetailsAt(dictionary_entry());
291 int enumeration_index = original_details.dictionary_index();
292 DCHECK(enumeration_index > 0);
293 details = details.set_index(enumeration_index);
294 dictionary->SetEntry(dictionary_entry(), name(), value, details);
295 property_details_ = details;
296 }
297 state_ = DATA;
298 }
299
300 WriteDataValue(value);
301
302 #if VERIFY_HEAP
303 if (FLAG_verify_heap) {
304 holder->JSObjectVerify();
305 }
306 #endif
307 }
308
309 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
310 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSObject> receiver,Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)311 void LookupIterator::PrepareTransitionToDataProperty(
312 Handle<JSObject> receiver, Handle<Object> value,
313 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
314 DCHECK(receiver.is_identical_to(GetStoreTarget()));
315 if (state_ == TRANSITION) return;
316
317 if (!IsElement() && name()->IsPrivate()) {
318 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
319 }
320
321 DCHECK(state_ != LookupIterator::ACCESSOR ||
322 (GetAccessors()->IsAccessorInfo() &&
323 AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
324 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
325 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
326
327 Handle<Map> map(receiver->map(), isolate_);
328
329 // Dictionary maps can always have additional data properties.
330 if (map->is_dictionary_map()) {
331 state_ = TRANSITION;
332 if (map->IsJSGlobalObjectMap()) {
333 // Install a property cell.
334 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
335 int entry;
336 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
337 global, name(), PropertyCellType::kUninitialized, &entry);
338 Handle<GlobalDictionary> dictionary(global->global_dictionary(),
339 isolate_);
340 DCHECK(cell->value()->IsTheHole(isolate_));
341 DCHECK(!value->IsTheHole(isolate_));
342 transition_ = cell;
343 // Assign an enumeration index to the property and update
344 // SetNextEnumerationIndex.
345 int index = dictionary->NextEnumerationIndex();
346 dictionary->SetNextEnumerationIndex(index + 1);
347 property_details_ = PropertyDetails(attributes, i::DATA, index,
348 PropertyCellType::kUninitialized);
349 PropertyCellType new_type =
350 PropertyCell::UpdatedType(cell, value, property_details_);
351 property_details_ = property_details_.set_cell_type(new_type);
352 cell->set_property_details(property_details_);
353 number_ = entry;
354 has_property_ = true;
355 } else {
356 // Don't set enumeration index (it will be set during value store).
357 property_details_ =
358 PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
359 transition_ = map;
360 }
361 return;
362 }
363
364 Handle<Map> transition =
365 Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
366 state_ = TRANSITION;
367 transition_ = transition;
368
369 if (transition->is_dictionary_map()) {
370 // Don't set enumeration index (it will be set during value store).
371 property_details_ =
372 PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
373 } else {
374 property_details_ = transition->GetLastDescriptorDetails();
375 has_property_ = true;
376 }
377 }
378
ApplyTransitionToDataProperty(Handle<JSObject> receiver)379 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
380 DCHECK_EQ(TRANSITION, state_);
381
382 DCHECK(receiver.is_identical_to(GetStoreTarget()));
383 holder_ = receiver;
384 if (receiver->IsJSGlobalObject()) {
385 state_ = DATA;
386 return;
387 }
388 Handle<Map> transition = transition_map();
389 bool simple_transition = transition->GetBackPointer() == receiver->map();
390 JSObject::MigrateToMap(receiver, transition);
391
392 if (simple_transition) {
393 int number = transition->LastAdded();
394 number_ = static_cast<uint32_t>(number);
395 property_details_ = transition->GetLastDescriptorDetails();
396 state_ = DATA;
397 } else if (receiver->map()->is_dictionary_map()) {
398 Handle<NameDictionary> dictionary(receiver->property_dictionary(),
399 isolate_);
400 int entry;
401 dictionary = NameDictionary::Add(dictionary, name(),
402 isolate_->factory()->uninitialized_value(),
403 property_details_, &entry);
404 receiver->set_properties(*dictionary);
405 // Reload details containing proper enumeration index value.
406 property_details_ = dictionary->DetailsAt(entry);
407 number_ = entry;
408 has_property_ = true;
409 state_ = DATA;
410
411 } else {
412 ReloadPropertyInformation<false>();
413 }
414 }
415
416
Delete()417 void LookupIterator::Delete() {
418 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
419 if (IsElement()) {
420 Handle<JSObject> object = Handle<JSObject>::cast(holder);
421 ElementsAccessor* accessor = object->GetElementsAccessor();
422 accessor->Delete(object, number_);
423 } else {
424 bool is_prototype_map = holder->map()->is_prototype_map();
425 RuntimeCallTimerScope stats_scope(
426 isolate_, is_prototype_map
427 ? &RuntimeCallStats::PrototypeObject_DeleteProperty
428 : &RuntimeCallStats::Object_DeleteProperty);
429
430 PropertyNormalizationMode mode =
431 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
432
433 if (holder->HasFastProperties()) {
434 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
435 "DeletingProperty");
436 ReloadPropertyInformation<false>();
437 }
438 // TODO(verwaest): Get rid of the name_ argument.
439 JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
440 if (holder->IsJSObject()) {
441 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
442 }
443 }
444 state_ = NOT_FOUND;
445 }
446
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)447 void LookupIterator::TransitionToAccessorProperty(
448 Handle<Object> getter, Handle<Object> setter,
449 PropertyAttributes attributes) {
450 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
451 // Can only be called when the receiver is a JSObject. JSProxy has to be
452 // handled via a trap. Adding properties to primitive values is not
453 // observable.
454 Handle<JSObject> receiver = GetStoreTarget();
455 if (!IsElement() && name()->IsPrivate()) {
456 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
457 }
458
459 if (!IsElement() && !receiver->map()->is_dictionary_map()) {
460 Handle<Map> old_map(receiver->map(), isolate_);
461
462 if (!holder_.is_identical_to(receiver)) {
463 holder_ = receiver;
464 state_ = NOT_FOUND;
465 } else if (state_ == INTERCEPTOR) {
466 LookupInRegularHolder<false>(*old_map, *holder_);
467 }
468 int descriptor =
469 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
470
471 Handle<Map> new_map = Map::TransitionToAccessorProperty(
472 isolate_, old_map, name_, descriptor, getter, setter, attributes);
473 bool simple_transition = new_map->GetBackPointer() == receiver->map();
474 JSObject::MigrateToMap(receiver, new_map);
475
476 if (simple_transition) {
477 int number = new_map->LastAdded();
478 number_ = static_cast<uint32_t>(number);
479 property_details_ = new_map->GetLastDescriptorDetails();
480 state_ = ACCESSOR;
481 return;
482 }
483
484 ReloadPropertyInformation<false>();
485 if (!new_map->is_dictionary_map()) return;
486 }
487
488 Handle<AccessorPair> pair;
489 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
490 pair = Handle<AccessorPair>::cast(GetAccessors());
491 // If the component and attributes are identical, nothing has to be done.
492 if (pair->Equals(*getter, *setter)) {
493 if (property_details().attributes() == attributes) {
494 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
495 return;
496 }
497 } else {
498 pair = AccessorPair::Copy(pair);
499 pair->SetComponents(*getter, *setter);
500 }
501 } else {
502 pair = factory()->NewAccessorPair();
503 pair->SetComponents(*getter, *setter);
504 }
505
506 TransitionToAccessorPair(pair, attributes);
507
508 #if VERIFY_HEAP
509 if (FLAG_verify_heap) {
510 receiver->JSObjectVerify();
511 }
512 #endif
513 }
514
515
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)516 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
517 PropertyAttributes attributes) {
518 Handle<JSObject> receiver = GetStoreTarget();
519 holder_ = receiver;
520
521 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
522 PropertyCellType::kMutable);
523
524 if (IsElement()) {
525 // TODO(verwaest): Move code into the element accessor.
526 Handle<SeededNumberDictionary> dictionary =
527 JSObject::NormalizeElements(receiver);
528
529 // We unconditionally pass used_as_prototype=false here because the call
530 // to RequireSlowElements takes care of the required IC clearing and
531 // we don't want to walk the heap twice.
532 dictionary =
533 SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
534 receiver->RequireSlowElements(*dictionary);
535
536 if (receiver->HasSlowArgumentsElements()) {
537 FixedArray* parameter_map = FixedArray::cast(receiver->elements());
538 uint32_t length = parameter_map->length() - 2;
539 if (number_ < length) {
540 parameter_map->set(number_ + 2, heap()->the_hole_value());
541 }
542 FixedArray::cast(receiver->elements())->set(1, *dictionary);
543 } else {
544 receiver->set_elements(*dictionary);
545 }
546
547 ReloadPropertyInformation<true>();
548 } else {
549 PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
550 ? KEEP_INOBJECT_PROPERTIES
551 : CLEAR_INOBJECT_PROPERTIES;
552 // Normalize object to make this operation simple.
553 JSObject::NormalizeProperties(receiver, mode, 0,
554 "TransitionToAccessorPair");
555
556 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
557 JSObject::ReoptimizeIfPrototype(receiver);
558
559 ReloadPropertyInformation<false>();
560 }
561 }
562
563
HolderIsReceiverOrHiddenPrototype() const564 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
565 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
566 // Optimization that only works if configuration_ is not mutable.
567 if (!check_prototype_chain()) return true;
568 DisallowHeapAllocation no_gc;
569 if (*receiver_ == *holder_) return true;
570 if (!receiver_->IsJSReceiver()) return false;
571 JSReceiver* current = JSReceiver::cast(*receiver_);
572 JSReceiver* object = *holder_;
573 if (!current->map()->has_hidden_prototype()) return false;
574 // JSProxy do not occur as hidden prototypes.
575 if (object->IsJSProxy()) return false;
576 PrototypeIterator iter(isolate(), current, kStartAtPrototype,
577 PrototypeIterator::END_AT_NON_HIDDEN);
578 while (!iter.IsAtEnd()) {
579 if (iter.GetCurrent<JSReceiver>() == object) return true;
580 iter.Advance();
581 }
582 return false;
583 }
584
585
FetchValue() const586 Handle<Object> LookupIterator::FetchValue() const {
587 Object* result = NULL;
588 if (IsElement()) {
589 Handle<JSObject> holder = GetHolder<JSObject>();
590 ElementsAccessor* accessor = holder->GetElementsAccessor();
591 return accessor->Get(holder, number_);
592 } else if (holder_->IsJSGlobalObject()) {
593 Handle<JSObject> holder = GetHolder<JSObject>();
594 result = holder->global_dictionary()->ValueAt(number_);
595 DCHECK(result->IsPropertyCell());
596 result = PropertyCell::cast(result)->value();
597 } else if (!holder_->HasFastProperties()) {
598 result = holder_->property_dictionary()->ValueAt(number_);
599 } else if (property_details_.type() == v8::internal::DATA) {
600 Handle<JSObject> holder = GetHolder<JSObject>();
601 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
602 return JSObject::FastPropertyAt(holder, property_details_.representation(),
603 field_index);
604 } else {
605 result = holder_->map()->instance_descriptors()->GetValue(number_);
606 }
607 return handle(result, isolate_);
608 }
609
GetFieldDescriptorIndex() const610 int LookupIterator::GetFieldDescriptorIndex() const {
611 DCHECK(has_property_);
612 DCHECK(holder_->HasFastProperties());
613 DCHECK_EQ(v8::internal::DATA, property_details_.type());
614 return descriptor_number();
615 }
616
GetAccessorIndex() const617 int LookupIterator::GetAccessorIndex() const {
618 DCHECK(has_property_);
619 DCHECK(holder_->HasFastProperties());
620 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
621 return descriptor_number();
622 }
623
624
GetConstantIndex() const625 int LookupIterator::GetConstantIndex() const {
626 DCHECK(has_property_);
627 DCHECK(holder_->HasFastProperties());
628 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
629 DCHECK(!IsElement());
630 return descriptor_number();
631 }
632
633
GetFieldIndex() const634 FieldIndex LookupIterator::GetFieldIndex() const {
635 DCHECK(has_property_);
636 DCHECK(holder_->HasFastProperties());
637 DCHECK_EQ(v8::internal::DATA, property_details_.type());
638 DCHECK(!IsElement());
639 Map* holder_map = holder_->map();
640 int index =
641 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
642 bool is_double = representation().IsDouble();
643 return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
644 }
645
GetFieldType() const646 Handle<FieldType> LookupIterator::GetFieldType() const {
647 DCHECK(has_property_);
648 DCHECK(holder_->HasFastProperties());
649 DCHECK_EQ(v8::internal::DATA, property_details_.type());
650 return handle(
651 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
652 isolate_);
653 }
654
655
GetPropertyCell() const656 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
657 DCHECK(!IsElement());
658 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
659 Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
660 DCHECK(value->IsPropertyCell());
661 return handle(PropertyCell::cast(value), isolate_);
662 }
663
664
GetAccessors() const665 Handle<Object> LookupIterator::GetAccessors() const {
666 DCHECK_EQ(ACCESSOR, state_);
667 return FetchValue();
668 }
669
670
GetDataValue() const671 Handle<Object> LookupIterator::GetDataValue() const {
672 DCHECK_EQ(DATA, state_);
673 Handle<Object> value = FetchValue();
674 return value;
675 }
676
677
WriteDataValue(Handle<Object> value)678 void LookupIterator::WriteDataValue(Handle<Object> value) {
679 DCHECK_EQ(DATA, state_);
680 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
681 if (IsElement()) {
682 Handle<JSObject> object = Handle<JSObject>::cast(holder);
683 ElementsAccessor* accessor = object->GetElementsAccessor();
684 accessor->Set(object, number_, *value);
685 } else if (holder->HasFastProperties()) {
686 if (property_details_.type() == v8::internal::DATA) {
687 JSObject::cast(*holder)->WriteToField(descriptor_number(),
688 property_details_, *value);
689 } else {
690 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
691 }
692 } else if (holder->IsJSGlobalObject()) {
693 GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
694 Object* cell = dictionary->ValueAt(dictionary_entry());
695 DCHECK(cell->IsPropertyCell());
696 PropertyCell::cast(cell)->set_value(*value);
697 } else {
698 NameDictionary* dictionary = holder->property_dictionary();
699 dictionary->ValueAtPut(dictionary_entry(), *value);
700 }
701 }
702
703 template <bool is_element>
SkipInterceptor(JSObject * holder)704 bool LookupIterator::SkipInterceptor(JSObject* holder) {
705 auto info = GetInterceptor<is_element>(holder);
706 // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
707 if (info->non_masking()) {
708 switch (interceptor_state_) {
709 case InterceptorState::kUninitialized:
710 interceptor_state_ = InterceptorState::kSkipNonMasking;
711 // Fall through.
712 case InterceptorState::kSkipNonMasking:
713 return true;
714 case InterceptorState::kProcessNonMasking:
715 return false;
716 }
717 }
718 return interceptor_state_ == InterceptorState::kProcessNonMasking;
719 }
720
NextHolder(Map * map)721 JSReceiver* LookupIterator::NextHolder(Map* map) {
722 DisallowHeapAllocation no_gc;
723 if (map->prototype() == heap()->null_value()) return NULL;
724 if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
725 return JSReceiver::cast(map->prototype());
726 }
727
NotFound(JSReceiver * const holder) const728 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
729 DCHECK(!IsElement());
730 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
731
732 Handle<String> name_string = Handle<String>::cast(name_);
733 if (name_string->length() == 0) return NOT_FOUND;
734
735 return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
736 ? INTEGER_INDEXED_EXOTIC
737 : NOT_FOUND;
738 }
739
740 namespace {
741
742 template <bool is_element>
HasInterceptor(Map * map)743 bool HasInterceptor(Map* map) {
744 return is_element ? map->has_indexed_interceptor()
745 : map->has_named_interceptor();
746 }
747
748 } // namespace
749
750 template <bool is_element>
LookupInSpecialHolder(Map * const map,JSReceiver * const holder)751 LookupIterator::State LookupIterator::LookupInSpecialHolder(
752 Map* const map, JSReceiver* const holder) {
753 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
754 switch (state_) {
755 case NOT_FOUND:
756 if (map->IsJSProxyMap()) {
757 if (is_element || !name_->IsPrivate()) return JSPROXY;
758 }
759 if (map->is_access_check_needed()) {
760 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
761 }
762 // Fall through.
763 case ACCESS_CHECK:
764 if (check_interceptor() && HasInterceptor<is_element>(map) &&
765 !SkipInterceptor<is_element>(JSObject::cast(holder))) {
766 if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
767 }
768 // Fall through.
769 case INTERCEPTOR:
770 if (!is_element && map->IsJSGlobalObjectMap()) {
771 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
772 int number = dict->FindEntry(name_);
773 if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
774 number_ = static_cast<uint32_t>(number);
775 DCHECK(dict->ValueAt(number_)->IsPropertyCell());
776 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
777 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
778 property_details_ = cell->property_details();
779 has_property_ = true;
780 switch (property_details_.kind()) {
781 case v8::internal::kData:
782 return DATA;
783 case v8::internal::kAccessor:
784 return ACCESSOR;
785 }
786 }
787 return LookupInRegularHolder<is_element>(map, holder);
788 case ACCESSOR:
789 case DATA:
790 return NOT_FOUND;
791 case INTEGER_INDEXED_EXOTIC:
792 case JSPROXY:
793 case TRANSITION:
794 UNREACHABLE();
795 }
796 UNREACHABLE();
797 return NOT_FOUND;
798 }
799
800 template <bool is_element>
LookupInRegularHolder(Map * const map,JSReceiver * const holder)801 LookupIterator::State LookupIterator::LookupInRegularHolder(
802 Map* const map, JSReceiver* const holder) {
803 DisallowHeapAllocation no_gc;
804 if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
805 return NOT_FOUND;
806 }
807
808 if (is_element) {
809 JSObject* js_object = JSObject::cast(holder);
810 ElementsAccessor* accessor = js_object->GetElementsAccessor();
811 FixedArrayBase* backing_store = js_object->elements();
812 number_ =
813 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
814 if (number_ == kMaxUInt32) {
815 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
816 }
817 property_details_ = accessor->GetDetails(js_object, number_);
818 } else if (!map->is_dictionary_map()) {
819 DescriptorArray* descriptors = map->instance_descriptors();
820 int number = descriptors->SearchWithCache(isolate_, *name_, map);
821 if (number == DescriptorArray::kNotFound) return NotFound(holder);
822 number_ = static_cast<uint32_t>(number);
823 property_details_ = descriptors->GetDetails(number_);
824 } else {
825 NameDictionary* dict = holder->property_dictionary();
826 int number = dict->FindEntry(name_);
827 if (number == NameDictionary::kNotFound) return NotFound(holder);
828 number_ = static_cast<uint32_t>(number);
829 property_details_ = dict->DetailsAt(number_);
830 }
831 has_property_ = true;
832 switch (property_details_.kind()) {
833 case v8::internal::kData:
834 return DATA;
835 case v8::internal::kAccessor:
836 return ACCESSOR;
837 }
838
839 UNREACHABLE();
840 return state_;
841 }
842
GetInterceptorForFailedAccessCheck() const843 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
844 const {
845 DCHECK_EQ(ACCESS_CHECK, state_);
846 DisallowHeapAllocation no_gc;
847 AccessCheckInfo* access_check_info =
848 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
849 if (access_check_info) {
850 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
851 : access_check_info->named_interceptor();
852 if (interceptor) {
853 return handle(InterceptorInfo::cast(interceptor), isolate_);
854 }
855 }
856 return Handle<InterceptorInfo>();
857 }
858
TryLookupCachedProperty()859 bool LookupIterator::TryLookupCachedProperty() {
860 return state() == LookupIterator::ACCESSOR &&
861 GetAccessors()->IsAccessorPair() && LookupCachedProperty();
862 }
863
LookupCachedProperty()864 bool LookupIterator::LookupCachedProperty() {
865 DCHECK_EQ(state(), LookupIterator::ACCESSOR);
866 DCHECK(GetAccessors()->IsAccessorPair());
867
868 AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
869 Handle<Object> getter(accessor_pair->getter(), isolate());
870 MaybeHandle<Name> maybe_name =
871 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
872 if (maybe_name.is_null()) return false;
873
874 // We have found a cached property! Modify the iterator accordingly.
875 name_ = maybe_name.ToHandleChecked();
876 Restart();
877 CHECK_EQ(state(), LookupIterator::DATA);
878 return true;
879 }
880
881 } // namespace internal
882 } // namespace v8
883