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.
7 #include "src/bootstrapper.h"
8 #include "src/deoptimizer.h"
9 #include "src/lookup.h"
10 #include "src/lookup-inl.h"
16 void LookupIterator::Next() {
17 DCHECK_NE(JSPROXY, state_);
18 DCHECK_NE(TRANSITION, state_);
19 DisallowHeapAllocation no_gc;
20 has_property_ = false;
22 JSReceiver* holder = *holder_;
23 Map* map = *holder_map_;
25 // Perform lookup on current holder.
26 state_ = LookupInHolder(map, holder);
27 if (IsFound()) return;
29 // Continue lookup if lookup on current holder failed.
31 JSReceiver* maybe_holder = NextHolder(map);
32 if (maybe_holder == nullptr) {
33 if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
34 RestartLookupForNonMaskingInterceptors();
39 holder = maybe_holder;
41 state_ = LookupInHolder(map, holder);
44 if (holder != *holder_) {
45 holder_ = handle(holder, isolate_);
46 holder_map_ = handle(map, isolate_);
51 void LookupIterator::RestartLookupForNonMaskingInterceptors() {
52 interceptor_state_ = InterceptorState::kProcessNonMasking;
54 property_details_ = PropertyDetails::Empty();
55 number_ = DescriptorArray::kNotFound;
56 holder_ = initial_holder_;
57 holder_map_ = handle(holder_->map(), isolate_);
63 Handle<JSReceiver> LookupIterator::GetRoot(Isolate* isolate,
64 Handle<Object> receiver,
66 if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
67 // Strings are the only objects with properties (only elements) directly on
68 // the wrapper. Hence we can skip generating the wrapper for all other cases.
69 if (index != kMaxUInt32 && receiver->IsString() &&
70 index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
71 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
72 // context, ensuring that we don't leak it into JS?
73 Handle<JSFunction> constructor = isolate->string_function();
74 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
75 Handle<JSValue>::cast(result)->set_value(*receiver);
78 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
80 unsigned int magic = 0xbbbbbbbb;
81 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
83 return Handle<JSReceiver>::cast(root);
87 Handle<Map> LookupIterator::GetReceiverMap() const {
88 if (receiver_->IsNumber()) return factory()->heap_number_map();
89 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
93 Handle<JSObject> LookupIterator::GetStoreTarget() const {
94 if (receiver_->IsJSGlobalProxy()) {
95 PrototypeIterator iter(isolate(), receiver_);
96 if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
97 return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
99 return Handle<JSObject>::cast(receiver_);
103 bool LookupIterator::HasAccess() const {
104 DCHECK_EQ(ACCESS_CHECK, state_);
105 return isolate_->MayAccess(GetHolder<JSObject>());
109 void LookupIterator::ReloadPropertyInformation() {
110 state_ = BEFORE_PROPERTY;
111 interceptor_state_ = InterceptorState::kUninitialized;
112 state_ = LookupInHolder(*holder_map_, *holder_);
113 DCHECK(IsFound() || holder_map_->is_dictionary_map());
117 void LookupIterator::ReloadHolderMap() {
118 DCHECK_EQ(DATA, state_);
120 DCHECK(JSObject::cast(*holder_)->HasExternalArrayElements() ||
121 JSObject::cast(*holder_)->HasFixedTypedArrayElements());
122 if (*holder_map_ != holder_->map()) {
123 holder_map_ = handle(holder_->map(), isolate_);
128 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
129 DCHECK(state_ == DATA || state_ == ACCESSOR);
130 DCHECK(HolderIsReceiverOrHiddenPrototype());
132 Handle<JSObject> holder = GetHolder<JSObject>();
135 ElementsKind kind = holder_map_->elements_kind();
136 ElementsKind to = value->OptimalElementsKind();
137 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
138 to = IsMoreGeneralElementsKindTransition(kind, to) ? to : kind;
139 JSObject::TransitionElementsKind(holder, to);
140 holder_map_ = handle(holder->map(), isolate_);
142 // Copy the backing store if it is copy-on-write.
143 if (IsFastSmiOrObjectElementsKind(to)) {
144 JSObject::EnsureWritableFastElements(holder);
148 if (holder_map_->is_dictionary_map()) return;
150 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
153 JSObject::MigrateToMap(holder, holder_map_);
154 ReloadPropertyInformation();
158 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
159 PropertyAttributes attributes) {
160 DCHECK(state_ == DATA || state_ == ACCESSOR);
161 DCHECK(HolderIsReceiverOrHiddenPrototype());
162 Handle<JSObject> holder = GetHolder<JSObject>();
164 DCHECK(!holder->HasExternalArrayElements());
165 DCHECK(!holder->HasFixedTypedArrayElements());
166 DCHECK(attributes != NONE || !holder->HasFastElements());
167 Handle<FixedArrayBase> elements(holder->elements());
168 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
170 } else if (holder_map_->is_dictionary_map()) {
171 PropertyDetails details(attributes, v8::internal::DATA, 0,
172 PropertyCellType::kMutable);
173 JSObject::SetNormalizedProperty(holder, name(), value, details);
175 holder_map_ = Map::ReconfigureExistingProperty(
176 holder_map_, descriptor_number(), i::kData, attributes);
178 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
179 JSObject::MigrateToMap(holder, holder_map_);
182 ReloadPropertyInformation();
186 void LookupIterator::PrepareTransitionToDataProperty(
187 Handle<Object> value, PropertyAttributes attributes,
188 Object::StoreFromKeyed store_mode) {
189 if (state_ == TRANSITION) return;
190 DCHECK(state_ != LookupIterator::ACCESSOR ||
191 (GetAccessors()->IsAccessorInfo() &&
192 AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
193 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
194 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
195 // Can only be called when the receiver is a JSObject. JSProxy has to be
196 // handled via a trap. Adding properties to primitive values is not
198 Handle<JSObject> receiver = GetStoreTarget();
200 if (!isolate()->IsInternallyUsedPropertyName(name()) &&
201 !receiver->map()->is_extensible()) {
205 auto transition = Map::TransitionToDataProperty(
206 handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
208 transition_ = transition;
210 if (receiver->IsGlobalObject()) {
211 // Install a property cell.
213 auto cell = GlobalObject::EnsurePropertyCell(
214 Handle<GlobalObject>::cast(receiver), name());
215 DCHECK(cell->value()->IsTheHole());
217 } else if (!transition->is_dictionary_map()) {
218 property_details_ = transition->GetLastDescriptorDetails();
219 has_property_ = true;
224 void LookupIterator::ApplyTransitionToDataProperty() {
225 DCHECK_EQ(TRANSITION, state_);
227 Handle<JSObject> receiver = GetStoreTarget();
228 if (receiver->IsGlobalObject()) return;
230 holder_map_ = transition_map();
231 JSObject::MigrateToMap(receiver, holder_map_);
232 ReloadPropertyInformation();
236 void LookupIterator::Delete() {
237 Handle<JSObject> holder = Handle<JSObject>::cast(holder_);
239 ElementsAccessor* accessor = holder->GetElementsAccessor();
240 accessor->Delete(holder, number_);
242 PropertyNormalizationMode mode = holder->map()->is_prototype_map()
243 ? KEEP_INOBJECT_PROPERTIES
244 : CLEAR_INOBJECT_PROPERTIES;
246 if (holder->HasFastProperties()) {
247 JSObject::NormalizeProperties(holder, mode, 0, "DeletingProperty");
248 holder_map_ = handle(holder->map(), isolate_);
249 ReloadPropertyInformation();
251 // TODO(verwaest): Get rid of the name_ argument.
252 JSObject::DeleteNormalizedProperty(holder, name_, number_);
253 JSObject::ReoptimizeIfPrototype(holder);
258 void LookupIterator::TransitionToAccessorProperty(
259 AccessorComponent component, Handle<Object> accessor,
260 PropertyAttributes attributes) {
261 DCHECK(!accessor->IsNull());
262 // Can only be called when the receiver is a JSObject. JSProxy has to be
263 // handled via a trap. Adding properties to primitive values is not
265 Handle<JSObject> receiver = GetStoreTarget();
267 if (!IsElement() && !receiver->map()->is_dictionary_map()) {
269 holder_map_ = Map::TransitionToAccessorProperty(
270 handle(receiver->map(), isolate_), name_, component, accessor,
272 JSObject::MigrateToMap(receiver, holder_map_);
274 ReloadPropertyInformation();
276 if (!holder_map_->is_dictionary_map()) return;
279 Handle<AccessorPair> pair;
280 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
281 pair = Handle<AccessorPair>::cast(GetAccessors());
282 // If the component and attributes are identical, nothing has to be done.
283 if (pair->get(component) == *accessor) {
284 if (property_details().attributes() == attributes) return;
286 pair = AccessorPair::Copy(pair);
287 pair->set(component, *accessor);
290 pair = factory()->NewAccessorPair();
291 pair->set(component, *accessor);
294 TransitionToAccessorPair(pair, attributes);
298 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
299 PropertyAttributes attributes) {
300 Handle<JSObject> receiver = GetStoreTarget();
303 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
304 PropertyCellType::kMutable);
307 // TODO(verwaest): Move code into the element accessor.
308 Handle<SeededNumberDictionary> dictionary =
309 JSObject::NormalizeElements(receiver);
311 dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details);
312 receiver->RequireSlowElements(*dictionary);
314 if (receiver->HasSlowArgumentsElements()) {
315 FixedArray* parameter_map = FixedArray::cast(receiver->elements());
316 uint32_t length = parameter_map->length() - 2;
317 if (number_ < length) {
318 parameter_map->set(number_ + 2, heap()->the_hole_value());
320 FixedArray::cast(receiver->elements())->set(1, *dictionary);
322 receiver->set_elements(*dictionary);
325 PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
326 ? KEEP_INOBJECT_PROPERTIES
327 : CLEAR_INOBJECT_PROPERTIES;
328 // Normalize object to make this operation simple.
329 JSObject::NormalizeProperties(receiver, mode, 0,
330 "TransitionToAccessorPair");
332 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
333 JSObject::ReoptimizeIfPrototype(receiver);
336 holder_map_ = handle(receiver->map(), isolate_);
337 ReloadPropertyInformation();
341 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
342 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
343 return InternalHolderIsReceiverOrHiddenPrototype();
346 bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
347 // Optimization that only works if configuration_ is not mutable.
348 if (!check_prototype_chain()) return true;
349 DisallowHeapAllocation no_gc;
350 if (!receiver_->IsJSReceiver()) return false;
351 Object* current = *receiver_;
352 JSReceiver* holder = *holder_;
353 // JSProxy do not occur as hidden prototypes.
354 if (current->IsJSProxy()) {
355 return JSReceiver::cast(current) == holder;
357 PrototypeIterator iter(isolate(), current,
358 PrototypeIterator::START_AT_RECEIVER);
360 if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
361 DCHECK(!current->IsJSProxy());
363 } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
368 Handle<Object> LookupIterator::FetchValue() const {
369 Object* result = NULL;
370 Handle<JSObject> holder = GetHolder<JSObject>();
372 // TODO(verwaest): Optimize.
373 if (holder->IsStringObjectWithCharacterAt(index_)) {
374 Handle<JSValue> js_value = Handle<JSValue>::cast(holder);
375 Handle<String> string(String::cast(js_value->value()));
376 return factory()->LookupSingleCharacterStringFromCode(
377 String::Flatten(string)->Get(index_));
380 ElementsAccessor* accessor = holder->GetElementsAccessor();
381 return accessor->Get(handle(holder->elements()), number_);
382 } else if (holder_map_->IsGlobalObjectMap()) {
383 result = holder->global_dictionary()->ValueAt(number_);
384 DCHECK(result->IsPropertyCell());
385 result = PropertyCell::cast(result)->value();
386 } else if (holder_map_->is_dictionary_map()) {
387 result = holder->property_dictionary()->ValueAt(number_);
388 } else if (property_details_.type() == v8::internal::DATA) {
389 FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
390 return JSObject::FastPropertyAt(holder, property_details_.representation(),
393 result = holder_map_->instance_descriptors()->GetValue(number_);
395 return handle(result, isolate_);
399 int LookupIterator::GetAccessorIndex() const {
400 DCHECK(has_property_);
401 DCHECK(!holder_map_->is_dictionary_map());
402 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
403 return descriptor_number();
407 int LookupIterator::GetConstantIndex() const {
408 DCHECK(has_property_);
409 DCHECK(!holder_map_->is_dictionary_map());
410 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
411 DCHECK(!IsElement());
412 return descriptor_number();
416 FieldIndex LookupIterator::GetFieldIndex() const {
417 DCHECK(has_property_);
418 DCHECK(!holder_map_->is_dictionary_map());
419 DCHECK_EQ(v8::internal::DATA, property_details_.type());
420 DCHECK(!IsElement());
422 holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
423 bool is_double = representation().IsDouble();
424 return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
428 Handle<HeapType> LookupIterator::GetFieldType() const {
429 DCHECK(has_property_);
430 DCHECK(!holder_map_->is_dictionary_map());
431 DCHECK_EQ(v8::internal::DATA, property_details_.type());
433 holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
438 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
439 DCHECK(!IsElement());
440 Handle<JSObject> holder = GetHolder<JSObject>();
441 Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
442 Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
443 DCHECK(value->IsPropertyCell());
444 return handle(PropertyCell::cast(value));
448 Handle<Object> LookupIterator::GetAccessors() const {
449 DCHECK_EQ(ACCESSOR, state_);
454 Handle<Object> LookupIterator::GetDataValue() const {
455 DCHECK_EQ(DATA, state_);
456 Handle<Object> value = FetchValue();
461 void LookupIterator::WriteDataValue(Handle<Object> value) {
462 DCHECK_EQ(DATA, state_);
463 Handle<JSObject> holder = GetHolder<JSObject>();
465 ElementsAccessor* accessor = holder->GetElementsAccessor();
466 accessor->Set(holder->elements(), number_, *value);
467 } else if (holder->IsGlobalObject()) {
468 Handle<GlobalDictionary> property_dictionary =
469 handle(holder->global_dictionary());
470 PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
472 } else if (holder_map_->is_dictionary_map()) {
473 NameDictionary* property_dictionary = holder->property_dictionary();
474 property_dictionary->ValueAtPut(dictionary_entry(), *value);
475 } else if (property_details_.type() == v8::internal::DATA) {
476 holder->WriteToField(descriptor_number(), *value);
478 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
483 bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
484 DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
485 // Currently typed arrays are the only such objects.
486 if (!holder->IsJSTypedArray()) return false;
487 if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
488 if (!InternalHolderIsReceiverOrHiddenPrototype()) {
489 exotic_index_state_ = ExoticIndexState::kNotExotic;
492 DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
494 // Compute and cache result.
496 result = index_ >= JSTypedArray::cast(holder)->length_value();
497 } else if (name()->IsString()) {
498 Handle<String> name_string = Handle<String>::cast(name());
499 if (name_string->length() != 0) {
500 result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
503 exotic_index_state_ =
504 result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
509 void LookupIterator::InternalizeName() {
510 if (name_->IsUniqueName()) return;
511 name_ = factory()->InternalizeString(Handle<String>::cast(name_));
515 bool LookupIterator::HasInterceptor(Map* map) const {
516 if (IsElement()) return map->has_indexed_interceptor();
517 return map->has_named_interceptor();
521 Handle<InterceptorInfo> LookupIterator::GetInterceptor() const {
522 DCHECK_EQ(INTERCEPTOR, state_);
523 return handle(GetInterceptor(JSObject::cast(*holder_)), isolate_);
527 InterceptorInfo* LookupIterator::GetInterceptor(JSObject* holder) const {
528 if (IsElement()) return holder->GetIndexedInterceptor();
529 return holder->GetNamedInterceptor();
533 bool LookupIterator::SkipInterceptor(JSObject* holder) {
534 auto info = GetInterceptor(holder);
535 // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
536 if (info->non_masking()) {
537 switch (interceptor_state_) {
538 case InterceptorState::kUninitialized:
539 interceptor_state_ = InterceptorState::kSkipNonMasking;
541 case InterceptorState::kSkipNonMasking:
543 case InterceptorState::kProcessNonMasking:
547 return interceptor_state_ == InterceptorState::kProcessNonMasking;
549 } // namespace internal