lookup.cc 16 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/v8.h"

#include "src/bootstrapper.h"
8
#include "src/deoptimizer.h"
9
#include "src/lookup.h"
10
#include "src/lookup-inl.h"
11 12 13 14 15 16

namespace v8 {
namespace internal {


void LookupIterator::Next() {
17 18
  DCHECK_NE(JSPROXY, state_);
  DCHECK_NE(TRANSITION, state_);
19
  DisallowHeapAllocation no_gc;
20
  has_property_ = false;
21

22
  JSReceiver* holder = *holder_;
23 24 25
  Map* map = *holder_map_;

  // Perform lookup on current holder.
26 27
  state_ = LookupInHolder(map, holder);
  if (IsFound()) return;
28 29

  // Continue lookup if lookup on current holder failed.
30
  do {
31
    JSReceiver* maybe_holder = NextHolder(map);
32 33 34 35 36 37 38
    if (maybe_holder == nullptr) {
      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
        RestartLookupForNonMaskingInterceptors();
        return;
      }
      break;
    }
39 40
    holder = maybe_holder;
    map = holder->map();
41 42
    state_ = LookupInHolder(map, holder);
  } while (!IsFound());
43

44 45 46 47
  if (holder != *holder_) {
    holder_ = handle(holder, isolate_);
    holder_map_ = handle(map, isolate_);
  }
48 49 50
}


51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
void LookupIterator::RestartLookupForNonMaskingInterceptors() {
  interceptor_state_ = InterceptorState::kProcessNonMasking;
  state_ = NOT_FOUND;
  property_details_ = PropertyDetails::Empty();
  number_ = DescriptorArray::kNotFound;
  holder_ = initial_holder_;
  holder_map_ = handle(holder_->map(), isolate_);
  Next();
}


Handle<JSReceiver> LookupIterator::GetRoot(Handle<Object> receiver,
                                           Isolate* isolate) {
  if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
  auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
66 67 68 69
  if (root->IsNull()) {
    unsigned int magic = 0xbbbbbbbb;
    isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
  }
70
  return Handle<JSReceiver>::cast(root);
71 72 73 74
}


Handle<Map> LookupIterator::GetReceiverMap() const {
75 76
  if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
77 78 79
}


80
Handle<JSObject> LookupIterator::GetStoreTarget() const {
81 82 83
  if (receiver_->IsJSGlobalProxy()) {
    PrototypeIterator iter(isolate(), receiver_);
    if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
84 85
    return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
  }
86
  return Handle<JSObject>::cast(receiver_);
87 88 89
}


90
bool LookupIterator::HasAccess() const {
91
  DCHECK_EQ(ACCESS_CHECK, state_);
92
  return isolate_->MayAccess(GetHolder<JSObject>());
93 94 95
}


96 97
void LookupIterator::ReloadPropertyInformation() {
  state_ = BEFORE_PROPERTY;
98
  interceptor_state_ = InterceptorState::kUninitialized;
99
  state_ = LookupInHolder(*holder_map_, *holder_);
100
  DCHECK(IsFound() || holder_map_->is_dictionary_map());
101 102 103
}


104
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
105
  DCHECK(state_ == DATA || state_ == ACCESSOR);
106
  DCHECK(HolderIsReceiverOrHiddenPrototype());
107 108 109 110 111 112 113 114 115 116 117 118

  Handle<JSObject> holder = GetHolder<JSObject>();

  if (IsElement()) {
    ElementsKind old_kind = holder_map_->elements_kind();
    holder_map_ = Map::PrepareForDataElement(holder_map_, value);
    ElementsKind new_kind = holder_map_->elements_kind();
    if (new_kind != old_kind) {
      // TODO(verwaest): Handle element migration in MigrateToMap.
      JSObject::UpdateAllocationSite(holder, new_kind);
      if (IsFastDoubleElementsKind(old_kind) !=
          IsFastDoubleElementsKind(new_kind)) {
119
        uint32_t capacity = holder->elements()->length();
120
        ElementsAccessor* accessor = ElementsAccessor::ForKind(new_kind);
121 122
        accessor->GrowCapacityAndConvert(holder, capacity);
        // GrowCapacityAndConvert migrated the object. No reloading of property
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
        // infomation is necessary for elements.
        return;
      } else if (FLAG_trace_elements_transitions) {
        Handle<FixedArrayBase> elements(holder->elements());
        JSObject::PrintElementsTransition(stdout, holder, old_kind, elements,
                                          new_kind, elements);
      }
    }

    // Copy the backing store if it is copy-on-write.
    if (IsFastSmiOrObjectElementsKind(new_kind)) {
      JSObject::EnsureWritableFastElements(holder);
    }

  } else {
    if (holder_map_->is_dictionary_map()) return;
    holder_map_ =
        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
  }

