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->IsSpecialReceiverMap()) {
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()->iterator_symbol()) {
195 if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
196 if (holder_->IsJSArray()) {
197 isolate_->InvalidateArrayIteratorProtector();
198 }
199 }
200 }
201
PrepareForDataProperty(Handle<Object> value)202 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
203 DCHECK(state_ == DATA || state_ == ACCESSOR);
204 DCHECK(HolderIsReceiverOrHiddenPrototype());
205
206 Handle<JSObject> holder = GetHolder<JSObject>();
207
208 if (IsElement()) {
209 ElementsKind kind = holder->GetElementsKind();
210 ElementsKind to = value->OptimalElementsKind();
211 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
212 to = GetMoreGeneralElementsKind(kind, to);
213
214 if (kind != to) {
215 JSObject::TransitionElementsKind(holder, to);
216 }
217
218 // Copy the backing store if it is copy-on-write.
219 if (IsFastSmiOrObjectElementsKind(to)) {
220 JSObject::EnsureWritableFastElements(holder);
221 }
222 return;
223 }
224
225 if (holder->IsJSGlobalObject()) {
226 Handle<GlobalDictionary> dictionary(holder->global_dictionary());
227 Handle<PropertyCell> cell(
228 PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
229 DCHECK(!cell->IsTheHole(isolate_));
230 property_details_ = cell->property_details();
231 PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
232 property_details_);
233 return;
234 }
235 if (!holder->HasFastProperties()) return;
236
237 PropertyConstness new_constness = kConst;
238 if (FLAG_track_constant_fields) {
239 if (constness() == kConst) {
240 DCHECK_EQ(kData, property_details_.kind());
241 // Check that current value matches new value otherwise we should make
242 // the property mutable.
243 if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
244 }
245 } else {
246 new_constness = kMutable;
247 }
248
249 Handle<Map> old_map(holder->map(), isolate_);
250 Handle<Map> new_map = Map::PrepareForDataProperty(
251 old_map, descriptor_number(), new_constness, value);
252
253 if (old_map.is_identical_to(new_map)) {
254 // Update the property details if the representation was None.
255 if (representation().IsNone()) {
256 property_details_ =
257 new_map->instance_descriptors()->GetDetails(descriptor_number());
258 }
259 return;
260 }
261
262 JSObject::MigrateToMap(holder, new_map);
263 ReloadPropertyInformation<false>();
264 }
265
266
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)267 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
268 PropertyAttributes attributes) {
269 DCHECK(state_ == DATA || state_ == ACCESSOR);
270 DCHECK(HolderIsReceiverOrHiddenPrototype());
271 Handle<JSObject> holder = GetHolder<JSObject>();
272 if (IsElement()) {
273 DCHECK(!holder->HasFixedTypedArrayElements());
274 DCHECK(attributes != NONE || !holder->HasFastElements());
275 Handle<FixedArrayBase> elements(holder->elements());
276 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
277 attributes);
278 ReloadPropertyInformation<true>();
279 } else if (holder->HasFastProperties()) {
280 Handle<Map> old_map(holder->map(), isolate_);
281 Handle<Map> new_map = Map::ReconfigureExistingProperty(
282 old_map, descriptor_number(), i::kData, attributes);
283 // Force mutable to avoid changing constant value by reconfiguring
284 // kData -> kAccessor -> kData.
285 new_map = Map::PrepareForDataProperty(new_map, descriptor_number(),
286 kMutable, value);
287 JSObject::MigrateToMap(holder, new_map);
288 ReloadPropertyInformation<false>();
289 } else {
290 PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable);
291 if (holder->IsJSGlobalObject()) {
292 Handle<GlobalDictionary> dictionary(holder->global_dictionary());
293
294 Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
295 dictionary, dictionary_entry(), value, details);
296 cell->set_value(*value);
297 property_details_ = cell->property_details();
298 } else {
299 Handle<NameDictionary> dictionary(holder->property_dictionary());
300 PropertyDetails original_details =
301 dictionary->DetailsAt(dictionary_entry());
302 int enumeration_index = original_details.dictionary_index();
303 DCHECK(enumeration_index > 0);
304 details = details.set_index(enumeration_index);
305 dictionary->SetEntry(dictionary_entry(), name(), value, details);
306 property_details_ = details;
307 }
308 state_ = DATA;
309 }
310
311 WriteDataValue(value, true);
312
313 #if VERIFY_HEAP
314 if (FLAG_verify_heap) {
315 holder->JSObjectVerify();
316 }
317 #endif
318 }
319
320 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
321 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSObject> receiver,Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)322 void LookupIterator::PrepareTransitionToDataProperty(
323 Handle<JSObject> receiver, Handle<Object> value,
324 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
325 DCHECK(receiver.is_identical_to(GetStoreTarget()));
326 if (state_ == TRANSITION) return;
327
328 if (!IsElement() && name()->IsPrivate()) {
329 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
330 }
331
332 DCHECK(state_ != LookupIterator::ACCESSOR ||
333 (GetAccessors()->IsAccessorInfo() &&
334 AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
335 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
336 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
337
338 Handle<Map> map(receiver->map(), isolate_);
339
340 // Dictionary maps can always have additional data properties.
341 if (map->is_dictionary_map()) {
342 state_ = TRANSITION;
343 if (map->IsJSGlobalObjectMap()) {
344 // Install a property cell.
345 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
346 int entry;
347 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
348 global, name(), PropertyCellType::kUninitialized, &entry);
349 Handle<GlobalDictionary> dictionary(global->global_dictionary(),
350 isolate_);
351 DCHECK(cell->value()->IsTheHole(isolate_));
352 DCHECK(!value->IsTheHole(isolate_));
353 transition_ = cell;
354 // Assign an enumeration index to the property and update
355 // SetNextEnumerationIndex.
356 int index = dictionary->NextEnumerationIndex();
357 dictionary->SetNextEnumerationIndex(index + 1);
358 property_details_ = PropertyDetails(kData, attributes, index,
359 PropertyCellType::kUninitialized);
360 PropertyCellType new_type =
361 PropertyCell::UpdatedType(cell, value, property_details_);
362 property_details_ = property_details_.set_cell_type(new_type);
363 cell->set_property_details(property_details_);
364 number_ = entry;
365 has_property_ = true;
366 } else {
367 // Don't set enumeration index (it will be set during value store).
368 property_details_ =
369 PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
370 transition_ = map;
371 }
372 return;
373 }
374
375 Handle<Map> transition = Map::TransitionToDataProperty(
376 map, name_, value, attributes, kDefaultFieldConstness, store_mode);
377 state_ = TRANSITION;
378 transition_ = transition;
379
380 if (transition->is_dictionary_map()) {
381 // Don't set enumeration index (it will be set during value store).
382 property_details_ =
383 PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
384 } else {
385 property_details_ = transition->GetLastDescriptorDetails();
386 has_property_ = true;
387 }
388 }
389
ApplyTransitionToDataProperty(Handle<JSObject> receiver)390 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
391 DCHECK_EQ(TRANSITION, state_);
392
393 DCHECK(receiver.is_identical_to(GetStoreTarget()));
394 holder_ = receiver;
395 if (receiver->IsJSGlobalObject()) {
396 state_ = DATA;
397 return;
398 }
399 Handle<Map> transition = transition_map();
400 bool simple_transition = transition->GetBackPointer() == receiver->map();
401 JSObject::MigrateToMap(receiver, transition);
402
403 if (simple_transition) {
404 int number = transition->LastAdded();
405 number_ = static_cast<uint32_t>(number);
406 property_details_ = transition->GetLastDescriptorDetails();
407 state_ = DATA;
408 } else if (receiver->map()->is_dictionary_map()) {
409 Handle<NameDictionary> dictionary(receiver->property_dictionary(),
410 isolate_);
411 int entry;
412 dictionary = NameDictionary::Add(dictionary, name(),
413 isolate_->factory()->uninitialized_value(),
414 property_details_, &entry);
415 receiver->set_properties(*dictionary);
416 // Reload details containing proper enumeration index value.
417 property_details_ = dictionary->DetailsAt(entry);
418 number_ = entry;
419 has_property_ = true;
420 state_ = DATA;
421
422 } else {
423 ReloadPropertyInformation<false>();
424 }
425 }
426
427
Delete()428 void LookupIterator::Delete() {
429 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
430 if (IsElement()) {
431 Handle<JSObject> object = Handle<JSObject>::cast(holder);
432 ElementsAccessor* accessor = object->GetElementsAccessor();
433 accessor->Delete(object, number_);
434 } else {
435 bool is_prototype_map = holder->map()->is_prototype_map();
436 RuntimeCallTimerScope stats_scope(
437 isolate_, is_prototype_map
438 ? &RuntimeCallStats::PrototypeObject_DeleteProperty
439 : &RuntimeCallStats::Object_DeleteProperty);
440
441 PropertyNormalizationMode mode =
442 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
443
444 if (holder->HasFastProperties()) {
445 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
446 "DeletingProperty");
447 ReloadPropertyInformation<false>();
448 }
449 // TODO(verwaest): Get rid of the name_ argument.
450 JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
451 if (holder->IsJSObject()) {
452 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
453 }
454 }
455 state_ = NOT_FOUND;
456 }
457
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)458 void LookupIterator::TransitionToAccessorProperty(
459 Handle<Object> getter, Handle<Object> setter,
460 PropertyAttributes attributes) {
461 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
462 // Can only be called when the receiver is a JSObject. JSProxy has to be
463 // handled via a trap. Adding properties to primitive values is not
464 // observable.
465 Handle<JSObject> receiver = GetStoreTarget();
466 if (!IsElement() && name()->IsPrivate()) {
467 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
468 }
469
470 if (!IsElement() && !receiver->map()->is_dictionary_map()) {
471 Handle<Map> old_map(receiver->map(), isolate_);
472
473 if (!holder_.is_identical_to(receiver)) {
474 holder_ = receiver;
475 state_ = NOT_FOUND;
476 } else if (state_ == INTERCEPTOR) {
477 LookupInRegularHolder<false>(*old_map, *holder_);
478 }
479 int descriptor =
480 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
481
482 Handle<Map> new_map = Map::TransitionToAccessorProperty(
483 isolate_, old_map, name_, descriptor, getter, setter, attributes);
484 bool simple_transition = new_map->GetBackPointer() == receiver->map();
485 JSObject::MigrateToMap(receiver, new_map);
486
487 if (simple_transition) {
488 int number = new_map->LastAdded();
489 number_ = static_cast<uint32_t>(number);
490 property_details_ = new_map->GetLastDescriptorDetails();
491 state_ = ACCESSOR;
492 return;
493 }
494
495 ReloadPropertyInformation<false>();
496 if (!new_map->is_dictionary_map()) return;
497 }
498
499 Handle<AccessorPair> pair;
500 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
501 pair = Handle<AccessorPair>::cast(GetAccessors());
502 // If the component and attributes are identical, nothing has to be done.
503 if (pair->Equals(*getter, *setter)) {
504 if (property_details().attributes() == attributes) {
505 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
506 return;
507 }
508 } else {
509 pair = AccessorPair::Copy(pair);
510 pair->SetComponents(*getter, *setter);
511 }
512 } else {
513 pair = factory()->NewAccessorPair();
514 pair->SetComponents(*getter, *setter);
515 }
516
517 TransitionToAccessorPair(pair, attributes);
518
519 #if VERIFY_HEAP
520 if (FLAG_verify_heap) {
521 receiver->JSObjectVerify();
522 }
523 #endif
524 }
525
526
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)527 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
528 PropertyAttributes attributes) {
529 Handle<JSObject> receiver = GetStoreTarget();
530 holder_ = receiver;
531
532 PropertyDetails details(kAccessor, attributes, 0, PropertyCellType::kMutable);
533
534 if (IsElement()) {
535 // TODO(verwaest): Move code into the element accessor.
536 Handle<SeededNumberDictionary> dictionary =
537 JSObject::NormalizeElements(receiver);
538
539 dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details,
540 receiver);
541 receiver->RequireSlowElements(*dictionary);
542
543 if (receiver->HasSlowArgumentsElements()) {
544 FixedArray* parameter_map = FixedArray::cast(receiver->elements());
545 uint32_t length = parameter_map->length() - 2;
546 if (number_ < length) {
547 parameter_map->set(number_ + 2, heap()->the_hole_value());
548 }
549 FixedArray::cast(receiver->elements())->set(1, *dictionary);
550 } else {
551 receiver->set_elements(*dictionary);
552 }
553
554 ReloadPropertyInformation<true>();
555 } else {
556 PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
557 ? KEEP_INOBJECT_PROPERTIES
558 : CLEAR_INOBJECT_PROPERTIES;
559 // Normalize object to make this operation simple.
560 JSObject::NormalizeProperties(receiver, mode, 0,
561 "TransitionToAccessorPair");
562
563 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
564 JSObject::ReoptimizeIfPrototype(receiver);
565
566 ReloadPropertyInformation<false>();
567 }
568 }
569
570
HolderIsReceiverOrHiddenPrototype() const571 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
572 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
573 // Optimization that only works if configuration_ is not mutable.
574 if (!check_prototype_chain()) return true;
575 DisallowHeapAllocation no_gc;
576 if (*receiver_ == *holder_) return true;
577 if (!receiver_->IsJSReceiver()) return false;
578 JSReceiver* current = JSReceiver::cast(*receiver_);
579 JSReceiver* object = *holder_;
580 if (!current->map()->has_hidden_prototype()) return false;
581 // JSProxy do not occur as hidden prototypes.
582 if (object->IsJSProxy()) return false;
583 PrototypeIterator iter(isolate(), current, kStartAtPrototype,
584 PrototypeIterator::END_AT_NON_HIDDEN);
585 while (!iter.IsAtEnd()) {
586 if (iter.GetCurrent<JSReceiver>() == object) return true;
587 iter.Advance();
588 }
589 return false;
590 }
591
592
FetchValue() const593 Handle<Object> LookupIterator::FetchValue() const {
594 Object* result = NULL;
595 if (IsElement()) {
596 Handle<JSObject> holder = GetHolder<JSObject>();
597 ElementsAccessor* accessor = holder->GetElementsAccessor();
598 return accessor->Get(holder, number_);
599 } else if (holder_->IsJSGlobalObject()) {
600 Handle<JSObject> holder = GetHolder<JSObject>();
601 result = holder->global_dictionary()->ValueAt(number_);
602 DCHECK(result->IsPropertyCell());
603 result = PropertyCell::cast(result)->value();
604 } else if (!holder_->HasFastProperties()) {
605 result = holder_->property_dictionary()->ValueAt(number_);
606 } else if (property_details_.location() == kField) {
607 DCHECK_EQ(kData, property_details_.kind());
608 Handle<JSObject> holder = GetHolder<JSObject>();
609 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
610 return JSObject::FastPropertyAt(holder, property_details_.representation(),
611 field_index);
612 } else {
613 result = holder_->map()->instance_descriptors()->GetValue(number_);
614 }
615 return handle(result, isolate_);
616 }
617
IsConstFieldValueEqualTo(Object * value) const618 bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
619 DCHECK(!IsElement());
620 DCHECK(holder_->HasFastProperties());
621 DCHECK_EQ(kField, property_details_.location());
622 DCHECK_EQ(kConst, property_details_.constness());
623 Handle<JSObject> holder = GetHolder<JSObject>();
624 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
625 if (property_details_.representation().IsDouble()) {
626 if (!value->IsNumber()) return false;
627 uint64_t bits;
628 if (holder->IsUnboxedDoubleField(field_index)) {
629 bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
630 } else {
631 Object* current_value = holder->RawFastPropertyAt(field_index);
632 DCHECK(current_value->IsMutableHeapNumber());
633 bits = HeapNumber::cast(current_value)->value_as_bits();
634 }
635 // Use bit representation of double to to check for hole double, since
636 // manipulating the signaling NaN used for the hole in C++, e.g. with
637 // bit_cast or value(), will change its value on ia32 (the x87 stack is
638 // used to return values and stores to the stack silently clear the
639 // signalling bit).
640 if (bits == kHoleNanInt64) {
641 // Uninitialized double field.
642 return true;
643 }
644 return bit_cast<double>(bits) == value->Number();
645 } else {
646 Object* current_value = holder->RawFastPropertyAt(field_index);
647 return current_value->IsUninitialized(isolate()) || current_value == value;
648 }
649 }
650
GetFieldDescriptorIndex() const651 int LookupIterator::GetFieldDescriptorIndex() const {
652 DCHECK(has_property_);
653 DCHECK(holder_->HasFastProperties());
654 DCHECK_EQ(kField, property_details_.location());
655 DCHECK_EQ(kData, property_details_.kind());
656 return descriptor_number();
657 }
658
GetAccessorIndex() const659 int LookupIterator::GetAccessorIndex() const {
660 DCHECK(has_property_);
661 DCHECK(holder_->HasFastProperties());
662 DCHECK_EQ(kDescriptor, property_details_.location());
663 DCHECK_EQ(kAccessor, property_details_.kind());
664 return descriptor_number();
665 }
666
667
GetConstantIndex() const668 int LookupIterator::GetConstantIndex() const {
669 DCHECK(has_property_);
670 DCHECK(holder_->HasFastProperties());
671 DCHECK_EQ(kDescriptor, property_details_.location());
672 DCHECK_EQ(kData, property_details_.kind());
673 DCHECK(!FLAG_track_constant_fields);
674 DCHECK(!IsElement());
675 return descriptor_number();
676 }
677
GetFieldOwnerMap() const678 Handle<Map> LookupIterator::GetFieldOwnerMap() const {
679 DCHECK(has_property_);
680 DCHECK(holder_->HasFastProperties());
681 DCHECK_EQ(kField, property_details_.location());
682 DCHECK(!IsElement());
683 Map* holder_map = holder_->map();
684 return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
685 }
686
GetFieldIndex() const687 FieldIndex LookupIterator::GetFieldIndex() const {
688 DCHECK(has_property_);
689 DCHECK(holder_->HasFastProperties());
690 DCHECK_EQ(kField, property_details_.location());
691 DCHECK(!IsElement());
692 Map* holder_map = holder_->map();
693 int index =
694 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
695 bool is_double = representation().IsDouble();
696 return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
697 }
698
GetFieldType() const699 Handle<FieldType> LookupIterator::GetFieldType() const {
700 DCHECK(has_property_);
701 DCHECK(holder_->HasFastProperties());
702 DCHECK_EQ(kField, property_details_.location());
703 return handle(
704 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
705 isolate_);
706 }
707
708
GetPropertyCell() const709 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
710 DCHECK(!IsElement());
711 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
712 Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
713 DCHECK(value->IsPropertyCell());
714 return handle(PropertyCell::cast(value), isolate_);
715 }
716
717
GetAccessors() const718 Handle<Object> LookupIterator::GetAccessors() const {
719 DCHECK_EQ(ACCESSOR, state_);
720 return FetchValue();
721 }
722
723
GetDataValue() const724 Handle<Object> LookupIterator::GetDataValue() const {
725 DCHECK_EQ(DATA, state_);
726 Handle<Object> value = FetchValue();
727 return value;
728 }
729
WriteDataValue(Handle<Object> value,bool initializing_store)730 void LookupIterator::WriteDataValue(Handle<Object> value,
731 bool initializing_store) {
732 DCHECK_EQ(DATA, state_);
733 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
734 if (IsElement()) {
735 Handle<JSObject> object = Handle<JSObject>::cast(holder);
736 ElementsAccessor* accessor = object->GetElementsAccessor();
737 accessor->Set(object, number_, *value);
738 } else if (holder->HasFastProperties()) {
739 if (property_details_.location() == kField) {
740 // Check that in case of kConst field the existing value is equal to
741 // |value|.
742 DCHECK_IMPLIES(
743 !initializing_store && property_details_.constness() == kConst,
744 IsConstFieldValueEqualTo(*value));
745 JSObject::cast(*holder)->WriteToField(descriptor_number(),
746 property_details_, *value);
747 } else {
748 DCHECK_EQ(kDescriptor, property_details_.location());
749 DCHECK_EQ(kConst, property_details_.constness());
750 }
751 } else if (holder->IsJSGlobalObject()) {
752 GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
753 Object* cell = dictionary->ValueAt(dictionary_entry());
754 DCHECK(cell->IsPropertyCell());
755 PropertyCell::cast(cell)->set_value(*value);
756 } else {
757 NameDictionary* dictionary = holder->property_dictionary();
758 dictionary->ValueAtPut(dictionary_entry(), *value);
759 }
760 }
761
762 template <bool is_element>
SkipInterceptor(JSObject * holder)763 bool LookupIterator::SkipInterceptor(JSObject* holder) {
764 auto info = GetInterceptor<is_element>(holder);
765 // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
766 if (info->non_masking()) {
767 switch (interceptor_state_) {
768 case InterceptorState::kUninitialized:
769 interceptor_state_ = InterceptorState::kSkipNonMasking;
770 // Fall through.
771 case InterceptorState::kSkipNonMasking:
772 return true;
773 case InterceptorState::kProcessNonMasking:
774 return false;
775 }
776 }
777 return interceptor_state_ == InterceptorState::kProcessNonMasking;
778 }
779
NextHolder(Map * map)780 JSReceiver* LookupIterator::NextHolder(Map* map) {
781 DisallowHeapAllocation no_gc;
782 if (map->prototype() == heap()->null_value()) return NULL;
783 if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
784 return JSReceiver::cast(map->prototype());
785 }
786
NotFound(JSReceiver * const holder) const787 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
788 DCHECK(!IsElement());
789 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
790
791 Handle<String> name_string = Handle<String>::cast(name_);
792 if (name_string->length() == 0) return NOT_FOUND;
793
794 return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
795 ? INTEGER_INDEXED_EXOTIC
796 : NOT_FOUND;
797 }
798
799 namespace {
800
801 template <bool is_element>
HasInterceptor(Map * map)802 bool HasInterceptor(Map* map) {
803 return is_element ? map->has_indexed_interceptor()
804 : map->has_named_interceptor();
805 }
806
807 } // namespace
808
809 template <bool is_element>
LookupInSpecialHolder(Map * const map,JSReceiver * const holder)810 LookupIterator::State LookupIterator::LookupInSpecialHolder(
811 Map* const map, JSReceiver* const holder) {
812 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
813 switch (state_) {
814 case NOT_FOUND:
815 if (map->IsJSProxyMap()) {
816 if (is_element || !name_->IsPrivate()) return JSPROXY;
817 }
818 if (map->is_access_check_needed()) {
819 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
820 }
821 // Fall through.
822 case ACCESS_CHECK:
823 if (check_interceptor() && HasInterceptor<is_element>(map) &&
824 !SkipInterceptor<is_element>(JSObject::cast(holder))) {
825 if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
826 }
827 // Fall through.
828 case INTERCEPTOR:
829 if (!is_element && map->IsJSGlobalObjectMap()) {
830 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
831 int number = dict->FindEntry(name_);
832 if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
833 number_ = static_cast<uint32_t>(number);
834 DCHECK(dict->ValueAt(number_)->IsPropertyCell());
835 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
836 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
837 property_details_ = cell->property_details();
838 has_property_ = true;
839 switch (property_details_.kind()) {
840 case v8::internal::kData:
841 return DATA;
842 case v8::internal::kAccessor:
843 return ACCESSOR;
844 }
845 }
846 return LookupInRegularHolder<is_element>(map, holder);
847 case ACCESSOR:
848 case DATA:
849 return NOT_FOUND;
850 case INTEGER_INDEXED_EXOTIC:
851 case JSPROXY:
852 case TRANSITION:
853 UNREACHABLE();
854 }
855 UNREACHABLE();
856 return NOT_FOUND;
857 }
858
859 template <bool is_element>
LookupInRegularHolder(Map * const map,JSReceiver * const holder)860 LookupIterator::State LookupIterator::LookupInRegularHolder(
861 Map* const map, JSReceiver* const holder) {
862 DisallowHeapAllocation no_gc;
863 if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
864 return NOT_FOUND;
865 }
866
867 if (is_element) {
868 JSObject* js_object = JSObject::cast(holder);
869 ElementsAccessor* accessor = js_object->GetElementsAccessor();
870 FixedArrayBase* backing_store = js_object->elements();
871 number_ =
872 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
873 if (number_ == kMaxUInt32) {
874 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
875 }
876 property_details_ = accessor->GetDetails(js_object, number_);
877 } else if (!map->is_dictionary_map()) {
878 DescriptorArray* descriptors = map->instance_descriptors();
879 int number = descriptors->SearchWithCache(isolate_, *name_, map);
880 if (number == DescriptorArray::kNotFound) return NotFound(holder);
881 number_ = static_cast<uint32_t>(number);
882 property_details_ = descriptors->GetDetails(number_);
883 } else {
884 NameDictionary* dict = holder->property_dictionary();
885 int number = dict->FindEntry(name_);
886 if (number == NameDictionary::kNotFound) return NotFound(holder);
887 number_ = static_cast<uint32_t>(number);
888 property_details_ = dict->DetailsAt(number_);
889 }
890 has_property_ = true;
891 switch (property_details_.kind()) {
892 case v8::internal::kData:
893 return DATA;
894 case v8::internal::kAccessor:
895 return ACCESSOR;
896 }
897
898 UNREACHABLE();
899 return state_;
900 }
901
GetInterceptorForFailedAccessCheck() const902 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
903 const {
904 DCHECK_EQ(ACCESS_CHECK, state_);
905 DisallowHeapAllocation no_gc;
906 AccessCheckInfo* access_check_info =
907 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
908 if (access_check_info) {
909 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
910 : access_check_info->named_interceptor();
911 if (interceptor) {
912 return handle(InterceptorInfo::cast(interceptor), isolate_);
913 }
914 }
915 return Handle<InterceptorInfo>();
916 }
917
TryLookupCachedProperty()918 bool LookupIterator::TryLookupCachedProperty() {
919 return state() == LookupIterator::ACCESSOR &&
920 GetAccessors()->IsAccessorPair() && LookupCachedProperty();
921 }
922
LookupCachedProperty()923 bool LookupIterator::LookupCachedProperty() {
924 DCHECK_EQ(state(), LookupIterator::ACCESSOR);
925 DCHECK(GetAccessors()->IsAccessorPair());
926
927 AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
928 Handle<Object> getter(accessor_pair->getter(), isolate());
929 MaybeHandle<Name> maybe_name =
930 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
931 if (maybe_name.is_null()) return false;
932
933 // We have found a cached property! Modify the iterator accordingly.
934 name_ = maybe_name.ToHandleChecked();
935 Restart();
936 CHECK_EQ(state(), LookupIterator::DATA);
937 return true;
938 }
939
940 } // namespace internal
941 } // namespace v8
942