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_);
62 Handle<JSReceiver> LookupIterator::GetRoot(Handle<Object> receiver,
64 if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
65 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
67 unsigned int magic = 0xbbbbbbbb;
68 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
70 return Handle<JSReceiver>::cast(root);
74 Handle<Map> LookupIterator::GetReceiverMap() const {
75 if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
76 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
80 Handle<JSObject> LookupIterator::GetStoreTarget() const {
81 if (receiver_->IsJSGlobalProxy()) {
82 PrototypeIterator iter(isolate(), receiver_);
83 if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
84 return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
86 return Handle<JSObject>::cast(receiver_);
90 bool LookupIterator::IsBootstrapping() const {
91 return isolate_->bootstrapper()->IsActive();
95 bool LookupIterator::HasAccess() const {
96 DCHECK_EQ(ACCESS_CHECK, state_);
97 return isolate_->MayAccess(GetHolder<JSObject>());
101 void LookupIterator::ReloadPropertyInformation() {
102 state_ = BEFORE_PROPERTY;
103 interceptor_state_ = InterceptorState::kUninitialized;
104 state_ = LookupInHolder(*holder_map_, *holder_);
105 DCHECK(IsFound() || holder_map_->is_dictionary_map());
109 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
110 DCHECK(state_ == DATA || state_ == ACCESSOR);
111 DCHECK(HolderIsReceiverOrHiddenPrototype());
112 if (holder_map_->is_dictionary_map()) return;
114 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
115 JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
116 ReloadPropertyInformation();
120 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
121 PropertyAttributes attributes) {
122 DCHECK(state_ == DATA || state_ == ACCESSOR);
123 DCHECK(HolderIsReceiverOrHiddenPrototype());
124 Handle<JSObject> holder = GetHolder<JSObject>();
125 if (holder_map_->is_dictionary_map()) {
126 PropertyDetails details(attributes, v8::internal::DATA, 0,
127 PropertyCellType::kMutable);
128 JSObject::SetNormalizedProperty(holder, name(), value, details);
130 holder_map_ = Map::ReconfigureExistingProperty(
131 holder_map_, descriptor_number(), i::kData, attributes);
133 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
134 JSObject::MigrateToMap(holder, holder_map_);
137 ReloadPropertyInformation();
141 void LookupIterator::PrepareTransitionToDataProperty(
142 Handle<Object> value, PropertyAttributes attributes,
143 Object::StoreFromKeyed store_mode) {
144 if (state_ == TRANSITION) return;
145 DCHECK_NE(LookupIterator::ACCESSOR, state_);
146 DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, state_);
147 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
148 // Can only be called when the receiver is a JSObject. JSProxy has to be
149 // handled via a trap. Adding properties to primitive values is not
151 Handle<JSObject> receiver = GetStoreTarget();
153 if (!isolate()->IsInternallyUsedPropertyName(name()) &&
154 !receiver->map()->is_extensible()) {
158 auto transition = Map::TransitionToDataProperty(
159 handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
161 transition_ = transition;
163 if (receiver->IsGlobalObject()) {
164 // Install a property cell.
166 auto cell = GlobalObject::EnsurePropertyCell(
167 Handle<GlobalObject>::cast(receiver), name());
168 DCHECK(cell->value()->IsTheHole());
170 } else if (transition->GetBackPointer()->IsMap()) {
171 property_details_ = transition->GetLastDescriptorDetails();
172 has_property_ = true;
177 void LookupIterator::ApplyTransitionToDataProperty() {
178 DCHECK_EQ(TRANSITION, state_);
180 Handle<JSObject> receiver = GetStoreTarget();
181 if (receiver->IsGlobalObject()) return;
183 holder_map_ = transition_map();
184 JSObject::MigrateToMap(receiver, holder_map_);
185 ReloadPropertyInformation();
189 void LookupIterator::TransitionToAccessorProperty(
190 AccessorComponent component, Handle<Object> accessor,
191 PropertyAttributes attributes) {
192 DCHECK(!accessor->IsNull());
193 // Can only be called when the receiver is a JSObject. JSProxy has to be
194 // handled via a trap. Adding properties to primitive values is not
196 Handle<JSObject> receiver = GetStoreTarget();
199 Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
200 name_, component, accessor, attributes);
201 JSObject::MigrateToMap(receiver, holder_map_);
203 ReloadPropertyInformation();
205 if (!holder_map_->is_dictionary_map()) return;
208 // Install the accessor into the dictionary-mode object.
209 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
210 PropertyCellType::kMutable);
211 Handle<AccessorPair> pair;
212 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
213 pair = Handle<AccessorPair>::cast(GetAccessors());
214 // If the component and attributes are identical, nothing has to be done.
215 if (pair->get(component) == *accessor) {
216 if (property_details().attributes() == attributes) return;
218 pair = AccessorPair::Copy(pair);
219 pair->set(component, *accessor);
222 pair = isolate()->factory()->NewAccessorPair();
223 pair->set(component, *accessor);
225 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
227 JSObject::ReoptimizeIfPrototype(receiver);
228 holder_map_ = handle(receiver->map(), isolate_);
229 ReloadPropertyInformation();
233 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
234 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
235 // Optimization that only works if configuration_ is not mutable.
236 if (!check_prototype_chain()) return true;
237 DisallowHeapAllocation no_gc;
238 if (!receiver_->IsJSReceiver()) return false;
239 Object* current = *receiver_;
240 JSReceiver* holder = *holder_;
241 // JSProxy do not occur as hidden prototypes.
242 if (current->IsJSProxy()) {
243 return JSReceiver::cast(current) == holder;
245 PrototypeIterator iter(isolate(), current,
246 PrototypeIterator::START_AT_RECEIVER);
248 if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
249 DCHECK(!current->IsJSProxy());
251 } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
256 Handle<Object> LookupIterator::FetchValue() const {
257 Object* result = NULL;
258 Handle<JSObject> holder = GetHolder<JSObject>();
259 if (holder_map_->is_dictionary_map()) {
260 result = holder->property_dictionary()->ValueAt(number_);
261 if (holder_map_->IsGlobalObjectMap()) {
262 DCHECK(result->IsPropertyCell());
263 result = PropertyCell::cast(result)->value();
265 } else if (property_details_.type() == v8::internal::DATA) {
266 FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
267 return JSObject::FastPropertyAt(holder, property_details_.representation(),
270 result = holder_map_->instance_descriptors()->GetValue(number_);
272 return handle(result, isolate_);
276 int LookupIterator::GetAccessorIndex() const {
277 DCHECK(has_property_);
278 DCHECK(!holder_map_->is_dictionary_map());
279 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
280 return descriptor_number();
284 int LookupIterator::GetConstantIndex() const {
285 DCHECK(has_property_);
286 DCHECK(!holder_map_->is_dictionary_map());
287 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
288 return descriptor_number();
292 FieldIndex LookupIterator::GetFieldIndex() const {
293 DCHECK(has_property_);
294 DCHECK(!holder_map_->is_dictionary_map());
295 DCHECK_EQ(v8::internal::DATA, property_details_.type());
297 holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
298 bool is_double = representation().IsDouble();
299 return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
303 Handle<HeapType> LookupIterator::GetFieldType() const {
304 DCHECK(has_property_);
305 DCHECK(!holder_map_->is_dictionary_map());
306 DCHECK_EQ(v8::internal::DATA, property_details_.type());
308 holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
313 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
314 Handle<JSObject> holder = GetHolder<JSObject>();
315 Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
316 Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
317 DCHECK(value->IsPropertyCell());
318 return handle(PropertyCell::cast(value));
322 Handle<Object> LookupIterator::GetAccessors() const {
323 DCHECK_EQ(ACCESSOR, state_);
328 Handle<Object> LookupIterator::GetDataValue() const {
329 DCHECK_EQ(DATA, state_);
330 Handle<Object> value = FetchValue();
335 Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
336 DCHECK_EQ(DATA, state_);
337 Handle<JSObject> holder = GetHolder<JSObject>();
338 if (holder_map_->is_dictionary_map()) {
339 Handle<NameDictionary> property_dictionary =
340 handle(holder->property_dictionary());
341 if (holder->IsGlobalObject()) {
342 value = PropertyCell::UpdateCell(property_dictionary, dictionary_entry(),
343 value, property_details_);
345 property_dictionary->ValueAtPut(dictionary_entry(), *value);
347 } else if (property_details_.type() == v8::internal::DATA) {
348 holder->WriteToField(descriptor_number(), *value);
350 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
356 bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
357 DCHECK(exotic_index_state_ != ExoticIndexState::kNoIndex);
358 // Currently typed arrays are the only such objects.
359 if (!holder->IsJSTypedArray()) return false;
360 if (exotic_index_state_ == ExoticIndexState::kIndex) return true;
361 DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
363 // Compute and cache result.
364 if (name()->IsString()) {
365 Handle<String> name_string = Handle<String>::cast(name());
366 if (name_string->length() != 0) {
367 result = IsNonArrayIndexInteger(*name_string);
370 exotic_index_state_ =
371 result ? ExoticIndexState::kIndex : ExoticIndexState::kNoIndex;
376 void LookupIterator::InternalizeName() {
377 if (name_->IsUniqueName()) return;
378 name_ = factory()->InternalizeString(Handle<String>::cast(name_));
382 bool LookupIterator::SkipInterceptor(JSObject* holder) {
383 auto info = holder->GetNamedInterceptor();
384 // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
385 if (info->non_masking()) {
386 switch (interceptor_state_) {
387 case InterceptorState::kUninitialized:
388 interceptor_state_ = InterceptorState::kSkipNonMasking;
390 case InterceptorState::kSkipNonMasking:
392 case InterceptorState::kProcessNonMasking:
396 return interceptor_state_ == InterceptorState::kProcessNonMasking;
398 } } // namespace v8::internal