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/deoptimizer.h"
9 : #include "src/elements.h"
10 : #include "src/field-type.h"
11 : #include "src/isolate-inl.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : // static
17 120305360 : LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
18 : Handle<Object> receiver,
19 : Handle<Object> key,
20 : bool* success,
21 : Configuration configuration) {
22 120305360 : uint32_t index = 0;
23 120305361 : if (key->ToArrayIndex(&index)) {
24 37363910 : *success = true;
25 37363910 : return LookupIterator(isolate, receiver, index, configuration);
26 : }
27 :
28 : Handle<Name> name;
29 165882901 : *success = Object::ToName(isolate, key).ToHandle(&name);
30 82941450 : if (!*success) {
31 : DCHECK(isolate->has_pending_exception());
32 : // Return an unusable dummy.
33 49 : return LookupIterator(receiver, isolate->factory()->empty_string());
34 : }
35 :
36 82941401 : if (name->AsArrayIndex(&index)) {
37 397269 : LookupIterator it(isolate, receiver, index, configuration);
38 : // Here we try to avoid having to rebuild the string later
39 : // by storing it on the indexed LookupIterator.
40 397269 : it.name_ = name;
41 397269 : return it;
42 : }
43 :
44 82544129 : return LookupIterator(receiver, name, configuration);
45 : }
46 :
47 : template <bool is_element>
48 486874270 : void LookupIterator::Start() {
49 : DisallowHeapAllocation no_gc;
50 :
51 486874270 : has_property_ = false;
52 486874270 : state_ = NOT_FOUND;
53 486874270 : holder_ = initial_holder_;
54 :
55 : JSReceiver* holder = *holder_;
56 : Map* map = holder->map();
57 :
58 486874270 : state_ = LookupInHolder<is_element>(map, holder);
59 973748466 : if (IsFound()) return;
60 :
61 262551337 : NextInternal<is_element>(map, holder);
62 : }
63 :
64 : template void LookupIterator::Start<true>();
65 : template void LookupIterator::Start<false>();
66 :
67 28568048 : void LookupIterator::Next() {
68 : DCHECK_NE(JSPROXY, state_);
69 : DCHECK_NE(TRANSITION, state_);
70 : DisallowHeapAllocation no_gc;
71 9741730 : has_property_ = false;
72 :
73 : JSReceiver* holder = *holder_;
74 : Map* map = holder->map();
75 :
76 9741730 : if (map->IsSpecialReceiverMap()) {
77 : state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
78 9741697 : : LookupInSpecialHolder<false>(map, holder);
79 19483427 : if (IsFound()) return;
80 : }
81 :
82 83650 : IsElement() ? NextInternal<true>(map, holder)
83 18085592 : : NextInternal<false>(map, holder);
84 : }
85 :
86 : template <bool is_element>
87 271635977 : void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
88 312710701 : do {
89 : JSReceiver* maybe_holder = NextHolder(map);
90 553588200 : if (maybe_holder == nullptr) {
91 240877499 : if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
92 175 : RestartLookupForNonMaskingInterceptors<is_element>();
93 175 : return;
94 : }
95 240877324 : state_ = NOT_FOUND;
96 388407359 : if (holder != *holder_) holder_ = handle(holder, isolate_);
97 : return;
98 : }
99 : holder = maybe_holder;
100 : map = holder->map();
101 312710701 : state_ = LookupInHolder<is_element>(map, holder);
102 : } while (!IsFound());
103 :
104 61516956 : holder_ = handle(holder, isolate_);
105 : }
106 :
107 : template <bool is_element>
108 5420846 : void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
109 5420846 : interceptor_state_ = interceptor_state;
110 5420846 : property_details_ = PropertyDetails::Empty();
111 5420846 : number_ = static_cast<uint32_t>(DescriptorArray::kNotFound);
112 5420846 : Start<is_element>();
113 5420846 : }
114 :
115 : template void LookupIterator::RestartInternal<true>(InterceptorState);
116 : template void LookupIterator::RestartInternal<false>(InterceptorState);
117 :
118 : // static
119 252579 : Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
120 : Isolate* isolate, Handle<Object> receiver, uint32_t index) {
121 : // Strings are the only objects with properties (only elements) directly on
122 : // the wrapper. Hence we can skip generating the wrapper for all other cases.
123 282435 : if (index != kMaxUInt32 && receiver->IsString() &&
124 6576 : index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
125 : // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
126 : // context, ensuring that we don't leak it into JS?
127 1705 : Handle<JSFunction> constructor = isolate->string_function();
128 1705 : Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
129 1705 : Handle<JSValue>::cast(result)->set_value(*receiver);
130 1705 : return result;
131 : }
132 : auto root =
133 250874 : handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
134 250874 : if (root->IsNull(isolate)) {
135 : unsigned int magic = 0xbbbbbbbb;
136 0 : isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
137 : }
138 : return Handle<JSReceiver>::cast(root);
139 : }
140 :
141 :
142 0 : Handle<Map> LookupIterator::GetReceiverMap() const {
143 0 : if (receiver_->IsNumber()) return factory()->heap_number_map();
144 0 : return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
145 : }
146 :
147 8675572 : bool LookupIterator::HasAccess() const {
148 : DCHECK_EQ(ACCESS_CHECK, state_);
149 : return isolate_->MayAccess(handle(isolate_->context()),
150 17351144 : GetHolder<JSObject>());
151 : }
152 :
153 : template <bool is_element>
154 7255357 : void LookupIterator::ReloadPropertyInformation() {
155 7255357 : state_ = BEFORE_PROPERTY;
156 7255357 : interceptor_state_ = InterceptorState::kUninitialized;
157 7255357 : state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
158 : DCHECK(IsFound() || !holder_->HasFastProperties());
159 7255359 : }
160 :
161 4933310 : void LookupIterator::InternalUpdateProtector() {
162 4933310 : if (isolate_->bootstrapper()->IsActive()) return;
163 :
164 246207 : if (*name_ == heap()->constructor_string()) {
165 226176 : if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
166 : // Setting the constructor property could change an instance's @@species
167 210463 : if (holder_->IsJSArray()) {
168 : isolate_->CountUsage(
169 50 : v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
170 50 : isolate_->InvalidateArraySpeciesProtector();
171 210413 : } else if (holder_->map()->is_prototype_map()) {
172 : DisallowHeapAllocation no_gc;
173 : // Setting the constructor of Array.prototype of any realm also needs
174 : // to invalidate the species protector
175 63747 : if (isolate_->IsInAnyContext(*holder_,
176 63747 : Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
177 : isolate_->CountUsage(v8::Isolate::UseCounterFeature::
178 0 : kArrayPrototypeConstructorModified);
179 0 : isolate_->InvalidateArraySpeciesProtector();
180 : }
181 : }
182 20031 : } else if (*name_ == heap()->species_symbol()) {
183 4318 : if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
184 : // Setting the Symbol.species property of any Array constructor invalidates
185 : // the species protector
186 4234 : if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
187 : isolate_->CountUsage(
188 25 : v8::Isolate::UseCounterFeature::kArraySpeciesModified);
189 25 : isolate_->InvalidateArraySpeciesProtector();
190 : }
191 15713 : } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
192 1280 : if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
193 69 : isolate_->InvalidateIsConcatSpreadableProtector();
194 14433 : } else if (*name_ == heap()->iterator_symbol()) {
195 14433 : if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
196 12417 : if (holder_->IsJSArray()) {
197 61 : isolate_->InvalidateArrayIteratorProtector();
198 : }
199 : }
200 : }
201 :
202 31585664 : void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
203 : DCHECK(state_ == DATA || state_ == ACCESSOR);
204 : DCHECK(HolderIsReceiverOrHiddenPrototype());
205 :
206 : Handle<JSObject> holder = GetHolder<JSObject>();
207 :
208 15910094 : if (IsElement()) {
209 : ElementsKind kind = holder->GetElementsKind();
210 1343849 : ElementsKind to = value->OptimalElementsKind();
211 1343849 : if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
212 : to = GetMoreGeneralElementsKind(kind, to);
213 :
214 1343849 : if (kind != to) {
215 27640 : JSObject::TransitionElementsKind(holder, to);
216 : }
217 :
218 : // Copy the backing store if it is copy-on-write.
219 1343849 : if (IsFastSmiOrObjectElementsKind(to)) {
220 132666 : JSObject::EnsureWritableFastElements(holder);
221 : }
222 15462536 : return;
223 : }
224 :
225 14566245 : if (holder->IsJSGlobalObject()) {
226 : Handle<GlobalDictionary> dictionary(holder->global_dictionary());
227 : Handle<PropertyCell> cell(
228 3063099 : PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
229 : DCHECK(!cell->IsTheHole(isolate_));
230 3063099 : property_details_ = cell->property_details();
231 : PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
232 3063099 : property_details_);
233 : return;
234 : }
235 11503146 : if (!holder->HasFastProperties()) return;
236 :
237 : PropertyConstness new_constness = kConst;
238 : if (FLAG_track_constant_fields) {
239 : if (constness() == kConst) {
240 : DCHECK_EQ(kData, property_details_.kind());
241 : // Check that current value matches new value otherwise we should make
242 : // the property mutable.
243 : if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
244 : }
245 : } else {
246 : new_constness = kMutable;
247 : }
248 :
249 9145109 : Handle<Map> old_map(holder->map(), isolate_);
250 : Handle<Map> new_map = Map::PrepareForDataProperty(
251 9145109 : old_map, descriptor_number(), new_constness, value);
252 :
253 9145110 : if (old_map.is_identical_to(new_map)) {
254 : // Update the property details if the representation was None.
255 17393176 : if (constness() != new_constness || representation().IsNone()) {
256 : property_details_ =
257 404263 : new_map->instance_descriptors()->GetDetails(descriptor_number());
258 : }
259 : return;
260 : }
261 :
262 447559 : JSObject::MigrateToMap(holder, new_map);
263 447558 : ReloadPropertyInformation<false>();
264 : }
265 :
266 :
267 1550241 : void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
268 6136086 : PropertyAttributes attributes) {
269 : DCHECK(state_ == DATA || state_ == ACCESSOR);
270 : DCHECK(HolderIsReceiverOrHiddenPrototype());
271 : Handle<JSObject> holder = GetHolder<JSObject>();
272 1550241 : if (IsElement()) {
273 : DCHECK(!holder->HasFixedTypedArrayElements());
274 : DCHECK(attributes != NONE || !holder->HasFastElements());
275 : Handle<FixedArrayBase> elements(holder->elements());
276 32183 : holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
277 32183 : attributes);
278 32183 : ReloadPropertyInformation<true>();
279 1518058 : } else if (holder->HasFastProperties()) {
280 1515893 : Handle<Map> old_map(holder->map(), isolate_);
281 : Handle<Map> new_map = Map::ReconfigureExistingProperty(
282 1515892 : old_map, descriptor_number(), i::kData, attributes);
283 : // Force mutable to avoid changing constant value by reconfiguring
284 : // kData -> kAccessor -> kData.
285 : new_map = Map::PrepareForDataProperty(new_map, descriptor_number(),
286 1515894 : kMutable, value);
287 1515895 : JSObject::MigrateToMap(holder, new_map);
288 1515894 : ReloadPropertyInformation<false>();
289 : }
290 :
291 3068306 : if (!IsElement() && !holder->HasFastProperties()) {
292 : PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable);
293 2168 : if (holder->IsJSGlobalObject()) {
294 : Handle<GlobalDictionary> dictionary(holder->global_dictionary());
295 :
296 : Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
297 521 : dictionary, dictionary_entry(), value, details);
298 521 : cell->set_value(*value);
299 521 : property_details_ = cell->property_details();
300 : } else {
301 : Handle<NameDictionary> dictionary(holder->property_dictionary());
302 : PropertyDetails original_details =
303 : dictionary->DetailsAt(dictionary_entry());
304 : int enumeration_index = original_details.dictionary_index();
305 : DCHECK(enumeration_index > 0);
306 : details = details.set_index(enumeration_index);
307 : dictionary->SetEntry(dictionary_entry(), name(), value, details);
308 1647 : property_details_ = details;
309 : }
310 2168 : state_ = DATA;
311 : }
312 :
313 1550243 : WriteDataValue(value, true);
314 :
315 : #if VERIFY_HEAP
316 : if (FLAG_verify_heap) {
317 : holder->JSObjectVerify();
318 : }
319 : #endif
320 1550243 : }
321 :
322 : // Can only be called when the receiver is a JSObject. JSProxy has to be handled
323 : // via a trap. Adding properties to primitive values is not observable.
324 53614893 : void LookupIterator::PrepareTransitionToDataProperty(
325 : Handle<JSObject> receiver, Handle<Object> value,
326 52173026 : PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
327 : DCHECK(receiver.is_identical_to(GetStoreTarget()));
328 53614893 : if (state_ == TRANSITION) return;
329 :
330 104346060 : if (!IsElement() && name()->IsPrivate()) {
331 2808436 : attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
332 : }
333 :
334 : DCHECK(state_ != LookupIterator::ACCESSOR ||
335 : (GetAccessors()->IsAccessorInfo() &&
336 : AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
337 : DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
338 : DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
339 :
340 52173025 : Handle<Map> map(receiver->map(), isolate_);
341 :
342 : // Dictionary maps can always have additional data properties.
343 52173043 : if (map->is_dictionary_map()) {
344 21971442 : state_ = TRANSITION;
345 21971442 : if (map->IsJSGlobalObjectMap()) {
346 : // Install a property cell.
347 : Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
348 : int entry;
349 : Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
350 8833396 : global, name(), PropertyCellType::kUninitialized, &entry);
351 : Handle<GlobalDictionary> dictionary(global->global_dictionary(),
352 8833396 : isolate_);
353 : DCHECK(cell->value()->IsTheHole(isolate_));
354 : DCHECK(!value->IsTheHole(isolate_));
355 8833396 : transition_ = cell;
356 : // Assign an enumeration index to the property and update
357 : // SetNextEnumerationIndex.
358 : int index = dictionary->NextEnumerationIndex();
359 8833396 : dictionary->SetNextEnumerationIndex(index + 1);
360 : property_details_ = PropertyDetails(kData, attributes, index,
361 8833396 : PropertyCellType::kUninitialized);
362 : PropertyCellType new_type =
363 8833396 : PropertyCell::UpdatedType(cell, value, property_details_);
364 8833395 : property_details_ = property_details_.set_cell_type(new_type);
365 : cell->set_property_details(property_details_);
366 8833396 : number_ = entry;
367 8833396 : has_property_ = true;
368 : } else {
369 : // Don't set enumeration index (it will be set during value store).
370 : property_details_ =
371 13138046 : PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
372 13138046 : transition_ = map;
373 : }
374 : return;
375 : }
376 :
377 : Handle<Map> transition = Map::TransitionToDataProperty(
378 30201601 : map, name_, value, attributes, kDefaultFieldConstness, store_mode);
379 30201603 : state_ = TRANSITION;
380 30201603 : transition_ = transition;
381 :
382 30201603 : if (transition->is_dictionary_map()) {
383 : // Don't set enumeration index (it will be set during value store).
384 : property_details_ =
385 101209 : PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
386 : } else {
387 30100395 : property_details_ = transition->GetLastDescriptorDetails();
388 30100395 : has_property_ = true;
389 : }
390 : }
391 :
392 52172281 : void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
393 : DCHECK_EQ(TRANSITION, state_);
394 :
395 : DCHECK(receiver.is_identical_to(GetStoreTarget()));
396 52172281 : holder_ = receiver;
397 52172271 : if (receiver->IsJSGlobalObject()) {
398 8832630 : state_ = DATA;
399 52172274 : return;
400 : }
401 : Handle<Map> transition = transition_map();
402 43339641 : bool simple_transition = transition->GetBackPointer() == receiver->map();
403 43339639 : JSObject::MigrateToMap(receiver, transition);
404 :
405 43339643 : if (simple_transition) {
406 : int number = transition->LastAdded();
407 25880593 : number_ = static_cast<uint32_t>(number);
408 25880593 : property_details_ = transition->GetLastDescriptorDetails();
409 25880593 : state_ = DATA;
410 17459050 : } else if (receiver->map()->is_dictionary_map()) {
411 : Handle<NameDictionary> dictionary(receiver->property_dictionary(),
412 13239255 : isolate_);
413 : int entry;
414 : dictionary = NameDictionary::Add(dictionary, name(),
415 : isolate_->factory()->uninitialized_value(),
416 26478510 : property_details_, &entry);
417 13239255 : receiver->set_properties(*dictionary);
418 : // Reload details containing proper enumeration index value.
419 26478510 : property_details_ = dictionary->DetailsAt(entry);
420 13239255 : number_ = entry;
421 13239255 : has_property_ = true;
422 13239255 : state_ = DATA;
423 :
424 : } else {
425 4219795 : ReloadPropertyInformation<false>();
426 : }
427 : }
428 :
429 :
430 6341519 : void LookupIterator::Delete() {
431 : Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
432 6341519 : if (IsElement()) {
433 : Handle<JSObject> object = Handle<JSObject>::cast(holder);
434 164144 : ElementsAccessor* accessor = object->GetElementsAccessor();
435 164144 : accessor->Delete(object, number_);
436 : } else {
437 : bool is_prototype_map = holder->map()->is_prototype_map();
438 : RuntimeCallTimerScope stats_scope(
439 : isolate_, is_prototype_map
440 : ? &RuntimeCallStats::PrototypeObject_DeleteProperty
441 6177375 : : &RuntimeCallStats::Object_DeleteProperty);
442 :
443 : PropertyNormalizationMode mode =
444 6177375 : is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
445 :
446 6177375 : if (holder->HasFastProperties()) {
447 : JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
448 188691 : "DeletingProperty");
449 188691 : ReloadPropertyInformation<false>();
450 : }
451 : // TODO(verwaest): Get rid of the name_ argument.
452 6177375 : JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
453 6177375 : if (holder->IsJSObject()) {
454 6177368 : JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
455 : }
456 : }
457 6341519 : state_ = NOT_FOUND;
458 6341519 : }
459 :
460 508193 : void LookupIterator::TransitionToAccessorProperty(
461 : Handle<Object> getter, Handle<Object> setter,
462 1932076 : PropertyAttributes attributes) {
463 : DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
464 : // Can only be called when the receiver is a JSObject. JSProxy has to be
465 : // handled via a trap. Adding properties to primitive values is not
466 : // observable.
467 508193 : Handle<JSObject> receiver = GetStoreTarget();
468 977074 : if (!IsElement() && name()->IsPrivate()) {
469 14 : attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
470 : }
471 :
472 977074 : if (!IsElement() && !receiver->map()->is_dictionary_map()) {
473 434909 : Handle<Map> old_map(receiver->map(), isolate_);
474 :
475 434909 : if (!holder_.is_identical_to(receiver)) {
476 0 : holder_ = receiver;
477 0 : state_ = NOT_FOUND;
478 434909 : } else if (state_ == INTERCEPTOR) {
479 42 : LookupInRegularHolder<false>(*old_map, *holder_);
480 : }
481 : int descriptor =
482 434909 : IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
483 :
484 : Handle<Map> new_map = Map::TransitionToAccessorProperty(
485 434909 : isolate_, old_map, name_, descriptor, getter, setter, attributes);
486 434910 : bool simple_transition = new_map->GetBackPointer() == receiver->map();
487 434910 : JSObject::MigrateToMap(receiver, new_map);
488 :
489 434910 : if (simple_transition) {
490 : int number = new_map->LastAdded();
491 30015 : number_ = static_cast<uint32_t>(number);
492 30015 : property_details_ = new_map->GetLastDescriptorDetails();
493 30015 : state_ = ACCESSOR;
494 30015 : return;
495 : }
496 :
497 404895 : ReloadPropertyInformation<false>();
498 404895 : if (!new_map->is_dictionary_map()) return;
499 : }
500 :
501 : Handle<AccessorPair> pair;
502 266533 : if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
503 : pair = Handle<AccessorPair>::cast(GetAccessors());
504 : // If the component and attributes are identical, nothing has to be done.
505 17291 : if (pair->Equals(*getter, *setter)) {
506 44 : if (property_details().attributes() == attributes) {
507 0 : if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
508 : return;
509 : }
510 : } else {
511 17247 : pair = AccessorPair::Copy(pair);
512 17247 : pair->SetComponents(*getter, *setter);
513 : }
514 : } else {
515 231744 : pair = factory()->NewAccessorPair();
516 231744 : pair->SetComponents(*getter, *setter);
517 : }
518 :
519 249035 : TransitionToAccessorPair(pair, attributes);
520 :
521 : #if VERIFY_HEAP
522 : if (FLAG_verify_heap) {
523 : receiver->JSObjectVerify();
524 : }
525 : #endif
526 : }
527 :
528 :
529 446342 : void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
530 446400 : PropertyAttributes attributes) {
531 446342 : Handle<JSObject> receiver = GetStoreTarget();
532 446342 : holder_ = receiver;
533 :
534 : PropertyDetails details(kAccessor, attributes, 0, PropertyCellType::kMutable);
535 :
536 446342 : if (IsElement()) {
537 : // TODO(verwaest): Move code into the element accessor.
538 : Handle<SeededNumberDictionary> dictionary =
539 39328 : JSObject::NormalizeElements(receiver);
540 :
541 : dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details,
542 39328 : receiver);
543 39328 : receiver->RequireSlowElements(*dictionary);
544 :
545 39328 : if (receiver->HasSlowArgumentsElements()) {
546 : FixedArray* parameter_map = FixedArray::cast(receiver->elements());
547 102 : uint32_t length = parameter_map->length() - 2;
548 102 : if (number_ < length) {
549 58 : parameter_map->set(number_ + 2, heap()->the_hole_value());
550 : }
551 102 : FixedArray::cast(receiver->elements())->set(1, *dictionary);
552 : } else {
553 39226 : receiver->set_elements(*dictionary);
554 : }
555 :
556 39328 : ReloadPropertyInformation<true>();
557 : } else {
558 : PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
559 : ? KEEP_INOBJECT_PROPERTIES
560 407014 : : CLEAR_INOBJECT_PROPERTIES;
561 : // Normalize object to make this operation simple.
562 : JSObject::NormalizeProperties(receiver, mode, 0,
563 407014 : "TransitionToAccessorPair");
564 :
565 407014 : JSObject::SetNormalizedProperty(receiver, name_, pair, details);
566 407014 : JSObject::ReoptimizeIfPrototype(receiver);
567 :
568 407014 : ReloadPropertyInformation<false>();
569 : }
570 446342 : }
571 :
572 :
573 15257120 : bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
574 : DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
575 : // Optimization that only works if configuration_ is not mutable.
576 15247841 : if (!check_prototype_chain()) return true;
577 : DisallowHeapAllocation no_gc;
578 15224632 : if (*receiver_ == *holder_) return true;
579 440880 : if (!receiver_->IsJSReceiver()) return false;
580 : JSReceiver* current = JSReceiver::cast(*receiver_);
581 : JSReceiver* object = *holder_;
582 437717 : if (!current->map()->has_hidden_prototype()) return false;
583 : // JSProxy do not occur as hidden prototypes.
584 9279 : if (object->IsJSProxy()) return false;
585 : PrototypeIterator iter(isolate(), current, kStartAtPrototype,
586 : PrototypeIterator::END_AT_NON_HIDDEN);
587 9889 : while (!iter.IsAtEnd()) {
588 9279 : if (iter.GetCurrent<JSReceiver>() == object) return true;
589 610 : iter.Advance();
590 : }
591 : return false;
592 : }
593 :
594 :
595 191053823 : Handle<Object> LookupIterator::FetchValue() const {
596 : Object* result = NULL;
597 191053823 : if (IsElement()) {
598 : Handle<JSObject> holder = GetHolder<JSObject>();
599 74015388 : ElementsAccessor* accessor = holder->GetElementsAccessor();
600 74015388 : return accessor->Get(holder, number_);
601 117038450 : } else if (holder_->IsJSGlobalObject()) {
602 : Handle<JSObject> holder = GetHolder<JSObject>();
603 14371256 : result = holder->global_dictionary()->ValueAt(number_);
604 : DCHECK(result->IsPropertyCell());
605 : result = PropertyCell::cast(result)->value();
606 109852822 : } else if (!holder_->HasFastProperties()) {
607 8067712 : result = holder_->property_dictionary()->ValueAt(number_);
608 105818969 : } else if (property_details_.location() == kField) {
609 : DCHECK_EQ(kData, property_details_.kind());
610 : Handle<JSObject> holder = GetHolder<JSObject>();
611 112391478 : FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
612 : return JSObject::FastPropertyAt(holder, property_details_.representation(),
613 56195739 : field_index);
614 : } else {
615 49623230 : result = holder_->map()->instance_descriptors()->GetValue(number_);
616 : }
617 60842714 : return handle(result, isolate_);
618 : }
619 :
620 0 : bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
621 : DCHECK(!IsElement());
622 : DCHECK(holder_->HasFastProperties());
623 : DCHECK_EQ(kField, property_details_.location());
624 : DCHECK_EQ(kConst, property_details_.constness());
625 : Handle<JSObject> holder = GetHolder<JSObject>();
626 0 : FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
627 0 : if (property_details_.representation().IsDouble()) {
628 0 : if (!value->IsNumber()) return false;
629 : uint64_t bits;
630 0 : if (holder->IsUnboxedDoubleField(field_index)) {
631 : bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
632 : } else {
633 0 : Object* current_value = holder->RawFastPropertyAt(field_index);
634 : DCHECK(current_value->IsMutableHeapNumber());
635 : bits = HeapNumber::cast(current_value)->value_as_bits();
636 : }
637 : // Use bit representation of double to to check for hole double, since
638 : // manipulating the signaling NaN used for the hole in C++, e.g. with
639 : // bit_cast or value(), will change its value on ia32 (the x87 stack is
640 : // used to return values and stores to the stack silently clear the
641 : // signalling bit).
642 0 : if (bits == kHoleNanInt64) {
643 : // Uninitialized double field.
644 : return true;
645 : }
646 0 : return bit_cast<double>(bits) == value->Number();
647 : } else {
648 0 : Object* current_value = holder->RawFastPropertyAt(field_index);
649 0 : return current_value->IsUninitialized(isolate()) || current_value == value;
650 : }
651 : }
652 :
653 329326 : int LookupIterator::GetFieldDescriptorIndex() const {
654 : DCHECK(has_property_);
655 : DCHECK(holder_->HasFastProperties());
656 : DCHECK_EQ(kField, property_details_.location());
657 : DCHECK_EQ(kData, property_details_.kind());
658 329326 : return descriptor_number();
659 : }
660 :
661 132444 : int LookupIterator::GetAccessorIndex() const {
662 : DCHECK(has_property_);
663 : DCHECK(holder_->HasFastProperties());
664 : DCHECK_EQ(kDescriptor, property_details_.location());
665 : DCHECK_EQ(kAccessor, property_details_.kind());
666 132444 : return descriptor_number();
667 : }
668 :
669 :
670 1380082 : int LookupIterator::GetConstantIndex() const {
671 : DCHECK(has_property_);
672 : DCHECK(holder_->HasFastProperties());
673 : DCHECK_EQ(kDescriptor, property_details_.location());
674 : DCHECK_EQ(kData, property_details_.kind());
675 : DCHECK(!FLAG_track_constant_fields);
676 : DCHECK(!IsElement());
677 1380082 : return descriptor_number();
678 : }
679 :
680 0 : Handle<Map> LookupIterator::GetFieldOwnerMap() const {
681 : DCHECK(has_property_);
682 : DCHECK(holder_->HasFastProperties());
683 : DCHECK_EQ(kField, property_details_.location());
684 : DCHECK(!IsElement());
685 : Map* holder_map = holder_->map();
686 0 : return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
687 : }
688 :
689 4362538 : FieldIndex LookupIterator::GetFieldIndex() const {
690 : DCHECK(has_property_);
691 : DCHECK(holder_->HasFastProperties());
692 : DCHECK_EQ(kField, property_details_.location());
693 : DCHECK(!IsElement());
694 : Map* holder_map = holder_->map();
695 : int index =
696 : holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
697 : bool is_double = representation().IsDouble();
698 2181270 : return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
699 : }
700 :
701 0 : Handle<FieldType> LookupIterator::GetFieldType() const {
702 : DCHECK(has_property_);
703 : DCHECK(holder_->HasFastProperties());
704 : DCHECK_EQ(kField, property_details_.location());
705 : return handle(
706 : holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
707 0 : isolate_);
708 : }
709 :
710 :
711 4721899 : Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
712 : DCHECK(!IsElement());
713 : Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
714 4721899 : Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
715 : DCHECK(value->IsPropertyCell());
716 9443802 : return handle(PropertyCell::cast(value), isolate_);
717 : }
718 :
719 :
720 14546571 : Handle<Object> LookupIterator::GetAccessors() const {
721 : DCHECK_EQ(ACCESSOR, state_);
722 33332858 : return FetchValue();
723 : }
724 :
725 :
726 157720956 : Handle<Object> LookupIterator::GetDataValue() const {
727 : DCHECK_EQ(DATA, state_);
728 157720956 : Handle<Object> value = FetchValue();
729 157720935 : return value;
730 : }
731 :
732 69223123 : void LookupIterator::WriteDataValue(Handle<Object> value,
733 130340974 : bool initializing_store) {
734 : DCHECK_EQ(DATA, state_);
735 : Handle<JSReceiver> holder = GetHolder<JSReceiver>();
736 69223123 : if (IsElement()) {
737 : Handle<JSObject> object = Handle<JSObject>::cast(holder);
738 1376032 : ElementsAccessor* accessor = object->GetElementsAccessor();
739 2752064 : accessor->Set(object, number_, *value);
740 67847091 : } else if (holder->HasFastProperties()) {
741 40432007 : if (property_details_.location() == kField) {
742 : // Check that in case of kConst field the existing value is equal to
743 : // |value|.
744 : DCHECK_IMPLIES(
745 : !initializing_store && property_details_.constness() == kConst,
746 : IsConstFieldValueEqualTo(*value));
747 : JSObject::cast(*holder)->WriteToField(descriptor_number(),
748 33702755 : property_details_, *value);
749 : } else {
750 : DCHECK_EQ(kDescriptor, property_details_.location());
751 : DCHECK_EQ(kConst, property_details_.constness());
752 : }
753 27415096 : } else if (holder->IsJSGlobalObject()) {
754 : GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
755 11822139 : Object* cell = dictionary->ValueAt(dictionary_entry());
756 : DCHECK(cell->IsPropertyCell());
757 11822140 : PropertyCell::cast(cell)->set_value(*value);
758 : } else {
759 : NameDictionary* dictionary = holder->property_dictionary();
760 : dictionary->ValueAtPut(dictionary_entry(), *value);
761 : }
762 69223141 : }
763 :
764 : template <bool is_element>
765 1066926 : bool LookupIterator::SkipInterceptor(JSObject* holder) {
766 : auto info = GetInterceptor<is_element>(holder);
767 706707 : if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) {
768 : return true;
769 : }
770 1066912 : if (info->non_masking()) {
771 469 : switch (interceptor_state_) {
772 : case InterceptorState::kUninitialized:
773 294 : interceptor_state_ = InterceptorState::kSkipNonMasking;
774 : // Fall through.
775 : case InterceptorState::kSkipNonMasking:
776 : return true;
777 : case InterceptorState::kProcessNonMasking:
778 : return false;
779 : }
780 : }
781 1066443 : return interceptor_state_ == InterceptorState::kProcessNonMasking;
782 : }
783 :
784 945051101 : JSReceiver* LookupIterator::NextHolder(Map* map) {
785 : DisallowHeapAllocation no_gc;
786 553588200 : if (map->prototype() == heap()->null_value()) return NULL;
787 478152972 : if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
788 : return JSReceiver::cast(map->prototype());
789 : }
790 :
791 196802215 : LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
792 : DCHECK(!IsElement());
793 196992961 : if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
794 :
795 : Handle<String> name_string = Handle<String>::cast(name_);
796 162316 : if (name_string->length() == 0) return NOT_FOUND;
797 :
798 162316 : return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
799 : ? INTEGER_INDEXED_EXOTIC
800 162316 : : NOT_FOUND;
801 : }
802 :
803 : namespace {
804 :
805 : template <bool is_element>
806 : bool HasInterceptor(Map* map) {
807 : return is_element ? map->has_indexed_interceptor()
808 : : map->has_named_interceptor();
809 : }
810 :
811 : } // namespace
812 :
813 : template <bool is_element>
814 57880103 : LookupIterator::State LookupIterator::LookupInSpecialHolder(
815 : Map* const map, JSReceiver* const holder) {
816 : STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
817 57880103 : switch (state_) {
818 : case NOT_FOUND:
819 48128424 : if (map->IsJSProxyMap()) {
820 352762 : if (is_element || !name_->IsPrivate()) return JSPROXY;
821 : }
822 47760916 : if (map->is_access_check_needed()) {
823 10261312 : if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
824 : }
825 : // Fall through.
826 : case ACCESS_CHECK:
827 77029626 : if (check_interceptor() && HasInterceptor<is_element>(map) &&
828 1066926 : !SkipInterceptor<is_element>(JSObject::cast(holder))) {
829 706331 : if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
830 : }
831 : // Fall through.
832 : case INTERCEPTOR:
833 47388796 : if (!is_element && map->IsJSGlobalObjectMap()) {
834 : GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
835 36139075 : int number = dict->FindEntry(name_);
836 36139070 : if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
837 16634600 : number_ = static_cast<uint32_t>(number);
838 : DCHECK(dict->ValueAt(number_)->IsPropertyCell());
839 16634600 : PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
840 33269200 : if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
841 16590630 : property_details_ = cell->property_details();
842 16590630 : has_property_ = true;
843 16590630 : switch (property_details_.kind()) {
844 : case v8::internal::kData:
845 : return DATA;
846 : case v8::internal::kAccessor:
847 58545 : return ACCESSOR;
848 : }
849 : }
850 11626006 : return LookupInRegularHolder<is_element>(map, holder);
851 : case ACCESSOR:
852 : case DATA:
853 : return NOT_FOUND;
854 : case INTEGER_INDEXED_EXOTIC:
855 : case JSPROXY:
856 : case TRANSITION:
857 0 : UNREACHABLE();
858 : }
859 0 : UNREACHABLE();
860 : return NOT_FOUND;
861 : }
862 :
863 : template <bool is_element>
864 770328000 : LookupIterator::State LookupIterator::LookupInRegularHolder(
865 : Map* const map, JSReceiver* const holder) {
866 : DisallowHeapAllocation no_gc;
867 770328000 : if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
868 : return NOT_FOUND;
869 : }
870 :
871 : if (is_element) {
872 : JSObject* js_object = JSObject::cast(holder);
873 414308518 : ElementsAccessor* accessor = js_object->GetElementsAccessor();
874 : FixedArrayBase* backing_store = js_object->elements();
875 414308517 : number_ =
876 414308518 : accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
877 414308517 : if (number_ == kMaxUInt32) {
878 337822400 : return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
879 : }
880 76486117 : property_details_ = accessor->GetDetails(js_object, number_);
881 356019370 : } else if (!map->is_dictionary_map()) {
882 : DescriptorArray* descriptors = map->instance_descriptors();
883 327106104 : int number = descriptors->SearchWithCache(isolate_, *name_, map);
884 327106225 : if (number == DescriptorArray::kNotFound) return NotFound(holder);
885 146053693 : number_ = static_cast<uint32_t>(number);
886 146053693 : property_details_ = descriptors->GetDetails(number_);
887 : } else {
888 : NameDictionary* dict = holder->property_dictionary();
889 28913266 : int number = dict->FindEntry(name_);
890 28913266 : if (number == NameDictionary::kNotFound) return NotFound(holder);
891 13163578 : number_ = static_cast<uint32_t>(number);
892 13163578 : property_details_ = dict->DetailsAt(number_);
893 : }
894 235703392 : has_property_ = true;
895 235703392 : switch (property_details_.kind()) {
896 : case v8::internal::kData:
897 : return DATA;
898 : case v8::internal::kAccessor:
899 15264990 : return ACCESSOR;
900 : }
901 :
902 0 : UNREACHABLE();
903 : return state_;
904 : }
905 :
906 1660 : Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
907 1325 : const {
908 : DCHECK_EQ(ACCESS_CHECK, state_);
909 : DisallowHeapAllocation no_gc;
910 : AccessCheckInfo* access_check_info =
911 1660 : AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
912 1660 : if (access_check_info) {
913 : Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
914 1325 : : access_check_info->named_interceptor();
915 1325 : if (interceptor) {
916 203 : return handle(InterceptorInfo::cast(interceptor), isolate_);
917 : }
918 : }
919 1457 : return Handle<InterceptorInfo>();
920 : }
921 :
922 12171028 : bool LookupIterator::TryLookupCachedProperty() {
923 9376179 : return state() == LookupIterator::ACCESSOR &&
924 21546347 : GetAccessors()->IsAccessorPair() && LookupCachedProperty();
925 : }
926 :
927 28126067 : bool LookupIterator::LookupCachedProperty() {
928 : DCHECK_EQ(state(), LookupIterator::ACCESSOR);
929 : DCHECK(GetAccessors()->IsAccessorPair());
930 :
931 : AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
932 : Handle<Object> getter(accessor_pair->getter(), isolate());
933 : MaybeHandle<Name> maybe_name =
934 9375319 : FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
935 9375319 : if (maybe_name.is_null()) return false;
936 :
937 : // We have found a cached property! Modify the iterator accordingly.
938 110 : name_ = maybe_name.ToHandleChecked();
939 110 : Restart();
940 110 : CHECK_EQ(state(), LookupIterator::DATA);
941 : return true;
942 : }
943 :
944 : } // namespace internal
945 : } // namespace v8
|