  JSObject::MigrateToMap(holder, holder_map_);
144
  ReloadPropertyInformation();
145 146 147
}


148 149
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
150
  DCHECK(state_ == DATA || state_ == ACCESSOR);
151 152
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  Handle<JSObject> holder = GetHolder<JSObject>();
153
  if (IsElement()) {
154 155
    DCHECK(!holder->HasExternalArrayElements());
    DCHECK(!holder->HasFixedTypedArrayElements());
156
    DCHECK(attributes != NONE || !holder->HasFastElements());
157 158 159
    Handle<FixedArrayBase> elements(holder->elements());
    holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
                                               attributes);
160
  } else if (holder_map_->is_dictionary_map()) {
161 162
    PropertyDetails details(attributes, v8::internal::DATA, 0,
                            PropertyCellType::kMutable);
163
    JSObject::SetNormalizedProperty(holder, name(), value, details);
164
  } else {
165 166 167 168
    holder_map_ = Map::ReconfigureExistingProperty(
        holder_map_, descriptor_number(), i::kData, attributes);
    holder_map_ =
        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
169
    JSObject::MigrateToMap(holder, holder_map_);
170 171
  }

172
  ReloadPropertyInformation();
173 174 175
}


176
void LookupIterator::PrepareTransitionToDataProperty(
177 178
    Handle<Object> value, PropertyAttributes attributes,
    Object::StoreFromKeyed store_mode) {
179
  if (state_ == TRANSITION) return;
180 181 182
  DCHECK(state_ != LookupIterator::ACCESSOR ||
         (GetAccessors()->IsAccessorInfo() &&
          AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
183
  DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
184
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
185 186 187
  // Can only be called when the receiver is a JSObject. JSProxy has to be
  // handled via a trap. Adding properties to primitive values is not
  // observable.
188
  Handle<JSObject> receiver = GetStoreTarget();
189

190
  if (!isolate()->IsInternallyUsedPropertyName(name()) &&
191 192
      !receiver->map()->is_extensible()) {
    return;
193 194
  }

195
  auto transition = Map::TransitionToDataProperty(
196
      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
197
  state_ = TRANSITION;
198 199 200 201 202 203 204 205 206
  transition_ = transition;

  if (receiver->IsGlobalObject()) {
    // Install a property cell.
    InternalizeName();
    auto cell = GlobalObject::EnsurePropertyCell(
        Handle<GlobalObject>::cast(receiver), name());
    DCHECK(cell->value()->IsTheHole());
    transition_ = cell;
207
  } else if (!transition->is_dictionary_map()) {
208 209 210
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
211 212 213 214 215 216 217
}


void LookupIterator::ApplyTransitionToDataProperty() {
  DCHECK_EQ(TRANSITION, state_);

  Handle<JSObject> receiver = GetStoreTarget();
218
  if (receiver->IsGlobalObject()) return;
219
  holder_ = receiver;
220
  holder_map_ = transition_map();
221
  JSObject::MigrateToMap(receiver, holder_map_);
222 223 224 225 226 227 228 229 230 231 232
  ReloadPropertyInformation();
}


void LookupIterator::TransitionToAccessorProperty(
    AccessorComponent component, Handle<Object> accessor,
    PropertyAttributes attributes) {
  DCHECK(!accessor->IsNull());
  // Can only be called when the receiver is a JSObject. JSProxy has to be
  // handled via a trap. Adding properties to primitive values is not
  // observable.
233
  Handle<JSObject> receiver = GetStoreTarget();
234
  holder_ = receiver;
235 236 237
  holder_map_ =
      Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
                                        name_, component, accessor, attributes);
238 239 240 241 242 243 244 245
  JSObject::MigrateToMap(receiver, holder_map_);

  ReloadPropertyInformation();

  if (!holder_map_->is_dictionary_map()) return;


  // Install the accessor into the dictionary-mode object.
246 247
  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
                          PropertyCellType::kMutable);
248
  Handle<AccessorPair> pair;
249
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    pair = Handle<AccessorPair>::cast(GetAccessors());
    // If the component and attributes are identical, nothing has to be done.
    if (pair->get(component) == *accessor) {
      if (property_details().attributes() == attributes) return;
    } else {
      pair = AccessorPair::Copy(pair);
      pair->set(component, *accessor);
    }
  } else {
    pair = isolate()->factory()->NewAccessorPair();
    pair->set(component, *accessor);
  }
  JSObject::SetNormalizedProperty(receiver, name_, pair, details);

  JSObject::ReoptimizeIfPrototype(receiver);
265
  holder_map_ = handle(receiver->map(), isolate_);
266
  ReloadPropertyInformation();
267 268 269
}


270
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
271
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
272 273 274 275
  return InternalHolderIsReceiverOrHiddenPrototype();
}

bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
276
  // Optimization that only works if configuration_ is not mutable.
277
  if (!check_prototype_chain()) return true;
278
  DisallowHeapAllocation no_gc;
279 280 281
  if (!receiver_->IsJSReceiver()) return false;
  Object* current = *receiver_;
  JSReceiver* holder = *holder_;
282 283 284 285 286 287 288 289
  // JSProxy do not occur as hidden prototypes.
  if (current->IsJSProxy()) {
    return JSReceiver::cast(current) == holder;
  }
  PrototypeIterator iter(isolate(), current,
                         PrototypeIterator::START_AT_RECEIVER);
  do {
    if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
290
    DCHECK(!current->IsJSProxy());
291 292 293 294 295 296
    iter.Advance();
  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
  return false;
}


297 298
Handle<Object> LookupIterator::FetchValue() const {
  Object* result = NULL;
299
  Handle<JSObject> holder = GetHolder<JSObject>();
300 301
  if (IsElement()) {
    // TODO(verwaest): Optimize.
302 303 304 305 306 307 308
    if (holder->IsStringObjectWithCharacterAt(index_)) {
      Handle<JSValue> js_value = Handle<JSValue>::cast(holder);
      Handle<String> string(String::cast(js_value->value()));
      return factory()->LookupSingleCharacterStringFromCode(
          String::Flatten(string)->Get(index_));
    }

309 310 311
    ElementsAccessor* accessor = holder->GetElementsAccessor();
    return accessor->Get(holder, index_);
  } else if (holder_map_->IsGlobalObjectMap()) {
312 313 314 315
    result = holder->global_dictionary()->ValueAt(number_);
    DCHECK(result->IsPropertyCell());
    result = PropertyCell::cast(result)->value();
  } else if (holder_map_->is_dictionary_map()) {
316
    result = holder->property_dictionary()->ValueAt(number_);
317
  } else if (property_details_.type() == v8::internal::DATA) {
318 319 320 321 322
    FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
    return JSObject::FastPropertyAt(holder, property_details_.representation(),
                                    field_index);
  } else {
    result = holder_map_->instance_descriptors()->GetValue(number_);
323 324 325 326 327
  }
  return handle(result, isolate_);
}


328 329 330 331 332 333 334 335
int LookupIterator::GetAccessorIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
  return descriptor_number();
}


336 337
int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
338
  DCHECK(!holder_map_->is_dictionary_map());
339
  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
340
  DCHECK(!IsElement());
341 342 343 344
  return descriptor_number();
}


345
FieldIndex LookupIterator::GetFieldIndex() const {
346
  DCHECK(has_property_);
347
  DCHECK(!holder_map_->is_dictionary_map());
348
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
349
  DCHECK(!IsElement());
350
  int index =
351
      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
352
  bool is_double = representation().IsDouble();
353
  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
354 355 356
}


