Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 5 | #include "src/lookup.h" |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 6 | |
| 7 | #include "src/bootstrapper.h" |
| 8 | #include "src/deoptimizer.h" |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 9 | #include "src/elements.h" |
| 10 | #include "src/isolate-inl.h" |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 11 | |
| 12 | namespace v8 { |
| 13 | namespace internal { |
| 14 | |
| 15 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 16 | // static |
| 17 | LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate, |
| 18 | Handle<Object> receiver, |
| 19 | Handle<Object> key, |
| 20 | bool* success, |
| 21 | Configuration configuration) { |
| 22 | uint32_t index = 0; |
| 23 | if (key->ToArrayIndex(&index)) { |
| 24 | *success = true; |
| 25 | return LookupIterator(isolate, receiver, index, configuration); |
| 26 | } |
| 27 | |
| 28 | Handle<Name> name; |
| 29 | *success = Object::ToName(isolate, key).ToHandle(&name); |
| 30 | if (!*success) { |
| 31 | DCHECK(isolate->has_pending_exception()); |
| 32 | // Return an unusable dummy. |
| 33 | return LookupIterator(receiver, isolate->factory()->empty_string()); |
| 34 | } |
| 35 | |
| 36 | if (name->AsArrayIndex(&index)) { |
| 37 | LookupIterator it(isolate, receiver, index, configuration); |
| 38 | // Here we try to avoid having to rebuild the string later |
| 39 | // by storing it on the indexed LookupIterator. |
| 40 | it.name_ = name; |
| 41 | return it; |
| 42 | } |
| 43 | |
| 44 | return LookupIterator(receiver, name, configuration); |
| 45 | } |
| 46 | |
| 47 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 48 | void LookupIterator::Next() { |
| 49 | DCHECK_NE(JSPROXY, state_); |
| 50 | DCHECK_NE(TRANSITION, state_); |
| 51 | DisallowHeapAllocation no_gc; |
| 52 | has_property_ = false; |
| 53 | |
| 54 | JSReceiver* holder = *holder_; |
| 55 | Map* map = *holder_map_; |
| 56 | |
| 57 | // Perform lookup on current holder. |
| 58 | state_ = LookupInHolder(map, holder); |
| 59 | if (IsFound()) return; |
| 60 | |
| 61 | // Continue lookup if lookup on current holder failed. |
| 62 | do { |
| 63 | JSReceiver* maybe_holder = NextHolder(map); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 64 | if (maybe_holder == nullptr) { |
| 65 | if (interceptor_state_ == InterceptorState::kSkipNonMasking) { |
| 66 | RestartLookupForNonMaskingInterceptors(); |
| 67 | return; |
| 68 | } |
| 69 | break; |
| 70 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 71 | holder = maybe_holder; |
| 72 | map = holder->map(); |
| 73 | state_ = LookupInHolder(map, holder); |
| 74 | } while (!IsFound()); |
| 75 | |
| 76 | if (holder != *holder_) { |
| 77 | holder_ = handle(holder, isolate_); |
| 78 | holder_map_ = handle(map, isolate_); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 83 | void LookupIterator::RestartInternal(InterceptorState interceptor_state) { |
| 84 | state_ = NOT_FOUND; |
| 85 | interceptor_state_ = interceptor_state; |
| 86 | property_details_ = PropertyDetails::Empty(); |
| 87 | holder_ = initial_holder_; |
| 88 | holder_map_ = handle(holder_->map(), isolate_); |
| 89 | number_ = DescriptorArray::kNotFound; |
| 90 | Next(); |
| 91 | } |
| 92 | |
| 93 | |
| 94 | // static |
| 95 | Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( |
| 96 | Isolate* isolate, Handle<Object> receiver, uint32_t index) { |
| 97 | // Strings are the only objects with properties (only elements) directly on |
| 98 | // the wrapper. Hence we can skip generating the wrapper for all other cases. |
| 99 | if (index != kMaxUInt32 && receiver->IsString() && |
| 100 | index < static_cast<uint32_t>(String::cast(*receiver)->length())) { |
| 101 | // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native |
| 102 | // context, ensuring that we don't leak it into JS? |
| 103 | Handle<JSFunction> constructor = isolate->string_function(); |
| 104 | Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); |
| 105 | Handle<JSValue>::cast(result)->set_value(*receiver); |
| 106 | return result; |
| 107 | } |
| 108 | auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate); |
| 109 | if (root->IsNull()) { |
| 110 | unsigned int magic = 0xbbbbbbbb; |
| 111 | isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); |
| 112 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 113 | return Handle<JSReceiver>::cast(root); |
| 114 | } |
| 115 | |
| 116 | |
| 117 | Handle<Map> LookupIterator::GetReceiverMap() const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 118 | if (receiver_->IsNumber()) return factory()->heap_number_map(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 119 | return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); |
| 120 | } |
| 121 | |
| 122 | |
| 123 | Handle<JSObject> LookupIterator::GetStoreTarget() const { |
| 124 | if (receiver_->IsJSGlobalProxy()) { |
| 125 | PrototypeIterator iter(isolate(), receiver_); |
| 126 | if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 127 | return PrototypeIterator::GetCurrent<JSGlobalObject>(iter); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 128 | } |
| 129 | return Handle<JSObject>::cast(receiver_); |
| 130 | } |
| 131 | |
| 132 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 133 | bool LookupIterator::HasAccess() const { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 134 | DCHECK_EQ(ACCESS_CHECK, state_); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 135 | return isolate_->MayAccess(handle(isolate_->context()), |
| 136 | GetHolder<JSObject>()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | |
| 140 | void LookupIterator::ReloadPropertyInformation() { |
| 141 | state_ = BEFORE_PROPERTY; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 142 | interceptor_state_ = InterceptorState::kUninitialized; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 143 | state_ = LookupInHolder(*holder_map_, *holder_); |
| 144 | DCHECK(IsFound() || holder_map_->is_dictionary_map()); |
| 145 | } |
| 146 | |
| 147 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 148 | void LookupIterator::ReloadHolderMap() { |
| 149 | DCHECK_EQ(DATA, state_); |
| 150 | DCHECK(IsElement()); |
| 151 | DCHECK(JSObject::cast(*holder_)->HasFixedTypedArrayElements()); |
| 152 | if (*holder_map_ != holder_->map()) { |
| 153 | holder_map_ = handle(holder_->map(), isolate_); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 158 | void LookupIterator::PrepareForDataProperty(Handle<Object> value) { |
| 159 | DCHECK(state_ == DATA || state_ == ACCESSOR); |
| 160 | DCHECK(HolderIsReceiverOrHiddenPrototype()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 161 | |
| 162 | Handle<JSObject> holder = GetHolder<JSObject>(); |
| 163 | |
| 164 | if (IsElement()) { |
| 165 | ElementsKind kind = holder_map_->elements_kind(); |
| 166 | ElementsKind to = value->OptimalElementsKind(); |
| 167 | if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to); |
| 168 | to = GetMoreGeneralElementsKind(kind, to); |
| 169 | JSObject::TransitionElementsKind(holder, to); |
| 170 | holder_map_ = handle(holder->map(), isolate_); |
| 171 | |
| 172 | // Copy the backing store if it is copy-on-write. |
| 173 | if (IsFastSmiOrObjectElementsKind(to)) { |
| 174 | JSObject::EnsureWritableFastElements(holder); |
| 175 | } |
| 176 | |
| 177 | } else { |
| 178 | if (holder_map_->is_dictionary_map()) return; |
| 179 | holder_map_ = |
| 180 | Map::PrepareForDataProperty(holder_map_, descriptor_number(), value); |
| 181 | } |
| 182 | |
| 183 | JSObject::MigrateToMap(holder, holder_map_); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 184 | ReloadPropertyInformation(); |
| 185 | } |
| 186 | |
| 187 | |
| 188 | void LookupIterator::ReconfigureDataProperty(Handle<Object> value, |
| 189 | PropertyAttributes attributes) { |
| 190 | DCHECK(state_ == DATA || state_ == ACCESSOR); |
| 191 | DCHECK(HolderIsReceiverOrHiddenPrototype()); |
| 192 | Handle<JSObject> holder = GetHolder<JSObject>(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 193 | if (IsElement()) { |
| 194 | DCHECK(!holder->HasFixedTypedArrayElements()); |
| 195 | DCHECK(attributes != NONE || !holder->HasFastElements()); |
| 196 | Handle<FixedArrayBase> elements(holder->elements()); |
| 197 | holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value, |
| 198 | attributes); |
| 199 | } else if (holder_map_->is_dictionary_map()) { |
| 200 | PropertyDetails details(attributes, v8::internal::DATA, 0, |
| 201 | PropertyCellType::kMutable); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 202 | JSObject::SetNormalizedProperty(holder, name(), value, details); |
| 203 | } else { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 204 | holder_map_ = Map::ReconfigureExistingProperty( |
| 205 | holder_map_, descriptor_number(), i::kData, attributes); |
| 206 | holder_map_ = |
| 207 | Map::PrepareForDataProperty(holder_map_, descriptor_number(), value); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 208 | JSObject::MigrateToMap(holder, holder_map_); |
| 209 | } |
| 210 | |
| 211 | ReloadPropertyInformation(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 212 | WriteDataValue(value); |
| 213 | |
| 214 | #if VERIFY_HEAP |
| 215 | if (FLAG_verify_heap) { |
| 216 | holder->JSObjectVerify(); |
| 217 | } |
| 218 | #endif |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 219 | } |
| 220 | |
| 221 | |
| 222 | void LookupIterator::PrepareTransitionToDataProperty( |
| 223 | Handle<Object> value, PropertyAttributes attributes, |
| 224 | Object::StoreFromKeyed store_mode) { |
| 225 | if (state_ == TRANSITION) return; |
| 226 | DCHECK(state_ != LookupIterator::ACCESSOR || |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 227 | (GetAccessors()->IsAccessorInfo() && |
| 228 | AccessorInfo::cast(*GetAccessors())->is_special_data_property())); |
| 229 | DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 230 | DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 231 | // Can only be called when the receiver is a JSObject. JSProxy has to be |
| 232 | // handled via a trap. Adding properties to primitive values is not |
| 233 | // observable. |
| 234 | Handle<JSObject> receiver = GetStoreTarget(); |
| 235 | |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 236 | if (!isolate()->IsInternallyUsedPropertyName(name()) && |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 237 | !receiver->map()->is_extensible()) { |
| 238 | return; |
| 239 | } |
| 240 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 241 | auto transition = Map::TransitionToDataProperty( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 242 | handle(receiver->map(), isolate_), name_, value, attributes, store_mode); |
| 243 | state_ = TRANSITION; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 244 | transition_ = transition; |
| 245 | |
| 246 | if (receiver->IsJSGlobalObject()) { |
| 247 | // Install a property cell. |
| 248 | InternalizeName(); |
| 249 | auto cell = JSGlobalObject::EnsurePropertyCell( |
| 250 | Handle<JSGlobalObject>::cast(receiver), name()); |
| 251 | DCHECK(cell->value()->IsTheHole()); |
| 252 | transition_ = cell; |
| 253 | } else if (!transition->is_dictionary_map()) { |
| 254 | property_details_ = transition->GetLastDescriptorDetails(); |
| 255 | has_property_ = true; |
| 256 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 257 | } |
| 258 | |
| 259 | |
| 260 | void LookupIterator::ApplyTransitionToDataProperty() { |
| 261 | DCHECK_EQ(TRANSITION, state_); |
| 262 | |
| 263 | Handle<JSObject> receiver = GetStoreTarget(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 264 | if (receiver->IsJSGlobalObject()) return; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 265 | holder_ = receiver; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 266 | holder_map_ = transition_map(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 267 | JSObject::MigrateToMap(receiver, holder_map_); |
| 268 | ReloadPropertyInformation(); |
| 269 | } |
| 270 | |
| 271 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 272 | void LookupIterator::Delete() { |
| 273 | Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); |
| 274 | if (IsElement()) { |
| 275 | Handle<JSObject> object = Handle<JSObject>::cast(holder); |
| 276 | ElementsAccessor* accessor = object->GetElementsAccessor(); |
| 277 | accessor->Delete(object, number_); |
| 278 | } else { |
| 279 | PropertyNormalizationMode mode = holder->map()->is_prototype_map() |
| 280 | ? KEEP_INOBJECT_PROPERTIES |
| 281 | : CLEAR_INOBJECT_PROPERTIES; |
| 282 | |
| 283 | if (holder->HasFastProperties()) { |
| 284 | JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, |
| 285 | "DeletingProperty"); |
| 286 | holder_map_ = handle(holder->map(), isolate_); |
| 287 | ReloadPropertyInformation(); |
| 288 | } |
| 289 | // TODO(verwaest): Get rid of the name_ argument. |
| 290 | JSReceiver::DeleteNormalizedProperty(holder, name_, number_); |
| 291 | if (holder->IsJSObject()) { |
| 292 | JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 298 | void LookupIterator::TransitionToAccessorProperty( |
| 299 | AccessorComponent component, Handle<Object> accessor, |
| 300 | PropertyAttributes attributes) { |
| 301 | DCHECK(!accessor->IsNull()); |
| 302 | // Can only be called when the receiver is a JSObject. JSProxy has to be |
| 303 | // handled via a trap. Adding properties to primitive values is not |
| 304 | // observable. |
| 305 | Handle<JSObject> receiver = GetStoreTarget(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 306 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 307 | if (!IsElement() && !receiver->map()->is_dictionary_map()) { |
| 308 | holder_ = receiver; |
| 309 | holder_map_ = Map::TransitionToAccessorProperty( |
| 310 | handle(receiver->map(), isolate_), name_, component, accessor, |
| 311 | attributes); |
| 312 | JSObject::MigrateToMap(receiver, holder_map_); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 313 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 314 | ReloadPropertyInformation(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 315 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 316 | if (!holder_map_->is_dictionary_map()) return; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 317 | } |
| 318 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 319 | Handle<AccessorPair> pair; |
| 320 | if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { |
| 321 | pair = Handle<AccessorPair>::cast(GetAccessors()); |
| 322 | // If the component and attributes are identical, nothing has to be done. |
| 323 | if (pair->get(component) == *accessor) { |
| 324 | if (property_details().attributes() == attributes) return; |
| 325 | } else { |
| 326 | pair = AccessorPair::Copy(pair); |
| 327 | pair->set(component, *accessor); |
| 328 | } |
| 329 | } else { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 330 | pair = factory()->NewAccessorPair(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 331 | pair->set(component, *accessor); |
| 332 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 333 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 334 | TransitionToAccessorPair(pair, attributes); |
| 335 | |
| 336 | #if VERIFY_HEAP |
| 337 | if (FLAG_verify_heap) { |
| 338 | receiver->JSObjectVerify(); |
| 339 | } |
| 340 | #endif |
| 341 | } |
| 342 | |
| 343 | |
| 344 | void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, |
| 345 | PropertyAttributes attributes) { |
| 346 | Handle<JSObject> receiver = GetStoreTarget(); |
| 347 | holder_ = receiver; |
| 348 | |
| 349 | PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0, |
| 350 | PropertyCellType::kMutable); |
| 351 | |
| 352 | if (IsElement()) { |
| 353 | // TODO(verwaest): Move code into the element accessor. |
| 354 | Handle<SeededNumberDictionary> dictionary = |
| 355 | JSObject::NormalizeElements(receiver); |
| 356 | |
| 357 | // We unconditionally pass used_as_prototype=false here because the call |
| 358 | // to RequireSlowElements takes care of the required IC clearing and |
| 359 | // we don't want to walk the heap twice. |
| 360 | dictionary = |
| 361 | SeededNumberDictionary::Set(dictionary, index_, pair, details, false); |
| 362 | receiver->RequireSlowElements(*dictionary); |
| 363 | |
| 364 | if (receiver->HasSlowArgumentsElements()) { |
| 365 | FixedArray* parameter_map = FixedArray::cast(receiver->elements()); |
| 366 | uint32_t length = parameter_map->length() - 2; |
| 367 | if (number_ < length) { |
| 368 | parameter_map->set(number_ + 2, heap()->the_hole_value()); |
| 369 | } |
| 370 | FixedArray::cast(receiver->elements())->set(1, *dictionary); |
| 371 | } else { |
| 372 | receiver->set_elements(*dictionary); |
| 373 | } |
| 374 | } else { |
| 375 | PropertyNormalizationMode mode = receiver->map()->is_prototype_map() |
| 376 | ? KEEP_INOBJECT_PROPERTIES |
| 377 | : CLEAR_INOBJECT_PROPERTIES; |
| 378 | // Normalize object to make this operation simple. |
| 379 | JSObject::NormalizeProperties(receiver, mode, 0, |
| 380 | "TransitionToAccessorPair"); |
| 381 | |
| 382 | JSObject::SetNormalizedProperty(receiver, name_, pair, details); |
| 383 | JSObject::ReoptimizeIfPrototype(receiver); |
| 384 | } |
| 385 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 386 | holder_map_ = handle(receiver->map(), isolate_); |
| 387 | ReloadPropertyInformation(); |
| 388 | } |
| 389 | |
| 390 | |
| 391 | bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { |
| 392 | DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 393 | return InternalHolderIsReceiverOrHiddenPrototype(); |
| 394 | } |
| 395 | |
| 396 | bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 397 | // Optimization that only works if configuration_ is not mutable. |
| 398 | if (!check_prototype_chain()) return true; |
| 399 | DisallowHeapAllocation no_gc; |
| 400 | if (!receiver_->IsJSReceiver()) return false; |
| 401 | Object* current = *receiver_; |
| 402 | JSReceiver* holder = *holder_; |
| 403 | // JSProxy do not occur as hidden prototypes. |
| 404 | if (current->IsJSProxy()) { |
| 405 | return JSReceiver::cast(current) == holder; |
| 406 | } |
| 407 | PrototypeIterator iter(isolate(), current, |
| 408 | PrototypeIterator::START_AT_RECEIVER); |
| 409 | do { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 410 | if (iter.GetCurrent<JSReceiver>() == holder) return true; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 411 | DCHECK(!current->IsJSProxy()); |
| 412 | iter.Advance(); |
| 413 | } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)); |
| 414 | return false; |
| 415 | } |
| 416 | |
| 417 | |
| 418 | Handle<Object> LookupIterator::FetchValue() const { |
| 419 | Object* result = NULL; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 420 | if (IsElement()) { |
| 421 | Handle<JSObject> holder = GetHolder<JSObject>(); |
| 422 | // TODO(verwaest): Optimize. |
| 423 | if (holder->IsStringObjectWithCharacterAt(index_)) { |
| 424 | Handle<JSValue> js_value = Handle<JSValue>::cast(holder); |
| 425 | Handle<String> string(String::cast(js_value->value())); |
| 426 | return factory()->LookupSingleCharacterStringFromCode( |
| 427 | String::Flatten(string)->Get(index_)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 428 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 429 | |
| 430 | ElementsAccessor* accessor = holder->GetElementsAccessor(); |
| 431 | return accessor->Get(handle(holder->elements()), number_); |
| 432 | } else if (holder_map_->IsJSGlobalObjectMap()) { |
| 433 | Handle<JSObject> holder = GetHolder<JSObject>(); |
| 434 | result = holder->global_dictionary()->ValueAt(number_); |
| 435 | DCHECK(result->IsPropertyCell()); |
| 436 | result = PropertyCell::cast(result)->value(); |
| 437 | } else if (holder_map_->is_dictionary_map()) { |
| 438 | result = holder_->property_dictionary()->ValueAt(number_); |
| 439 | } else if (property_details_.type() == v8::internal::DATA) { |
| 440 | Handle<JSObject> holder = GetHolder<JSObject>(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 441 | FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_); |
| 442 | return JSObject::FastPropertyAt(holder, property_details_.representation(), |
| 443 | field_index); |
| 444 | } else { |
| 445 | result = holder_map_->instance_descriptors()->GetValue(number_); |
| 446 | } |
| 447 | return handle(result, isolate_); |
| 448 | } |
| 449 | |
| 450 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 451 | int LookupIterator::GetAccessorIndex() const { |
| 452 | DCHECK(has_property_); |
| 453 | DCHECK(!holder_map_->is_dictionary_map()); |
| 454 | DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type()); |
| 455 | return descriptor_number(); |
| 456 | } |
| 457 | |
| 458 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 459 | int LookupIterator::GetConstantIndex() const { |
| 460 | DCHECK(has_property_); |
| 461 | DCHECK(!holder_map_->is_dictionary_map()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 462 | DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); |
| 463 | DCHECK(!IsElement()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 464 | return descriptor_number(); |
| 465 | } |
| 466 | |
| 467 | |
| 468 | FieldIndex LookupIterator::GetFieldIndex() const { |
| 469 | DCHECK(has_property_); |
| 470 | DCHECK(!holder_map_->is_dictionary_map()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 471 | DCHECK_EQ(v8::internal::DATA, property_details_.type()); |
| 472 | DCHECK(!IsElement()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 473 | int index = |
| 474 | holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number()); |
| 475 | bool is_double = representation().IsDouble(); |
| 476 | return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double); |
| 477 | } |
| 478 | |
| 479 | |
| 480 | Handle<HeapType> LookupIterator::GetFieldType() const { |
| 481 | DCHECK(has_property_); |
| 482 | DCHECK(!holder_map_->is_dictionary_map()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 483 | DCHECK_EQ(v8::internal::DATA, property_details_.type()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 484 | return handle( |
| 485 | holder_map_->instance_descriptors()->GetFieldType(descriptor_number()), |
| 486 | isolate_); |
| 487 | } |
| 488 | |
| 489 | |
| 490 | Handle<PropertyCell> LookupIterator::GetPropertyCell() const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 491 | DCHECK(!IsElement()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 492 | Handle<JSObject> holder = GetHolder<JSObject>(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 493 | Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder); |
| 494 | Object* value = global->global_dictionary()->ValueAt(dictionary_entry()); |
| 495 | DCHECK(value->IsPropertyCell()); |
| 496 | return handle(PropertyCell::cast(value)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 497 | } |
| 498 | |
| 499 | |
| 500 | Handle<Object> LookupIterator::GetAccessors() const { |
| 501 | DCHECK_EQ(ACCESSOR, state_); |
| 502 | return FetchValue(); |
| 503 | } |
| 504 | |
| 505 | |
| 506 | Handle<Object> LookupIterator::GetDataValue() const { |
| 507 | DCHECK_EQ(DATA, state_); |
| 508 | Handle<Object> value = FetchValue(); |
| 509 | return value; |
| 510 | } |
| 511 | |
| 512 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 513 | void LookupIterator::WriteDataValue(Handle<Object> value) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 514 | DCHECK_EQ(DATA, state_); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 515 | Handle<JSReceiver> holder = GetHolder<JSReceiver>(); |
| 516 | if (IsElement()) { |
| 517 | Handle<JSObject> object = Handle<JSObject>::cast(holder); |
| 518 | ElementsAccessor* accessor = object->GetElementsAccessor(); |
| 519 | accessor->Set(object->elements(), number_, *value); |
| 520 | } else if (holder->IsJSGlobalObject()) { |
| 521 | Handle<GlobalDictionary> property_dictionary = |
| 522 | handle(JSObject::cast(*holder)->global_dictionary()); |
| 523 | PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value, |
| 524 | property_details_); |
| 525 | } else if (holder_map_->is_dictionary_map()) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 526 | NameDictionary* property_dictionary = holder->property_dictionary(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 527 | property_dictionary->ValueAtPut(dictionary_entry(), *value); |
| 528 | } else if (property_details_.type() == v8::internal::DATA) { |
| 529 | JSObject::cast(*holder)->WriteToField(descriptor_number(), *value); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 530 | } else { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 531 | DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 532 | } |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 533 | } |
| 534 | |
| 535 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 536 | bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) { |
| 537 | DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic); |
| 538 | if (exotic_index_state_ == ExoticIndexState::kExotic) return true; |
| 539 | if (!InternalHolderIsReceiverOrHiddenPrototype()) { |
| 540 | exotic_index_state_ = ExoticIndexState::kNotExotic; |
| 541 | return false; |
| 542 | } |
| 543 | DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized); |
| 544 | bool result = false; |
| 545 | // Compute and cache result. |
| 546 | if (IsElement()) { |
| 547 | result = index_ >= JSTypedArray::cast(holder)->length_value(); |
| 548 | } else if (name()->IsString()) { |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 549 | Handle<String> name_string = Handle<String>::cast(name()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 550 | if (name_string->length() != 0) { |
| 551 | result = IsSpecialIndex(isolate_->unicode_cache(), *name_string); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 552 | } |
| 553 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 554 | exotic_index_state_ = |
| 555 | result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic; |
| 556 | return result; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 557 | } |
| 558 | |
| 559 | |
| 560 | void LookupIterator::InternalizeName() { |
| 561 | if (name_->IsUniqueName()) return; |
| 562 | name_ = factory()->InternalizeString(Handle<String>::cast(name_)); |
| 563 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 564 | |
| 565 | |
| 566 | bool LookupIterator::HasInterceptor(Map* map) const { |
| 567 | if (IsElement()) return map->has_indexed_interceptor(); |
| 568 | return map->has_named_interceptor(); |
| 569 | } |
| 570 | |
| 571 | |
| 572 | bool LookupIterator::SkipInterceptor(JSObject* holder) { |
| 573 | auto info = GetInterceptor(holder); |
| 574 | // TODO(dcarney): check for symbol/can_intercept_symbols here as well. |
| 575 | if (info->non_masking()) { |
| 576 | switch (interceptor_state_) { |
| 577 | case InterceptorState::kUninitialized: |
| 578 | interceptor_state_ = InterceptorState::kSkipNonMasking; |
| 579 | // Fall through. |
| 580 | case InterceptorState::kSkipNonMasking: |
| 581 | return true; |
| 582 | case InterceptorState::kProcessNonMasking: |
| 583 | return false; |
| 584 | } |
| 585 | } |
| 586 | return interceptor_state_ == InterceptorState::kProcessNonMasking; |
| 587 | } |
| 588 | |
| 589 | |
| 590 | JSReceiver* LookupIterator::NextHolder(Map* map) { |
| 591 | DisallowHeapAllocation no_gc; |
| 592 | if (!map->prototype()->IsJSReceiver()) return NULL; |
| 593 | |
| 594 | JSReceiver* next = JSReceiver::cast(map->prototype()); |
| 595 | DCHECK(!next->map()->IsJSGlobalObjectMap() || |
| 596 | next->map()->is_hidden_prototype()); |
| 597 | |
| 598 | if (!check_prototype_chain() && |
| 599 | !(check_hidden() && next->map()->is_hidden_prototype()) && |
| 600 | // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even |
| 601 | // when not checking other hidden prototypes. |
| 602 | !map->IsJSGlobalProxyMap()) { |
| 603 | return NULL; |
| 604 | } |
| 605 | |
| 606 | return next; |
| 607 | } |
| 608 | |
| 609 | |
| 610 | LookupIterator::State LookupIterator::LookupInHolder(Map* const map, |
| 611 | JSReceiver* const holder) { |
| 612 | STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); |
| 613 | DisallowHeapAllocation no_gc; |
| 614 | if (interceptor_state_ == InterceptorState::kProcessNonMasking) { |
| 615 | return LookupNonMaskingInterceptorInHolder(map, holder); |
| 616 | } |
| 617 | switch (state_) { |
| 618 | case NOT_FOUND: |
| 619 | if (map->IsJSProxyMap()) { |
| 620 | // Do not leak private property names. |
| 621 | if (IsElement() || !name_->IsPrivate()) return JSPROXY; |
| 622 | } |
| 623 | if (map->is_access_check_needed() && |
| 624 | (IsElement() || !isolate_->IsInternallyUsedPropertyName(name_))) { |
| 625 | return ACCESS_CHECK; |
| 626 | } |
| 627 | // Fall through. |
| 628 | case ACCESS_CHECK: |
| 629 | if (exotic_index_state_ != ExoticIndexState::kNotExotic && |
| 630 | holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) { |
| 631 | return INTEGER_INDEXED_EXOTIC; |
| 632 | } |
| 633 | if (check_interceptor() && HasInterceptor(map) && |
| 634 | !SkipInterceptor(JSObject::cast(holder))) { |
| 635 | // Do not leak private property names. |
| 636 | if (!name_.is_null() && name_->IsPrivate()) return NOT_FOUND; |
| 637 | return INTERCEPTOR; |
| 638 | } |
| 639 | // Fall through. |
| 640 | case INTERCEPTOR: |
| 641 | if (IsElement()) { |
| 642 | // TODO(verwaest): Optimize. |
| 643 | if (holder->IsStringObjectWithCharacterAt(index_)) { |
| 644 | PropertyAttributes attributes = |
| 645 | static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); |
| 646 | property_details_ = PropertyDetails(attributes, v8::internal::DATA, 0, |
| 647 | PropertyCellType::kNoCell); |
| 648 | } else { |
| 649 | JSObject* js_object = JSObject::cast(holder); |
| 650 | if (js_object->elements() == isolate()->heap()->empty_fixed_array()) { |
| 651 | return NOT_FOUND; |
| 652 | } |
| 653 | |
| 654 | ElementsAccessor* accessor = js_object->GetElementsAccessor(); |
| 655 | FixedArrayBase* backing_store = js_object->elements(); |
| 656 | number_ = |
| 657 | accessor->GetEntryForIndex(js_object, backing_store, index_); |
| 658 | if (number_ == kMaxUInt32) return NOT_FOUND; |
| 659 | property_details_ = accessor->GetDetails(backing_store, number_); |
| 660 | } |
| 661 | } else if (!map->is_dictionary_map()) { |
| 662 | DescriptorArray* descriptors = map->instance_descriptors(); |
| 663 | int number = descriptors->SearchWithCache(*name_, map); |
| 664 | if (number == DescriptorArray::kNotFound) return NOT_FOUND; |
| 665 | number_ = static_cast<uint32_t>(number); |
| 666 | property_details_ = descriptors->GetDetails(number_); |
| 667 | } else if (map->IsJSGlobalObjectMap()) { |
| 668 | GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); |
| 669 | int number = dict->FindEntry(name_); |
| 670 | if (number == GlobalDictionary::kNotFound) return NOT_FOUND; |
| 671 | number_ = static_cast<uint32_t>(number); |
| 672 | DCHECK(dict->ValueAt(number_)->IsPropertyCell()); |
| 673 | PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); |
| 674 | if (cell->value()->IsTheHole()) return NOT_FOUND; |
| 675 | property_details_ = cell->property_details(); |
| 676 | } else { |
| 677 | NameDictionary* dict = holder->property_dictionary(); |
| 678 | int number = dict->FindEntry(name_); |
| 679 | if (number == NameDictionary::kNotFound) return NOT_FOUND; |
| 680 | number_ = static_cast<uint32_t>(number); |
| 681 | property_details_ = dict->DetailsAt(number_); |
| 682 | } |
| 683 | has_property_ = true; |
| 684 | switch (property_details_.kind()) { |
| 685 | case v8::internal::kData: |
| 686 | return DATA; |
| 687 | case v8::internal::kAccessor: |
| 688 | return ACCESSOR; |
| 689 | } |
| 690 | case ACCESSOR: |
| 691 | case DATA: |
| 692 | return NOT_FOUND; |
| 693 | case INTEGER_INDEXED_EXOTIC: |
| 694 | case JSPROXY: |
| 695 | case TRANSITION: |
| 696 | UNREACHABLE(); |
| 697 | } |
| 698 | UNREACHABLE(); |
| 699 | return state_; |
| 700 | } |
| 701 | |
| 702 | |
| 703 | LookupIterator::State LookupIterator::LookupNonMaskingInterceptorInHolder( |
| 704 | Map* const map, JSReceiver* const holder) { |
| 705 | switch (state_) { |
| 706 | case NOT_FOUND: |
| 707 | if (check_interceptor() && HasInterceptor(map) && |
| 708 | !SkipInterceptor(JSObject::cast(holder))) { |
| 709 | return INTERCEPTOR; |
| 710 | } |
| 711 | // Fall through. |
| 712 | default: |
| 713 | return NOT_FOUND; |
| 714 | } |
| 715 | UNREACHABLE(); |
| 716 | return state_; |
| 717 | } |
| 718 | |
| 719 | } // namespace internal |
| 720 | } // namespace v8 |