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 #include "src/objects/hash-table-inl.h"
13
14 namespace v8 {
15 namespace internal {
16
17 // static
PropertyOrElement(Isolate * isolate,Handle<Object> receiver,Handle<Object> key,bool * success,Handle<JSReceiver> holder,Configuration configuration)18 LookupIterator LookupIterator::PropertyOrElement(
19 Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
20 bool* success, Handle<JSReceiver> holder, Configuration configuration) {
21 uint32_t index = 0;
22 if (key->ToArrayIndex(&index)) {
23 *success = true;
24 return LookupIterator(isolate, receiver, index, holder, configuration);
25 }
26
27 Handle<Name> name;
28 *success = Object::ToName(isolate, key).ToHandle(&name);
29 if (!*success) {
30 DCHECK(isolate->has_pending_exception());
31 // Return an unusable dummy.
32 return LookupIterator(isolate, receiver,
33 isolate->factory()->empty_string());
34 }
35
36 if (name->AsArrayIndex(&index)) {
37 LookupIterator it(isolate, receiver, index, holder, 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, holder, configuration);
45 }
46
47 // static
PropertyOrElement(Isolate * isolate,Handle<Object> receiver,Handle<Object> key,bool * success,Configuration configuration)48 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
49 Handle<Object> receiver,
50 Handle<Object> key,
51 bool* success,
52 Configuration configuration) {
53 // TODO(mslekova): come up with better way to avoid duplication
54 uint32_t index = 0;
55 if (key->ToArrayIndex(&index)) {
56 *success = true;
57 return LookupIterator(isolate, receiver, index, configuration);
58 }
59
60 Handle<Name> name;
61 *success = Object::ToName(isolate, key).ToHandle(&name);
62 if (!*success) {
63 DCHECK(isolate->has_pending_exception());
64 // Return an unusable dummy.
65 return LookupIterator(isolate, receiver,
66 isolate->factory()->empty_string());
67 }
68
69 if (name->AsArrayIndex(&index)) {
70 LookupIterator it(isolate, receiver, index, configuration);
71 // Here we try to avoid having to rebuild the string later
72 // by storing it on the indexed LookupIterator.
73 it.name_ = name;
74 return it;
75 }
76
77 return LookupIterator(isolate, receiver, name, configuration);
78 }
79
80 // TODO(ishell): Consider removing this way of LookupIterator creation.
81 // static
ForTransitionHandler(Isolate * isolate,Handle<Object> receiver,Handle<Name> name,Handle<Object> value,MaybeHandle<Map> maybe_transition_map)82 LookupIterator LookupIterator::ForTransitionHandler(
83 Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
84 Handle<Object> value, MaybeHandle<Map> maybe_transition_map) {
85 Handle<Map> transition_map;
86 if (!maybe_transition_map.ToHandle(&transition_map) ||
87 !transition_map->IsPrototypeValidityCellValid()) {
88 // This map is not a valid transition handler, so full lookup is required.
89 return LookupIterator(isolate, receiver, name);
90 }
91
92 PropertyDetails details = PropertyDetails::Empty();
93 bool has_property;
94 if (transition_map->is_dictionary_map()) {
95 details = PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
96 has_property = false;
97 } else {
98 details = transition_map->GetLastDescriptorDetails();
99 has_property = true;
100 }
101 #ifdef DEBUG
102 if (name->IsPrivate()) {
103 DCHECK_EQ(DONT_ENUM, details.attributes());
104 } else {
105 DCHECK_EQ(NONE, details.attributes());
106 }
107 #endif
108 LookupIterator it(isolate, receiver, name, transition_map, details,
109 has_property);
110
111 if (!transition_map->is_dictionary_map()) {
112 int descriptor_number = transition_map->LastAdded();
113 Handle<Map> new_map =
114 Map::PrepareForDataProperty(isolate, transition_map, descriptor_number,
115 PropertyConstness::kConst, value);
116 // Reload information; this is no-op if nothing changed.
117 it.property_details_ =
118 new_map->instance_descriptors()->GetDetails(descriptor_number);
119 it.transition_ = new_map;
120 }
121 return it;
122 }
123
LookupIterator(Isolate * isolate,Handle<Object> receiver,Handle<Name> name,Handle<Map> transition_map,PropertyDetails details,bool has_property)124 LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
125 Handle<Name> name, Handle<Map> transition_map,
126 PropertyDetails details, bool has_property)
127 : configuration_(DEFAULT),
128 state_(TRANSITION),
129 has_property_(has_property),
130 interceptor_state_(InterceptorState::kUninitialized),
131 property_details_(details),
132 isolate_(isolate),
133 name_(name),
134 transition_(transition_map),
135 receiver_(receiver),
136 initial_holder_(GetRoot(isolate, receiver)),
137 index_(kMaxUInt32),
138 number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) {
139 holder_ = initial_holder_;
140 }
141
142 template <bool is_element>
Start()143 void LookupIterator::Start() {
144 DisallowHeapAllocation no_gc;
145
146 has_property_ = false;
147 state_ = NOT_FOUND;
148 holder_ = initial_holder_;
149
150 JSReceiver* holder = *holder_;
151 Map* map = holder->map();
152
153 state_ = LookupInHolder<is_element>(map, holder);
154 if (IsFound()) return;
155
156 NextInternal<is_element>(map, holder);
157 }
158
159 template void LookupIterator::Start<true>();
160 template void LookupIterator::Start<false>();
161
Next()162 void LookupIterator::Next() {
163 DCHECK_NE(JSPROXY, state_);
164 DCHECK_NE(TRANSITION, state_);
165 DisallowHeapAllocation no_gc;
166 has_property_ = false;
167
168 JSReceiver* holder = *holder_;
169 Map* map = holder->map();
170
171 if (map->IsSpecialReceiverMap()) {
172 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
173 : LookupInSpecialHolder<false>(map, holder);
174 if (IsFound()) return;
175 }
176
177 IsElement() ? NextInternal<true>(map, holder)
178 : NextInternal<false>(map, holder);
179 }
180
181 template <bool is_element>
NextInternal(Map * map,JSReceiver * holder)182 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
183 do {
184 JSReceiver* maybe_holder = NextHolder(map);
185 if (maybe_holder == nullptr) {
186 if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
187 RestartLookupForNonMaskingInterceptors<is_element>();
188 return;
189 }
190 state_ = NOT_FOUND;
191 if (holder != *holder_) holder_ = handle(holder, isolate_);
192 return;
193 }
194 holder = maybe_holder;
195 map = holder->map();
196 state_ = LookupInHolder<is_element>(map, holder);
197 } while (!IsFound());
198
199 holder_ = handle(holder, isolate_);
200 }
201
202 template <bool is_element>
RestartInternal(InterceptorState interceptor_state)203 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
204 interceptor_state_ = interceptor_state;
205 property_details_ = PropertyDetails::Empty();
206 number_ = static_cast<uint32_t>(DescriptorArray::kNotFound);
207 Start<is_element>();
208 }
209
210 template void LookupIterator::RestartInternal<true>(InterceptorState);
211 template void LookupIterator::RestartInternal<false>(InterceptorState);
212
213 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> receiver,uint32_t index)214 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
215 Isolate* isolate, Handle<Object> receiver, uint32_t index) {
216 // Strings are the only objects with properties (only elements) directly on
217 // the wrapper. Hence we can skip generating the wrapper for all other cases.
218 if (index != kMaxUInt32 && receiver->IsString() &&
219 index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
220 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
221 // context, ensuring that we don't leak it into JS?
222 Handle<JSFunction> constructor = isolate->string_function();
223 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
224 Handle<JSValue>::cast(result)->set_value(*receiver);
225 return result;
226 }
227 auto root =
228 handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
229 if (root->IsNull(isolate)) {
230 isolate->PushStackTraceAndDie(*receiver);
231 }
232 return Handle<JSReceiver>::cast(root);
233 }
234
235
GetReceiverMap() const236 Handle<Map> LookupIterator::GetReceiverMap() const {
237 if (receiver_->IsNumber()) return factory()->heap_number_map();
238 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
239 }
240
HasAccess() const241 bool LookupIterator::HasAccess() const {
242 DCHECK_EQ(ACCESS_CHECK, state_);
243 return isolate_->MayAccess(handle(isolate_->context(), isolate_),
244 GetHolder<JSObject>());
245 }
246
247 template <bool is_element>
ReloadPropertyInformation()248 void LookupIterator::ReloadPropertyInformation() {
249 state_ = BEFORE_PROPERTY;
250 interceptor_state_ = InterceptorState::kUninitialized;
251 state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
252 DCHECK(IsFound() || !holder_->HasFastProperties());
253 }
254
255 namespace {
256
IsTypedArrayFunctionInAnyContext(Isolate * isolate,JSReceiver * holder)257 bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, JSReceiver* holder) {
258 static uint32_t context_slots[] = {
259 #define TYPED_ARRAY_CONTEXT_SLOTS(Type, type, TYPE, ctype) \
260 Context::TYPE##_ARRAY_FUN_INDEX,
261
262 TYPED_ARRAYS(TYPED_ARRAY_CONTEXT_SLOTS)
263 #undef TYPED_ARRAY_CONTEXT_SLOTS
264 };
265
266 if (!holder->IsJSFunction()) return false;
267
268 return std::any_of(
269 std::begin(context_slots), std::end(context_slots),
270 [=](uint32_t slot) { return isolate->IsInAnyContext(holder, slot); });
271 }
272
273 } // namespace
274
InternalUpdateProtector()275 void LookupIterator::InternalUpdateProtector() {
276 if (isolate_->bootstrapper()->IsActive()) return;
277
278 ReadOnlyRoots roots(heap());
279 if (*name_ == roots.constructor_string()) {
280 if (!isolate_->IsArraySpeciesLookupChainIntact() &&
281 !isolate_->IsTypedArraySpeciesLookupChainIntact() &&
282 !isolate_->IsPromiseSpeciesLookupChainIntact())
283 return;
284 // Setting the constructor property could change an instance's @@species
285 if (holder_->IsJSArray()) {
286 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
287 isolate_->CountUsage(
288 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
289 isolate_->InvalidateArraySpeciesProtector();
290 return;
291 } else if (holder_->IsJSPromise()) {
292 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
293 isolate_->InvalidatePromiseSpeciesProtector();
294 return;
295 } else if (holder_->IsJSTypedArray()) {
296 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
297 isolate_->InvalidateTypedArraySpeciesProtector();
298 return;
299 }
300 if (holder_->map()->is_prototype_map()) {
301 DisallowHeapAllocation no_gc;
302 // Setting the constructor of Array.prototype, Promise.prototype or
303 // %TypedArray%.prototype of any realm also needs to invalidate the
304 // @@species protector.
305 // For typed arrays, we check a prototype of this holder since TypedArrays
306 // have different prototypes for each type, and their parent prototype is
307 // pointing the same TYPED_ARRAY_PROTOTYPE.
308 if (isolate_->IsInAnyContext(*holder_,
309 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
310 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
311 isolate_->CountUsage(
312 v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
313 isolate_->InvalidateArraySpeciesProtector();
314 } else if (isolate_->IsInAnyContext(*holder_,
315 Context::PROMISE_PROTOTYPE_INDEX)) {
316 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
317 isolate_->InvalidatePromiseSpeciesProtector();
318 } else if (isolate_->IsInAnyContext(
319 holder_->map()->prototype(),
320 Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
321 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
322 isolate_->InvalidateTypedArraySpeciesProtector();
323 }
324 }
325 } else if (*name_ == roots.next_string()) {
326 if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
327 // Setting the next property of %ArrayIteratorPrototype% also needs to
328 // invalidate the array iterator protector.
329 if (isolate_->IsInAnyContext(
330 *holder_, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)) {
331 isolate_->InvalidateArrayIteratorProtector();
332 }
333 } else if (*name_ == roots.species_symbol()) {
334 if (!isolate_->IsArraySpeciesLookupChainIntact() &&
335 !isolate_->IsTypedArraySpeciesLookupChainIntact() &&
336 !isolate_->IsPromiseSpeciesLookupChainIntact())
337 return;
338 // Setting the Symbol.species property of any Array, Promise or TypedArray
339 // constructor invalidates the @@species protector
340 if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
341 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
342 isolate_->CountUsage(
343 v8::Isolate::UseCounterFeature::kArraySpeciesModified);
344 isolate_->InvalidateArraySpeciesProtector();
345 } else if (isolate_->IsInAnyContext(*holder_,
346 Context::PROMISE_FUNCTION_INDEX)) {
347 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
348 isolate_->InvalidatePromiseSpeciesProtector();
349 } else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
350 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
351 isolate_->InvalidateTypedArraySpeciesProtector();
352 }
353 } else if (*name_ == roots.is_concat_spreadable_symbol()) {
354 if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
355 isolate_->InvalidateIsConcatSpreadableProtector();
356 } else if (*name_ == roots.iterator_symbol()) {
357 if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
358 if (holder_->IsJSArray()) {
359 isolate_->InvalidateArrayIteratorProtector();
360 }
361 } else if (*name_ == roots.resolve_string()) {
362 if (!isolate_->IsPromiseResolveLookupChainIntact()) return;
363 // Setting the "resolve" property on any %Promise% intrinsic object
364 // invalidates the Promise.resolve protector.
365 if (isolate_->IsInAnyContext(*holder_, Context::PROMISE_FUNCTION_INDEX)) {
366 isolate_->InvalidatePromiseResolveProtector();
367 }
368 } else if (*name_ == roots.then_string()) {
369 if (!isolate_->IsPromiseThenLookupChainIntact()) return;
370 // Setting the "then" property on any JSPromise instance or on the
371 // initial %PromisePrototype% invalidates the Promise#then protector.
372 // Also setting the "then" property on the initial %ObjectPrototype%
373 // invalidates the Promise#then protector, since we use this protector
374 // to guard the fast-path in AsyncGeneratorResolve, where we can skip
375 // the ResolvePromise step and go directly to FulfillPromise if we
376 // know that the Object.prototype doesn't contain a "then" method.
377 if (holder_->IsJSPromise() ||
378 isolate_->IsInAnyContext(*holder_,
379 Context::INITIAL_OBJECT_PROTOTYPE_INDEX) ||
380 isolate_->IsInAnyContext(*holder_, Context::PROMISE_PROTOTYPE_INDEX)) {
381 isolate_->InvalidatePromiseThenProtector();
382 }
383 }
384 }
385
PrepareForDataProperty(Handle<Object> value)386 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
387 DCHECK(state_ == DATA || state_ == ACCESSOR);
388 DCHECK(HolderIsReceiverOrHiddenPrototype());
389
390 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
391 // JSProxy does not have fast properties so we do an early return.
392 DCHECK_IMPLIES(holder->IsJSProxy(), !holder->HasFastProperties());
393 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
394 if (holder->IsJSProxy()) return;
395
396 Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
397
398 if (IsElement()) {
399 ElementsKind kind = holder_obj->GetElementsKind();
400 ElementsKind to = value->OptimalElementsKind();
401 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
402 to = GetMoreGeneralElementsKind(kind, to);
403
404 if (kind != to) {
405 JSObject::TransitionElementsKind(holder_obj, to);
406 }
407
408 // Copy the backing store if it is copy-on-write.
409 if (IsSmiOrObjectElementsKind(to)) {
410 JSObject::EnsureWritableFastElements(holder_obj);
411 }
412 return;
413 }
414
415 if (holder_obj->IsJSGlobalObject()) {
416 Handle<GlobalDictionary> dictionary(
417 JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate());
418 Handle<PropertyCell> cell(dictionary->CellAt(dictionary_entry()),
419 isolate());
420 property_details_ = cell->property_details();
421 PropertyCell::PrepareForValue(isolate(), dictionary, dictionary_entry(),
422 value, property_details_);
423 return;
424 }
425 if (!holder_obj->HasFastProperties()) return;
426
427 PropertyConstness new_constness = PropertyConstness::kConst;
428 if (FLAG_track_constant_fields) {
429 if (constness() == PropertyConstness::kConst) {
430 DCHECK_EQ(kData, property_details_.kind());
431 // Check that current value matches new value otherwise we should make
432 // the property mutable.
433 if (!IsConstFieldValueEqualTo(*value))
434 new_constness = PropertyConstness::kMutable;
435 }
436 } else {
437 new_constness = PropertyConstness::kMutable;
438 }
439
440 Handle<Map> old_map(holder_obj->map(), isolate_);
441 Handle<Map> new_map = Map::PrepareForDataProperty(
442 isolate(), old_map, descriptor_number(), new_constness, value);
443
444 if (old_map.is_identical_to(new_map)) {
445 // Update the property details if the representation was None.
446 if (constness() != new_constness || representation().IsNone()) {
447 property_details_ =
448 new_map->instance_descriptors()->GetDetails(descriptor_number());
449 }
450 return;
451 }
452
453 JSObject::MigrateToMap(holder_obj, new_map);
454 ReloadPropertyInformation<false>();
455 }
456
457
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)458 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
459 PropertyAttributes attributes) {
460 DCHECK(state_ == DATA || state_ == ACCESSOR);
461 DCHECK(HolderIsReceiverOrHiddenPrototype());
462
463 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
464
465 // Property details can never change for private fields.
466 if (holder->IsJSProxy()) {
467 DCHECK(name()->IsPrivate());
468 return;
469 }
470
471 Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
472 if (IsElement()) {
473 DCHECK(!holder_obj->HasFixedTypedArrayElements());
474 DCHECK(attributes != NONE || !holder_obj->HasFastElements());
475 Handle<FixedArrayBase> elements(holder_obj->elements(), isolate());
476 holder_obj->GetElementsAccessor()->Reconfigure(holder_obj, elements,
477 number_, value, attributes);
478 ReloadPropertyInformation<true>();
479 } else if (holder_obj->HasFastProperties()) {
480 Handle<Map> old_map(holder_obj->map(), isolate_);
481 Handle<Map> new_map = Map::ReconfigureExistingProperty(
482 isolate_, old_map, descriptor_number(), i::kData, attributes);
483 // Force mutable to avoid changing constant value by reconfiguring
484 // kData -> kAccessor -> kData.
485 new_map =
486 Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(),
487 PropertyConstness::kMutable, value);
488 JSObject::MigrateToMap(holder_obj, new_map);
489 ReloadPropertyInformation<false>();
490 }
491
492 if (!IsElement() && !holder_obj->HasFastProperties()) {
493 PropertyDetails details(kData, attributes, PropertyCellType::kMutable);
494 if (holder_obj->map()->is_prototype_map() &&
495 (property_details_.attributes() & READ_ONLY) == 0 &&
496 (attributes & READ_ONLY) != 0) {
497 // Invalidate prototype validity cell when a property is reconfigured
498 // from writable to read-only as this may invalidate transitioning store
499 // IC handlers.
500 JSObject::InvalidatePrototypeChains(holder->map());
501 }
502 if (holder_obj->IsJSGlobalObject()) {
503 Handle<GlobalDictionary> dictionary(
504 JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate());
505
506 Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
507 isolate(), dictionary, dictionary_entry(), value, details);
508 cell->set_value(*value);
509 property_details_ = cell->property_details();
510 } else {
511 Handle<NameDictionary> dictionary(holder_obj->property_dictionary(),
512 isolate());
513 PropertyDetails original_details =
514 dictionary->DetailsAt(dictionary_entry());
515 int enumeration_index = original_details.dictionary_index();
516 DCHECK_GT(enumeration_index, 0);
517 details = details.set_index(enumeration_index);
518 dictionary->SetEntry(isolate(), dictionary_entry(), *name(), *value,
519 details);
520 property_details_ = details;
521 }
522 state_ = DATA;
523 }
524
525 WriteDataValue(value, true);
526
527 #if VERIFY_HEAP
528 if (FLAG_verify_heap) {
529 holder->HeapObjectVerify(isolate());
530 }
531 #endif
532 }
533
534 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
535 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSReceiver> receiver,Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)536 void LookupIterator::PrepareTransitionToDataProperty(
537 Handle<JSReceiver> receiver, Handle<Object> value,
538 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
539 DCHECK_IMPLIES(receiver->IsJSProxy(), name()->IsPrivate());
540 DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
541 if (state_ == TRANSITION) return;
542
543 if (!IsElement() && name()->IsPrivate()) {
544 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
545 }
546
547 DCHECK(state_ != LookupIterator::ACCESSOR ||
548 (GetAccessors()->IsAccessorInfo() &&
549 AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
550 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
551 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
552
553 Handle<Map> map(receiver->map(), isolate_);
554
555 // Dictionary maps can always have additional data properties.
556 if (map->is_dictionary_map()) {
557 state_ = TRANSITION;
558 if (map->IsJSGlobalObjectMap()) {
559 // Install a property cell.
560 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
561 int entry;
562 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
563 global, name(), PropertyCellType::kUninitialized, &entry);
564 Handle<GlobalDictionary> dictionary(global->global_dictionary(),
565 isolate_);
566 DCHECK(cell->value()->IsTheHole(isolate_));
567 DCHECK(!value->IsTheHole(isolate_));
568 transition_ = cell;
569 // Assign an enumeration index to the property and update
570 // SetNextEnumerationIndex.
571 int index = dictionary->NextEnumerationIndex();
572 dictionary->SetNextEnumerationIndex(index + 1);
573 property_details_ = PropertyDetails(
574 kData, attributes, PropertyCellType::kUninitialized, index);
575 PropertyCellType new_type =
576 PropertyCell::UpdatedType(isolate(), cell, value, property_details_);
577 property_details_ = property_details_.set_cell_type(new_type);
578 cell->set_property_details(property_details_);
579 number_ = entry;
580 has_property_ = true;
581 } else {
582 // Don't set enumeration index (it will be set during value store).
583 property_details_ =
584 PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
585 transition_ = map;
586 }
587 return;
588 }
589
590 Handle<Map> transition =
591 Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
592 kDefaultFieldConstness, store_mode);
593 state_ = TRANSITION;
594 transition_ = transition;
595
596 if (transition->is_dictionary_map()) {
597 // Don't set enumeration index (it will be set during value store).
598 property_details_ =
599 PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
600 } else {
601 property_details_ = transition->GetLastDescriptorDetails();
602 has_property_ = true;
603 }
604 }
605
ApplyTransitionToDataProperty(Handle<JSReceiver> receiver)606 void LookupIterator::ApplyTransitionToDataProperty(
607 Handle<JSReceiver> receiver) {
608 DCHECK_EQ(TRANSITION, state_);
609
610 DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
611 holder_ = receiver;
612 if (receiver->IsJSGlobalObject()) {
613 JSObject::InvalidatePrototypeChains(receiver->map());
614 state_ = DATA;
615 return;
616 }
617 Handle<Map> transition = transition_map();
618 bool simple_transition = transition->GetBackPointer() == receiver->map();
619
620 if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
621 !transition->IsPrototypeValidityCellValid()) {
622 // Only LookupIterator instances with DEFAULT (full prototype chain)
623 // configuration can produce valid transition handler maps.
624 Handle<Object> validity_cell =
625 Map::GetOrCreatePrototypeChainValidityCell(transition, isolate());
626 transition->set_prototype_validity_cell(*validity_cell);
627 }
628
629 if (!receiver->IsJSProxy()) {
630 JSObject::MigrateToMap(Handle<JSObject>::cast(receiver), transition);
631 }
632
633 if (simple_transition) {
634 int number = transition->LastAdded();
635 number_ = static_cast<uint32_t>(number);
636 property_details_ = transition->GetLastDescriptorDetails();
637 state_ = DATA;
638 } else if (receiver->map()->is_dictionary_map()) {
639 Handle<NameDictionary> dictionary(receiver->property_dictionary(),
640 isolate_);
641 int entry;
642 if (receiver->map()->is_prototype_map() && receiver->IsJSObject()) {
643 JSObject::InvalidatePrototypeChains(receiver->map());
644 }
645 dictionary = NameDictionary::Add(isolate(), dictionary, name(),
646 isolate_->factory()->uninitialized_value(),
647 property_details_, &entry);
648 receiver->SetProperties(*dictionary);
649 // Reload details containing proper enumeration index value.
650 property_details_ = dictionary->DetailsAt(entry);
651 number_ = entry;
652 has_property_ = true;
653 state_ = DATA;
654
655 } else {
656 ReloadPropertyInformation<false>();
657 }
658 }
659
660
Delete()661 void LookupIterator::Delete() {
662 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
663 if (IsElement()) {
664 Handle<JSObject> object = Handle<JSObject>::cast(holder);
665 ElementsAccessor* accessor = object->GetElementsAccessor();
666 accessor->Delete(object, number_);
667 } else {
668 DCHECK(!name()->IsPrivateField());
669 bool is_prototype_map = holder->map()->is_prototype_map();
670 RuntimeCallTimerScope stats_scope(
671 isolate_, is_prototype_map
672 ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
673 : RuntimeCallCounterId::kObject_DeleteProperty);
674
675 PropertyNormalizationMode mode =
676 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
677
678 if (holder->HasFastProperties()) {
679 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
680 "DeletingProperty");
681 ReloadPropertyInformation<false>();
682 }
683 JSReceiver::DeleteNormalizedProperty(holder, number_);
684 if (holder->IsJSObject()) {
685 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
686 }
687 }
688 state_ = NOT_FOUND;
689 }
690
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)691 void LookupIterator::TransitionToAccessorProperty(
692 Handle<Object> getter, Handle<Object> setter,
693 PropertyAttributes attributes) {
694 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
695 // Can only be called when the receiver is a JSObject. JSProxy has to be
696 // handled via a trap. Adding properties to primitive values is not
697 // observable.
698 Handle<JSObject> receiver = GetStoreTarget<JSObject>();
699 if (!IsElement() && name()->IsPrivate()) {
700 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
701 }
702
703 if (!IsElement() && !receiver->map()->is_dictionary_map()) {
704 Handle<Map> old_map(receiver->map(), isolate_);
705
706 if (!holder_.is_identical_to(receiver)) {
707 holder_ = receiver;
708 state_ = NOT_FOUND;
709 } else if (state_ == INTERCEPTOR) {
710 LookupInRegularHolder<false>(*old_map, *holder_);
711 }
712 int descriptor =
713 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
714
715 Handle<Map> new_map = Map::TransitionToAccessorProperty(
716 isolate_, old_map, name_, descriptor, getter, setter, attributes);
717 bool simple_transition = new_map->GetBackPointer() == receiver->map();
718 JSObject::MigrateToMap(receiver, new_map);
719
720 if (simple_transition) {
721 int number = new_map->LastAdded();
722 number_ = static_cast<uint32_t>(number);
723 property_details_ = new_map->GetLastDescriptorDetails();
724 state_ = ACCESSOR;
725 return;
726 }
727
728 ReloadPropertyInformation<false>();
729 if (!new_map->is_dictionary_map()) return;
730 }
731
732 Handle<AccessorPair> pair;
733 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
734 pair = Handle<AccessorPair>::cast(GetAccessors());
735 // If the component and attributes are identical, nothing has to be done.
736 if (pair->Equals(*getter, *setter)) {
737 if (property_details().attributes() == attributes) {
738 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
739 return;
740 }
741 } else {
742 pair = AccessorPair::Copy(isolate(), pair);
743 pair->SetComponents(*getter, *setter);
744 }
745 } else {
746 pair = factory()->NewAccessorPair();
747 pair->SetComponents(*getter, *setter);
748 }
749
750 TransitionToAccessorPair(pair, attributes);
751
752 #if VERIFY_HEAP
753 if (FLAG_verify_heap) {
754 receiver->JSObjectVerify(isolate());
755 }
756 #endif
757 }
758
759
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)760 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
761 PropertyAttributes attributes) {
762 Handle<JSObject> receiver = GetStoreTarget<JSObject>();
763 holder_ = receiver;
764
765 PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable);
766
767 if (IsElement()) {
768 // TODO(verwaest): Move code into the element accessor.
769 isolate_->CountUsage(v8::Isolate::kIndexAccessor);
770 Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(receiver);
771
772 dictionary = NumberDictionary::Set(isolate_, dictionary, index_, pair,
773 receiver, details);
774 receiver->RequireSlowElements(*dictionary);
775
776 if (receiver->HasSlowArgumentsElements()) {
777 FixedArray* parameter_map = FixedArray::cast(receiver->elements());
778 uint32_t length = parameter_map->length() - 2;
779 if (number_ < length) {
780 parameter_map->set(number_ + 2, ReadOnlyRoots(heap()).the_hole_value());
781 }
782 FixedArray::cast(receiver->elements())->set(1, *dictionary);
783 } else {
784 receiver->set_elements(*dictionary);
785 }
786
787 ReloadPropertyInformation<true>();
788 } else {
789 PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES;
790 if (receiver->map()->is_prototype_map()) {
791 JSObject::InvalidatePrototypeChains(receiver->map());
792 mode = KEEP_INOBJECT_PROPERTIES;
793 }
794
795 // Normalize object to make this operation simple.
796 JSObject::NormalizeProperties(receiver, mode, 0,
797 "TransitionToAccessorPair");
798
799 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
800 JSObject::ReoptimizeIfPrototype(receiver);
801
802 ReloadPropertyInformation<false>();
803 }
804 }
805
HolderIsReceiver() const806 bool LookupIterator::HolderIsReceiver() const {
807 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
808 // Optimization that only works if configuration_ is not mutable.
809 if (!check_prototype_chain()) return true;
810 return *receiver_ == *holder_;
811 }
812
HolderIsReceiverOrHiddenPrototype() const813 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
814 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
815 // Optimization that only works if configuration_ is not mutable.
816 if (!check_prototype_chain()) return true;
817 DisallowHeapAllocation no_gc;
818 if (*receiver_ == *holder_) return true;
819 if (!receiver_->IsJSReceiver()) return false;
820 JSReceiver* current = JSReceiver::cast(*receiver_);
821 JSReceiver* object = *holder_;
822 if (!current->map()->has_hidden_prototype()) return false;
823 // JSProxy do not occur as hidden prototypes.
824 if (object->IsJSProxy()) return false;
825 PrototypeIterator iter(isolate(), current, kStartAtPrototype,
826 PrototypeIterator::END_AT_NON_HIDDEN);
827 while (!iter.IsAtEnd()) {
828 if (iter.GetCurrent<JSReceiver>() == object) return true;
829 iter.Advance();
830 }
831 return false;
832 }
833
834
FetchValue() const835 Handle<Object> LookupIterator::FetchValue() const {
836 Object* result = nullptr;
837 if (IsElement()) {
838 Handle<JSObject> holder = GetHolder<JSObject>();
839 ElementsAccessor* accessor = holder->GetElementsAccessor();
840 return accessor->Get(holder, number_);
841 } else if (holder_->IsJSGlobalObject()) {
842 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
843 result = holder->global_dictionary()->ValueAt(number_);
844 } else if (!holder_->HasFastProperties()) {
845 result = holder_->property_dictionary()->ValueAt(number_);
846 } else if (property_details_.location() == kField) {
847 DCHECK_EQ(kData, property_details_.kind());
848 Handle<JSObject> holder = GetHolder<JSObject>();
849 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
850 return JSObject::FastPropertyAt(holder, property_details_.representation(),
851 field_index);
852 } else {
853 result = holder_->map()->instance_descriptors()->GetStrongValue(number_);
854 }
855 return handle(result, isolate_);
856 }
857
IsConstFieldValueEqualTo(Object * value) const858 bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
859 DCHECK(!IsElement());
860 DCHECK(holder_->HasFastProperties());
861 DCHECK_EQ(kField, property_details_.location());
862 DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
863 Handle<JSObject> holder = GetHolder<JSObject>();
864 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
865 if (property_details_.representation().IsDouble()) {
866 if (!value->IsNumber()) return false;
867 uint64_t bits;
868 if (holder->IsUnboxedDoubleField(field_index)) {
869 bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
870 } else {
871 Object* current_value = holder->RawFastPropertyAt(field_index);
872 DCHECK(current_value->IsMutableHeapNumber());
873 bits = MutableHeapNumber::cast(current_value)->value_as_bits();
874 }
875 // Use bit representation of double to to check for hole double, since
876 // manipulating the signaling NaN used for the hole in C++, e.g. with
877 // bit_cast or value(), will change its value on ia32 (the x87 stack is
878 // used to return values and stores to the stack silently clear the
879 // signalling bit).
880 if (bits == kHoleNanInt64) {
881 // Uninitialized double field.
882 return true;
883 }
884 return bit_cast<double>(bits) == value->Number();
885 } else {
886 Object* current_value = holder->RawFastPropertyAt(field_index);
887 return current_value->IsUninitialized(isolate()) || current_value == value;
888 }
889 }
890
GetFieldDescriptorIndex() const891 int LookupIterator::GetFieldDescriptorIndex() const {
892 DCHECK(has_property_);
893 DCHECK(holder_->HasFastProperties());
894 DCHECK_EQ(kField, property_details_.location());
895 DCHECK_EQ(kData, property_details_.kind());
896 return descriptor_number();
897 }
898
GetAccessorIndex() const899 int LookupIterator::GetAccessorIndex() const {
900 DCHECK(has_property_);
901 DCHECK(holder_->HasFastProperties());
902 DCHECK_EQ(kDescriptor, property_details_.location());
903 DCHECK_EQ(kAccessor, property_details_.kind());
904 return descriptor_number();
905 }
906
907
GetConstantIndex() const908 int LookupIterator::GetConstantIndex() const {
909 DCHECK(has_property_);
910 DCHECK(holder_->HasFastProperties());
911 DCHECK_EQ(kDescriptor, property_details_.location());
912 DCHECK_EQ(kData, property_details_.kind());
913 DCHECK(!FLAG_track_constant_fields);
914 DCHECK(!IsElement());
915 return descriptor_number();
916 }
917
GetFieldOwnerMap() const918 Handle<Map> LookupIterator::GetFieldOwnerMap() const {
919 DCHECK(has_property_);
920 DCHECK(holder_->HasFastProperties());
921 DCHECK_EQ(kField, property_details_.location());
922 DCHECK(!IsElement());
923 Map* holder_map = holder_->map();
924 return handle(holder_map->FindFieldOwner(isolate(), descriptor_number()),
925 isolate_);
926 }
927
GetFieldIndex() const928 FieldIndex LookupIterator::GetFieldIndex() const {
929 DCHECK(has_property_);
930 DCHECK(holder_->HasFastProperties());
931 DCHECK_EQ(kField, property_details_.location());
932 DCHECK(!IsElement());
933 return FieldIndex::ForDescriptor(holder_->map(), descriptor_number());
934 }
935
GetFieldType() const936 Handle<FieldType> LookupIterator::GetFieldType() const {
937 DCHECK(has_property_);
938 DCHECK(holder_->HasFastProperties());
939 DCHECK_EQ(kField, property_details_.location());
940 return handle(
941 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
942 isolate_);
943 }
944
945
GetPropertyCell() const946 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
947 DCHECK(!IsElement());
948 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
949 return handle(holder->global_dictionary()->CellAt(dictionary_entry()),
950 isolate_);
951 }
952
953
GetAccessors() const954 Handle<Object> LookupIterator::GetAccessors() const {
955 DCHECK_EQ(ACCESSOR, state_);
956 return FetchValue();
957 }
958
959
GetDataValue() const960 Handle<Object> LookupIterator::GetDataValue() const {
961 DCHECK_EQ(DATA, state_);
962 Handle<Object> value = FetchValue();
963 return value;
964 }
965
WriteDataValue(Handle<Object> value,bool initializing_store)966 void LookupIterator::WriteDataValue(Handle<Object> value,
967 bool initializing_store) {
968 DCHECK_EQ(DATA, state_);
969 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
970 if (IsElement()) {
971 Handle<JSObject> object = Handle<JSObject>::cast(holder);
972 ElementsAccessor* accessor = object->GetElementsAccessor();
973 accessor->Set(object, number_, *value);
974 } else if (holder->HasFastProperties()) {
975 if (property_details_.location() == kField) {
976 // Check that in case of VariableMode::kConst field the existing value is
977 // equal to |value|.
978 DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
979 PropertyConstness::kConst,
980 IsConstFieldValueEqualTo(*value));
981 JSObject::cast(*holder)->WriteToField(descriptor_number(),
982 property_details_, *value);
983 } else {
984 DCHECK_EQ(kDescriptor, property_details_.location());
985 DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
986 }
987 } else if (holder->IsJSGlobalObject()) {
988 GlobalDictionary* dictionary =
989 JSGlobalObject::cast(*holder)->global_dictionary();
990 dictionary->CellAt(dictionary_entry())->set_value(*value);
991 } else {
992 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
993 NameDictionary* dictionary = holder->property_dictionary();
994 dictionary->ValueAtPut(dictionary_entry(), *value);
995 }
996 }
997
998 template <bool is_element>
SkipInterceptor(JSObject * holder)999 bool LookupIterator::SkipInterceptor(JSObject* holder) {
1000 auto info = GetInterceptor<is_element>(holder);
1001 if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) {
1002 return true;
1003 }
1004 if (info->non_masking()) {
1005 switch (interceptor_state_) {
1006 case InterceptorState::kUninitialized:
1007 interceptor_state_ = InterceptorState::kSkipNonMasking;
1008 V8_FALLTHROUGH;
1009 case InterceptorState::kSkipNonMasking:
1010 return true;
1011 case InterceptorState::kProcessNonMasking:
1012 return false;
1013 }
1014 }
1015 return interceptor_state_ == InterceptorState::kProcessNonMasking;
1016 }
1017
NextHolder(Map * map)1018 JSReceiver* LookupIterator::NextHolder(Map* map) {
1019 DisallowHeapAllocation no_gc;
1020 if (map->prototype() == ReadOnlyRoots(heap()).null_value()) return nullptr;
1021 if (!check_prototype_chain() && !map->has_hidden_prototype()) return nullptr;
1022 return JSReceiver::cast(map->prototype());
1023 }
1024
NotFound(JSReceiver * const holder) const1025 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
1026 DCHECK(!IsElement());
1027 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
1028
1029 Handle<String> name_string = Handle<String>::cast(name_);
1030 if (name_string->length() == 0) return NOT_FOUND;
1031
1032 return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
1033 ? INTEGER_INDEXED_EXOTIC
1034 : NOT_FOUND;
1035 }
1036
1037 namespace {
1038
1039 template <bool is_element>
HasInterceptor(Map * map)1040 bool HasInterceptor(Map* map) {
1041 return is_element ? map->has_indexed_interceptor()
1042 : map->has_named_interceptor();
1043 }
1044
1045 } // namespace
1046
1047 template <bool is_element>
LookupInSpecialHolder(Map * const map,JSReceiver * const holder)1048 LookupIterator::State LookupIterator::LookupInSpecialHolder(
1049 Map* const map, JSReceiver* const holder) {
1050 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
1051 switch (state_) {
1052 case NOT_FOUND:
1053 if (map->IsJSProxyMap()) {
1054 if (is_element || !name_->IsPrivate()) return JSPROXY;
1055 }
1056 if (map->is_access_check_needed()) {
1057 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
1058 }
1059 V8_FALLTHROUGH;
1060 case ACCESS_CHECK:
1061 if (check_interceptor() && HasInterceptor<is_element>(map) &&
1062 !SkipInterceptor<is_element>(JSObject::cast(holder))) {
1063 if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
1064 }
1065 V8_FALLTHROUGH;
1066 case INTERCEPTOR:
1067 if (!is_element && map->IsJSGlobalObjectMap()) {
1068 GlobalDictionary* dict =
1069 JSGlobalObject::cast(holder)->global_dictionary();
1070 int number = dict->FindEntry(isolate(), name_);
1071 if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
1072 number_ = static_cast<uint32_t>(number);
1073 PropertyCell* cell = dict->CellAt(number_);
1074 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
1075 property_details_ = cell->property_details();
1076 has_property_ = true;
1077 switch (property_details_.kind()) {
1078 case v8::internal::kData:
1079 return DATA;
1080 case v8::internal::kAccessor:
1081 return ACCESSOR;
1082 }
1083 }
1084 return LookupInRegularHolder<is_element>(map, holder);
1085 case ACCESSOR:
1086 case DATA:
1087 return NOT_FOUND;
1088 case INTEGER_INDEXED_EXOTIC:
1089 case JSPROXY:
1090 case TRANSITION:
1091 UNREACHABLE();
1092 }
1093 UNREACHABLE();
1094 }
1095
1096 template <bool is_element>
LookupInRegularHolder(Map * const map,JSReceiver * const holder)1097 LookupIterator::State LookupIterator::LookupInRegularHolder(
1098 Map* const map, JSReceiver* const holder) {
1099 DisallowHeapAllocation no_gc;
1100 if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
1101 return NOT_FOUND;
1102 }
1103
1104 if (is_element) {
1105 JSObject* js_object = JSObject::cast(holder);
1106 ElementsAccessor* accessor = js_object->GetElementsAccessor();
1107 FixedArrayBase* backing_store = js_object->elements();
1108 number_ =
1109 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
1110 if (number_ == kMaxUInt32) {
1111 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
1112 }
1113 property_details_ = accessor->GetDetails(js_object, number_);
1114 } else if (!map->is_dictionary_map()) {
1115 DescriptorArray* descriptors = map->instance_descriptors();
1116 int number = descriptors->SearchWithCache(isolate_, *name_, map);
1117 if (number == DescriptorArray::kNotFound) return NotFound(holder);
1118 number_ = static_cast<uint32_t>(number);
1119 property_details_ = descriptors->GetDetails(number_);
1120 } else {
1121 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
1122 NameDictionary* dict = holder->property_dictionary();
1123 int number = dict->FindEntry(isolate(), name_);
1124 if (number == NameDictionary::kNotFound) return NotFound(holder);
1125 number_ = static_cast<uint32_t>(number);
1126 property_details_ = dict->DetailsAt(number_);
1127 }
1128 has_property_ = true;
1129 switch (property_details_.kind()) {
1130 case v8::internal::kData:
1131 return DATA;
1132 case v8::internal::kAccessor:
1133 return ACCESSOR;
1134 }
1135
1136 UNREACHABLE();
1137 }
1138
GetInterceptorForFailedAccessCheck() const1139 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
1140 const {
1141 DCHECK_EQ(ACCESS_CHECK, state_);
1142 DisallowHeapAllocation no_gc;
1143 AccessCheckInfo* access_check_info =
1144 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
1145 if (access_check_info) {
1146 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
1147 : access_check_info->named_interceptor();
1148 if (interceptor) {
1149 return handle(InterceptorInfo::cast(interceptor), isolate_);
1150 }
1151 }
1152 return Handle<InterceptorInfo>();
1153 }
1154
TryLookupCachedProperty()1155 bool LookupIterator::TryLookupCachedProperty() {
1156 return state() == LookupIterator::ACCESSOR &&
1157 GetAccessors()->IsAccessorPair() && LookupCachedProperty();
1158 }
1159
LookupCachedProperty()1160 bool LookupIterator::LookupCachedProperty() {
1161 DCHECK_EQ(state(), LookupIterator::ACCESSOR);
1162 DCHECK(GetAccessors()->IsAccessorPair());
1163
1164 AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
1165 Handle<Object> getter(accessor_pair->getter(), isolate());
1166 MaybeHandle<Name> maybe_name =
1167 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
1168 if (maybe_name.is_null()) return false;
1169
1170 // We have found a cached property! Modify the iterator accordingly.
1171 name_ = maybe_name.ToHandleChecked();
1172 Restart();
1173 CHECK_EQ(state(), LookupIterator::DATA);
1174 return true;
1175 }
1176
1177 } // namespace internal
1178 } // namespace v8
1179