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