357 358
Handle<HeapType> LookupIterator::GetFieldType() const {
  DCHECK(has_property_);
359
  DCHECK(!holder_map_->is_dictionary_map());
360
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
361
  return handle(
362
      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
363 364 365 366
      isolate_);
}


367
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
368
  DCHECK(!IsElement());
369 370
  Handle<JSObject> holder = GetHolder<JSObject>();
  Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
371
  Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
372
  DCHECK(value->IsPropertyCell());
373
  return handle(PropertyCell::cast(value));
374 375 376
}


377
Handle<Object> LookupIterator::GetAccessors() const {
378
  DCHECK_EQ(ACCESSOR, state_);
379 380 381 382 383
  return FetchValue();
}


Handle<Object> LookupIterator::GetDataValue() const {
384
  DCHECK_EQ(DATA, state_);
385 386 387 388 389
  Handle<Object> value = FetchValue();
  return value;
}


390
void LookupIterator::WriteDataValue(Handle<Object> value) {
391
  DCHECK_EQ(DATA, state_);
392
  Handle<JSObject> holder = GetHolder<JSObject>();
393 394
  if (IsElement()) {
    ElementsAccessor* accessor = holder->GetElementsAccessor();
395
    accessor->Set(holder->elements(), index_, *value);
396
  } else if (holder->IsGlobalObject()) {
397 398 399 400 401
    Handle<GlobalDictionary> property_dictionary =
        handle(holder->global_dictionary());
    PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
                             property_details_);
  } else if (holder_map_->is_dictionary_map()) {
402
    NameDictionary* property_dictionary = holder->property_dictionary();
403
    property_dictionary->ValueAtPut(dictionary_entry(), *value);
404
  } else if (property_details_.type() == v8::internal::DATA) {
405
    holder->WriteToField(descriptor_number(), *value);
406
  } else {
407
    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
408 409 410 411
  }
}


412
bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
413
  DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
414 415
  // Currently typed arrays are the only such objects.
  if (!holder->IsJSTypedArray()) return false;
416
  if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
417 418 419 420
  if (!InternalHolderIsReceiverOrHiddenPrototype()) {
    exotic_index_state_ = ExoticIndexState::kNotExotic;
    return false;
  }
421 422 423
  DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
  bool result = false;
  // Compute and cache result.
424 425 426
  if (IsElement()) {
    result = index_ >= JSTypedArray::cast(holder)->length_value();
  } else if (name()->IsString()) {
427
    Handle<String> name_string = Handle<String>::cast(name());
428
    if (name_string->length() != 0) {
dcarney's avatar
dcarney committed
429
      result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
430 431
    }
  }
432
  exotic_index_state_ =
433
      result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
434
  return result;
435 436 437
}


438 439 440 441
void LookupIterator::InternalizeName() {
  if (name_->IsUniqueName()) return;
  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}
442 443


444 445 446 447 448 449
bool LookupIterator::HasInterceptor(Map* map) const {
  if (IsElement()) return map->has_indexed_interceptor();
  return map->has_named_interceptor();
}


450 451
Handle<InterceptorInfo> LookupIterator::GetInterceptor() const {
  DCHECK_EQ(INTERCEPTOR, state_);
452 453 454 455 456 457 458
  return handle(GetInterceptor(JSObject::cast(*holder_)), isolate_);
}


InterceptorInfo* LookupIterator::GetInterceptor(JSObject* holder) const {
  if (IsElement()) return holder->GetIndexedInterceptor();
  return holder->GetNamedInterceptor();
459 460 461
}


462
bool LookupIterator::SkipInterceptor(JSObject* holder) {
463
  auto info = GetInterceptor(holder);
464 465 466 467 468 469 470 471 472 473 474 475 476 477
  // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
  if (info->non_masking()) {
    switch (interceptor_state_) {
      case InterceptorState::kUninitialized:
        interceptor_state_ = InterceptorState::kSkipNonMasking;
      // Fall through.
      case InterceptorState::kSkipNonMasking:
        return true;
      case InterceptorState::kProcessNonMasking:
        return false;
    }
  }
  return interceptor_state_ == InterceptorState::kProcessNonMasking;
}
478 479
}  // namespace internal
}  // namespace v8