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/v8.h"
6 
7 #include "src/bootstrapper.h"
8 #include "src/deoptimizer.h"
9 #include "src/lookup.h"
10 #include "src/lookup-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 
Next()16 void LookupIterator::Next() {
17   DCHECK_NE(JSPROXY, state_);
18   DCHECK_NE(TRANSITION, state_);
19   DisallowHeapAllocation no_gc;
20   has_property_ = false;
21 
22   JSReceiver* holder = *holder_;
23   Map* map = *holder_map_;
24 
25   // Perform lookup on current holder.
26   state_ = LookupInHolder(map, holder);
27   if (IsFound()) return;
28 
29   // Continue lookup if lookup on current holder failed.
30   do {
31     JSReceiver* maybe_holder = NextHolder(map);
32     if (maybe_holder == NULL) break;
33     holder = maybe_holder;
34     map = holder->map();
35     state_ = LookupInHolder(map, holder);
36   } while (!IsFound());
37 
38   if (holder != *holder_) {
39     holder_ = handle(holder, isolate_);
40     holder_map_ = handle(map, isolate_);
41   }
42 }
43 
44 
GetRoot() const45 Handle<JSReceiver> LookupIterator::GetRoot() const {
46   if (receiver_->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver_);
47   Handle<Object> root =
48       handle(receiver_->GetRootMap(isolate_)->prototype(), isolate_);
49   CHECK(!root->IsNull());
50   return Handle<JSReceiver>::cast(root);
51 }
52 
53 
GetReceiverMap() const54 Handle<Map> LookupIterator::GetReceiverMap() const {
55   if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
56   return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
57 }
58 
59 
GetStoreTarget() const60 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));
65   }
66   return Handle<JSObject>::cast(receiver_);
67 }
68 
69 
IsBootstrapping() const70 bool LookupIterator::IsBootstrapping() const {
71   return isolate_->bootstrapper()->IsActive();
72 }
73 
74 
HasAccess(v8::AccessType access_type) const75 bool LookupIterator::HasAccess(v8::AccessType access_type) const {
76   DCHECK_EQ(ACCESS_CHECK, state_);
77   return isolate_->MayNamedAccess(GetHolder<JSObject>(), name_, access_type);
78 }
79 
80 
ReloadPropertyInformation()81 void LookupIterator::ReloadPropertyInformation() {
82   state_ = BEFORE_PROPERTY;
83   state_ = LookupInHolder(*holder_map_, *holder_);
84   DCHECK(IsFound() || holder_map_->is_dictionary_map());
85 }
86 
87 
PrepareForDataProperty(Handle<Object> value)88 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
89   DCHECK(state_ == DATA || state_ == ACCESSOR);
90   DCHECK(HolderIsReceiverOrHiddenPrototype());
91   if (holder_map_->is_dictionary_map()) return;
92   holder_map_ =
93       Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
94   JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
95   ReloadPropertyInformation();
96 }
97 
98 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)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, NORMAL, 0);
106     JSObject::SetNormalizedProperty(holder, name(), value, details);
107   } else {
108     holder_map_ = Map::ReconfigureDataProperty(holder_map_, descriptor_number(),
109                                                attributes);
110     JSObject::MigrateToMap(holder, holder_map_);
111   }
112 
113   ReloadPropertyInformation();
114 }
115 
116 
PrepareTransitionToDataProperty(Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)117 void LookupIterator::PrepareTransitionToDataProperty(
118     Handle<Object> value, PropertyAttributes attributes,
119     Object::StoreFromKeyed store_mode) {
120   if (state_ == TRANSITION) return;
121   DCHECK(state_ != LookupIterator::ACCESSOR ||
122          GetAccessors()->IsDeclaredAccessorInfo());
123   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
124 
125   // Can only be called when the receiver is a JSObject. JSProxy has to be
126   // handled via a trap. Adding properties to primitive values is not
127   // observable.
128   Handle<JSObject> receiver = GetStoreTarget();
129 
130   if (!name().is_identical_to(isolate()->factory()->hidden_string()) &&
131       !receiver->map()->is_extensible()) {
132     return;
133   }
134 
135   transition_map_ = Map::TransitionToDataProperty(
136       handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
137   state_ = TRANSITION;
138 }
139 
140 
ApplyTransitionToDataProperty()141 void LookupIterator::ApplyTransitionToDataProperty() {
142   DCHECK_EQ(TRANSITION, state_);
143 
144   Handle<JSObject> receiver = GetStoreTarget();
145   holder_ = receiver;
146   holder_map_ = transition_map_;
147   JSObject::MigrateToMap(receiver, holder_map_);
148   ReloadPropertyInformation();
149 }
150 
151 
TransitionToAccessorProperty(AccessorComponent component,Handle<Object> accessor,PropertyAttributes attributes)152 void LookupIterator::TransitionToAccessorProperty(
153     AccessorComponent component, Handle<Object> accessor,
154     PropertyAttributes attributes) {
155   DCHECK(!accessor->IsNull());
156   // Can only be called when the receiver is a JSObject. JSProxy has to be
157   // handled via a trap. Adding properties to primitive values is not
158   // observable.
159   Handle<JSObject> receiver = GetStoreTarget();
160   holder_ = receiver;
161   holder_map_ =
162       Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
163                                         name_, component, accessor, attributes);
164   JSObject::MigrateToMap(receiver, holder_map_);
165 
166   ReloadPropertyInformation();
167 
168   if (!holder_map_->is_dictionary_map()) return;
169 
170   // We have to deoptimize since accesses to data properties may have been
171   // inlined without a corresponding map-check.
172   if (holder_map_->IsGlobalObjectMap()) {
173     Deoptimizer::DeoptimizeGlobalObject(*receiver);
174   }
175 
176   // Install the accessor into the dictionary-mode object.
177   PropertyDetails details(attributes, CALLBACKS, 0);
178   Handle<AccessorPair> pair;
179   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
180     pair = Handle<AccessorPair>::cast(GetAccessors());
181     // If the component and attributes are identical, nothing has to be done.
182     if (pair->get(component) == *accessor) {
183       if (property_details().attributes() == attributes) return;
184     } else {
185       pair = AccessorPair::Copy(pair);
186       pair->set(component, *accessor);
187     }
188   } else {
189     pair = isolate()->factory()->NewAccessorPair();
190     pair->set(component, *accessor);
191   }
192   JSObject::SetNormalizedProperty(receiver, name_, pair, details);
193 
194   JSObject::ReoptimizeIfPrototype(receiver);
195   holder_map_ = handle(receiver->map(), isolate_);
196   ReloadPropertyInformation();
197 }
198 
199 
HolderIsReceiverOrHiddenPrototype() const200 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
201   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
202   // Optimization that only works if configuration_ is not mutable.
203   if (!check_prototype_chain()) return true;
204   DisallowHeapAllocation no_gc;
205   if (!receiver_->IsJSReceiver()) return false;
206   Object* current = *receiver_;
207   JSReceiver* holder = *holder_;
208   // JSProxy do not occur as hidden prototypes.
209   if (current->IsJSProxy()) {
210     return JSReceiver::cast(current) == holder;
211   }
212   PrototypeIterator iter(isolate(), current,
213                          PrototypeIterator::START_AT_RECEIVER);
214   do {
215     if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
216     DCHECK(!current->IsJSProxy());
217     iter.Advance();
218   } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
219   return false;
220 }
221 
222 
FetchValue() const223 Handle<Object> LookupIterator::FetchValue() const {
224   Object* result = NULL;
225   Handle<JSObject> holder = GetHolder<JSObject>();
226   if (holder_map_->is_dictionary_map()) {
227     result = holder->property_dictionary()->ValueAt(number_);
228     if (holder_map_->IsGlobalObjectMap()) {
229       result = PropertyCell::cast(result)->value();
230     }
231   } else if (property_details_.type() == v8::internal::FIELD) {
232     FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
233     return JSObject::FastPropertyAt(holder, property_details_.representation(),
234                                     field_index);
235   } else {
236     result = holder_map_->instance_descriptors()->GetValue(number_);
237   }
238   return handle(result, isolate_);
239 }
240 
241 
GetConstantIndex() const242 int LookupIterator::GetConstantIndex() const {
243   DCHECK(has_property_);
244   DCHECK(!holder_map_->is_dictionary_map());
245   DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
246   return descriptor_number();
247 }
248 
249 
GetFieldIndex() const250 FieldIndex LookupIterator::GetFieldIndex() const {
251   DCHECK(has_property_);
252   DCHECK(!holder_map_->is_dictionary_map());
253   DCHECK_EQ(v8::internal::FIELD, property_details_.type());
254   int index =
255       holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
256   bool is_double = representation().IsDouble();
257   return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
258 }
259 
260 
GetFieldType() const261 Handle<HeapType> LookupIterator::GetFieldType() const {
262   DCHECK(has_property_);
263   DCHECK(!holder_map_->is_dictionary_map());
264   DCHECK_EQ(v8::internal::FIELD, property_details_.type());
265   return handle(
266       holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
267       isolate_);
268 }
269 
270 
GetPropertyCell() const271 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
272   Handle<JSObject> holder = GetHolder<JSObject>();
273   Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
274   Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
275   return Handle<PropertyCell>(PropertyCell::cast(value));
276 }
277 
278 
GetAccessors() const279 Handle<Object> LookupIterator::GetAccessors() const {
280   DCHECK_EQ(ACCESSOR, state_);
281   return FetchValue();
282 }
283 
284 
GetDataValue() const285 Handle<Object> LookupIterator::GetDataValue() const {
286   DCHECK_EQ(DATA, state_);
287   Handle<Object> value = FetchValue();
288   return value;
289 }
290 
291 
WriteDataValue(Handle<Object> value)292 void LookupIterator::WriteDataValue(Handle<Object> value) {
293   DCHECK_EQ(DATA, state_);
294   Handle<JSObject> holder = GetHolder<JSObject>();
295   if (holder_map_->is_dictionary_map()) {
296     NameDictionary* property_dictionary = holder->property_dictionary();
297     if (holder->IsGlobalObject()) {
298       Handle<PropertyCell> cell(
299           PropertyCell::cast(property_dictionary->ValueAt(dictionary_entry())));
300       PropertyCell::SetValueInferType(cell, value);
301     } else {
302       property_dictionary->ValueAtPut(dictionary_entry(), *value);
303     }
304   } else if (property_details_.type() == v8::internal::FIELD) {
305     holder->WriteToField(descriptor_number(), *value);
306   } else {
307     DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
308   }
309 }
310 
311 
InternalizeName()312 void LookupIterator::InternalizeName() {
313   if (name_->IsUniqueName()) return;
314   name_ = factory()->InternalizeString(Handle<String>::cast(name_));
315 }
316 } }  // namespace v8::internal
317