Home | History | Annotate | Download | only in src
      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 
     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 
     45 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 
     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_);
     57 }
     58 
     59 
     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));
     65   }
     66   return Handle<JSObject>::cast(receiver_);
     67 }
     68 
     69 
     70 bool LookupIterator::IsBootstrapping() const {
     71   return isolate_->bootstrapper()->IsActive();
     72 }
     73 
     74 
     75 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 
     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 
     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 
     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 
    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 
    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 
    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 
    200 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 
    223 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 
    242 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 
    250 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 
    261 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 
    271 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 
    279 Handle<Object> LookupIterator::GetAccessors() const {
    280   DCHECK_EQ(ACCESSOR, state_);
    281   return FetchValue();
    282 }
    283 
    284 
    285 Handle<Object> LookupIterator::GetDataValue() const {
    286   DCHECK_EQ(DATA, state_);
    287   Handle<Object> value = FetchValue();
    288   return value;
    289 }
    290 
    291 
    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 
    312 void LookupIterator::InternalizeName() {
    313   if (name_->IsUniqueName()) return;
    314   name_ = factory()->InternalizeString(Handle<String>::cast(name_));
    315 }
    316 } }  // namespace v8::internal
    317