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 == NULL) break;
33 holder = maybe_holder;
35 state_ = LookupInHolder(map, holder);
38 if (holder != *holder_) {
39 holder_ = handle(holder, isolate_);
40 holder_map_ = handle(map, isolate_);
45 Handle<JSReceiver> LookupIterator::GetRoot() const {
46 if (receiver_->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver_);
48 handle(receiver_->GetRootMap(isolate_)->prototype(), isolate_);
49 CHECK(!root->IsNull());
50 return Handle<JSReceiver>::cast(root);
54 Handle<Map> LookupIterator::GetReceiverMap() const {
55 if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
56 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
60 Handle<JSObject> LookupIterator::GetStoreTarget() const {
61 if (receiver_->IsJSGlobalProxy()) {
62 PrototypeIterator iter(isolate(), receiver_);
63 if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
64 return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
66 return Handle<JSObject>::cast(receiver_);
70 bool LookupIterator::IsBootstrapping() const {
71 return isolate_->bootstrapper()->IsActive();
75 bool LookupIterator::HasAccess(v8::AccessType access_type) const {
76 DCHECK_EQ(ACCESS_CHECK, state_);
77 return isolate_->MayNamedAccess(GetHolder<JSObject>(), name_, access_type);
81 void LookupIterator::ReloadPropertyInformation() {
82 state_ = BEFORE_PROPERTY;
83 state_ = LookupInHolder(*holder_map_, *holder_);
84 DCHECK(IsFound() || holder_map_->is_dictionary_map());
88 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
89 DCHECK(state_ == DATA || state_ == ACCESSOR);
90 DCHECK(HolderIsReceiverOrHiddenPrototype());
91 if (holder_map_->is_dictionary_map()) return;
93 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
94 JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
95 ReloadPropertyInformation();
99 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
100 PropertyAttributes attributes) {
101 DCHECK(state_ == DATA || state_ == ACCESSOR);
102 DCHECK(HolderIsReceiverOrHiddenPrototype());
103 Handle<JSObject> holder = GetHolder<JSObject>();
104 if (holder_map_->is_dictionary_map()) {
105 PropertyDetails details(attributes, v8::internal::DATA, 0);
106 JSObject::SetNormalizedProperty(holder, name(), value, details);
108 holder_map_ = Map::ReconfigureExistingProperty(
109 holder_map_, descriptor_number(), i::kData, attributes);
111 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
112 JSObject::MigrateToMap(holder, holder_map_);
115 ReloadPropertyInformation();
119 void LookupIterator::PrepareTransitionToDataProperty(
120 Handle<Object> value, PropertyAttributes attributes,
121 Object::StoreFromKeyed store_mode) {
122 if (state_ == TRANSITION) return;
123 DCHECK(state_ != LookupIterator::ACCESSOR);
124 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
125 DCHECK(!IsSpecialNumericIndex());
126 // Can only be called when the receiver is a JSObject. JSProxy has to be
127 // handled via a trap. Adding properties to primitive values is not
129 Handle<JSObject> receiver = GetStoreTarget();
131 if (!isolate()->IsInternallyUsedPropertyName(name()) &&
132 !receiver->map()->is_extensible()) {
136 auto transition = Map::TransitionToDataProperty(
137 handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
139 transition_ = transition;
141 if (receiver->IsGlobalObject()) {
142 // Install a property cell.
144 auto cell = GlobalObject::EnsurePropertyCell(
145 Handle<GlobalObject>::cast(receiver), name());
146 DCHECK(cell->value()->IsTheHole());
148 } else if (transition->GetBackPointer()->IsMap()) {
149 property_details_ = transition->GetLastDescriptorDetails();
150 has_property_ = true;
155 void LookupIterator::ApplyTransitionToDataProperty() {
156 DCHECK_EQ(TRANSITION, state_);
158 Handle<JSObject> receiver = GetStoreTarget();
159 if (receiver->IsGlobalObject()) return;
161 holder_map_ = transition_map();
162 JSObject::MigrateToMap(receiver, holder_map_);
163 ReloadPropertyInformation();
167 void LookupIterator::TransitionToAccessorProperty(
168 AccessorComponent component, Handle<Object> accessor,
169 PropertyAttributes attributes) {
170 DCHECK(!accessor->IsNull());
171 // Can only be called when the receiver is a JSObject. JSProxy has to be
172 // handled via a trap. Adding properties to primitive values is not
174 Handle<JSObject> receiver = GetStoreTarget();
177 Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
178 name_, component, accessor, attributes);
179 JSObject::MigrateToMap(receiver, holder_map_);
181 ReloadPropertyInformation();
183 if (!holder_map_->is_dictionary_map()) return;
185 // We have to deoptimize since accesses to data properties may have been
186 // inlined without a corresponding map-check.
187 if (holder_map_->IsGlobalObjectMap()) {
188 Deoptimizer::DeoptimizeGlobalObject(*receiver);
191 // Install the accessor into the dictionary-mode object.
192 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0);
193 Handle<AccessorPair> pair;
194 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
195 pair = Handle<AccessorPair>::cast(GetAccessors());
196 // If the component and attributes are identical, nothing has to be done.
197 if (pair->get(component) == *accessor) {
198 if (property_details().attributes() == attributes) return;
200 pair = AccessorPair::Copy(pair);
201 pair->set(component, *accessor);
204 pair = isolate()->factory()->NewAccessorPair();
205 pair->set(component, *accessor);
207 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
209 JSObject::ReoptimizeIfPrototype(receiver);
210 holder_map_ = handle(receiver->map(), isolate_);
211 ReloadPropertyInformation();
215 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
216 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
217 // Optimization that only works if configuration_ is not mutable.
218 if (!check_prototype_chain()) return true;
219 DisallowHeapAllocation no_gc;
220 if (!receiver_->IsJSReceiver()) return false;
221 Object* current = *receiver_;
222 JSReceiver* holder = *holder_;
223 // JSProxy do not occur as hidden prototypes.
224 if (current->IsJSProxy()) {
225 return JSReceiver::cast(current) == holder;
227 PrototypeIterator iter(isolate(), current,
228 PrototypeIterator::START_AT_RECEIVER);
230 if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
231 DCHECK(!current->IsJSProxy());
233 } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
238 Handle<Object> LookupIterator::FetchValue() const {
239 Object* result = NULL;
240 Handle<JSObject> holder = GetHolder<JSObject>();
241 if (holder_map_->is_dictionary_map()) {
242 result = holder->property_dictionary()->ValueAt(number_);
243 if (holder_map_->IsGlobalObjectMap()) {
244 result = PropertyCell::cast(result)->value();
246 } else if (property_details_.type() == v8::internal::DATA) {
247 FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
248 return JSObject::FastPropertyAt(holder, property_details_.representation(),
251 result = holder_map_->instance_descriptors()->GetValue(number_);
253 return handle(result, isolate_);
257 int LookupIterator::GetAccessorIndex() const {
258 DCHECK(has_property_);
259 DCHECK(!holder_map_->is_dictionary_map());
260 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
261 return descriptor_number();
265 int LookupIterator::GetConstantIndex() const {
266 DCHECK(has_property_);
267 DCHECK(!holder_map_->is_dictionary_map());
268 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
269 return descriptor_number();
273 FieldIndex LookupIterator::GetFieldIndex() const {
274 DCHECK(has_property_);
275 DCHECK(!holder_map_->is_dictionary_map());
276 DCHECK_EQ(v8::internal::DATA, property_details_.type());
278 holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
279 bool is_double = representation().IsDouble();
280 return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
284 Handle<HeapType> LookupIterator::GetFieldType() const {
285 DCHECK(has_property_);
286 DCHECK(!holder_map_->is_dictionary_map());
287 DCHECK_EQ(v8::internal::DATA, property_details_.type());
289 holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
294 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
295 Handle<JSObject> holder = GetHolder<JSObject>();
296 Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
297 Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
298 return Handle<PropertyCell>(PropertyCell::cast(value));
302 Handle<Object> LookupIterator::GetAccessors() const {
303 DCHECK_EQ(ACCESSOR, state_);
308 Handle<Object> LookupIterator::GetDataValue() const {
309 DCHECK_EQ(DATA, state_);
310 Handle<Object> value = FetchValue();
315 Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
316 DCHECK_EQ(DATA, state_);
317 Handle<JSObject> holder = GetHolder<JSObject>();
318 if (holder_map_->is_dictionary_map()) {
319 NameDictionary* property_dictionary = holder->property_dictionary();
320 if (holder->IsGlobalObject()) {
321 Handle<PropertyCell> cell(
322 PropertyCell::cast(property_dictionary->ValueAt(dictionary_entry())));
323 value = PropertyCell::SetValueInferType(cell, value);
325 property_dictionary->ValueAtPut(dictionary_entry(), *value);
327 } else if (property_details_.type() == v8::internal::DATA) {
328 holder->WriteToField(descriptor_number(), *value);
330 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
336 bool LookupIterator::IsSpecialNumericIndex() const {
337 if (GetStoreTarget()->IsJSTypedArray() && name()->IsString()) {
338 Handle<String> name_string = Handle<String>::cast(name());
339 if (name_string->length() > 0) {
341 StringToDouble(isolate()->unicode_cache(), name_string, NO_FLAGS);
342 if (!std::isnan(d)) {
343 if (String::Equals(isolate()->factory()->minus_zero_string(),
347 Factory* factory = isolate()->factory();
348 Handle<Object> num = factory->NewNumber(d);
349 Handle<String> roundtrip_string = factory->NumberToString(num);
350 if (String::Equals(name_string, roundtrip_string)) return true;
358 void LookupIterator::InternalizeName() {
359 if (name_->IsUniqueName()) return;
360 name_ = factory()->InternalizeString(Handle<String>::cast(name_));
362 } } // namespace v8::internal