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 12namespace v8 { 13namespace internal { 14 15 16void 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 45Handle<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 54Handle<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 60Handle<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 70bool LookupIterator::IsBootstrapping() const { 71 return isolate_->bootstrapper()->IsActive(); 72} 73 74 75bool 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 81void LookupIterator::ReloadPropertyInformation() { 82 state_ = BEFORE_PROPERTY; 83 state_ = LookupInHolder(*holder_map_, *holder_); 84 DCHECK(IsFound() || holder_map_->is_dictionary_map()); 85} 86 87 88void 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 99void 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 117void 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 141void 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 152void 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 200bool 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 223Handle<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 242int 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 250FieldIndex 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 261Handle<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 271Handle<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 279Handle<Object> LookupIterator::GetAccessors() const { 280 DCHECK_EQ(ACCESSOR, state_); 281 return FetchValue(); 282} 283 284 285Handle<Object> LookupIterator::GetDataValue() const { 286 DCHECK_EQ(DATA, state_); 287 Handle<Object> value = FetchValue(); 288 return value; 289} 290 291 292void 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 312void LookupIterator::InternalizeName() { 313 if (name_->IsUniqueName()) return; 314 name_ = factory()->InternalizeString(Handle<String>::cast(name_)); 315} 316} } // namespace v8::internal 317