lookup.cc 12.8 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 91 92 93 94
bool LookupIterator::IsBootstrapping() const {
  return isolate_->bootstrapper()->IsActive();
}


95
bool LookupIterator::HasAccess() const {
96
  DCHECK_EQ(ACCESS_CHECK, state_);
97
  return isolate_->MayAccess(GetHolder<JSObject>());
98 99 100
}


101 102
void LookupIterator::ReloadPropertyInformation() {
  state_ = BEFORE_PROPERTY;
103
  interceptor_state_ = InterceptorState::kUninitialized;
104
  state_ = LookupInHolder(*holder_map_, *holder_);
105
  DCHECK(IsFound() || holder_map_->is_dictionary_map());
106 107 108
}


109
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
110
  DCHECK(state_ == DATA || state_ == ACCESSOR);
111
  DCHECK(HolderIsReceiverOrHiddenPrototype());
112
  if (holder_map_->is_dictionary_map()) return;
113 114
  holder_map_ =
      Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
115
  JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
116
  ReloadPropertyInformation();
117 118 119
}


120 121
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
122
  DCHECK(state_ == DATA || state_ == ACCESSOR);
123 124 125
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
126 127
    PropertyDetails details(attributes, v8::internal::DATA, 0,
                            PropertyCellType::kMutable);
128
    JSObject::SetNormalizedProperty(holder, name(), value, details);
129
  } else {
130 131 132 133
    holder_map_ = Map::ReconfigureExistingProperty(
        holder_map_, descriptor_number(), i::kData, attributes);
    holder_map_ =
        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
134
    JSObject::MigrateToMap(holder, holder_map_);
135 136
  }

137
  ReloadPropertyInformation();
138 139 140
}


141
void LookupIterator::PrepareTransitionToDataProperty(
142 143
    Handle<Object> value, PropertyAttributes attributes,
    Object::StoreFromKeyed store_mode) {
144
  if (state_ == TRANSITION) return;
145 146
  DCHECK_NE(LookupIterator::ACCESSOR, state_);
  DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, state_);
147
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
148 149 150
  // 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.
151
  Handle<JSObject> receiver = GetStoreTarget();
152

153
  if (!isolate()->IsInternallyUsedPropertyName(name()) &&
154 155
      !receiver->map()->is_extensible()) {
    return;
156 157
  }

158
  auto transition = Map::TransitionToDataProperty(
159
      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
160
  state_ = TRANSITION;
161 162 163 164 165 166 167 168 169 170 171 172 173
  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;
  } else if (transition->GetBackPointer()->IsMap()) {
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
174 175 176 177 178 179 180
}


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

  Handle<JSObject> receiver = GetStoreTarget();
181
  if (receiver->IsGlobalObject()) return;
182
  holder_ = receiver;
183
  holder_map_ = transition_map();
184
  JSObject::MigrateToMap(receiver, holder_map_);
185 186 187 188 189 190 191 192 193 194 195
  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.
196
  Handle<JSObject> receiver = GetStoreTarget();
197
  holder_ = receiver;
198 199 200
  holder_map_ =
      Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
                                        name_, component, accessor, attributes);
201 202 203 204 205 206 207 208
  JSObject::MigrateToMap(receiver, holder_map_);

  ReloadPropertyInformation();

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


  // Install the accessor into the dictionary-mode object.
209 210
  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
                          PropertyCellType::kMutable);
211
  Handle<AccessorPair> pair;
212
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    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);
228
  holder_map_ = handle(receiver->map(), isolate_);
229
  ReloadPropertyInformation();
230 231 232
}


233
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
234
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
235
  // Optimization that only works if configuration_ is not mutable.
236
  if (!check_prototype_chain()) return true;
237
  DisallowHeapAllocation no_gc;
238 239 240
  if (!receiver_->IsJSReceiver()) return false;
  Object* current = *receiver_;
  JSReceiver* holder = *holder_;
241 242 243 244 245 246 247 248
  // 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;
249
    DCHECK(!current->IsJSProxy());
250 251 252 253 254 255
    iter.Advance();
  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
  return false;
}


256 257
Handle<Object> LookupIterator::FetchValue() const {
  Object* result = NULL;
258
  Handle<JSObject> holder = GetHolder<JSObject>();
259 260 261
  if (holder_map_->is_dictionary_map()) {
    result = holder->property_dictionary()->ValueAt(number_);
    if (holder_map_->IsGlobalObjectMap()) {
262
      DCHECK(result->IsPropertyCell());
263 264
      result = PropertyCell::cast(result)->value();
    }
265
  } else if (property_details_.type() == v8::internal::DATA) {
266 267 268 269 270
    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_);
271 272 273 274 275
  }
  return handle(result, isolate_);
}


276 277 278 279 280 281 282 283
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();
}


284 285
int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
286
  DCHECK(!holder_map_->is_dictionary_map());
287
  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
288 289 290 291
  return descriptor_number();
}


292
FieldIndex LookupIterator::GetFieldIndex() const {
293
  DCHECK(has_property_);
294
  DCHECK(!holder_map_->is_dictionary_map());
295
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
296
  int index =
297
      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
298
  bool is_double = representation().IsDouble();
299
  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
300 301 302
}


303 304
Handle<HeapType> LookupIterator::GetFieldType() const {
  DCHECK(has_property_);
305
  DCHECK(!holder_map_->is_dictionary_map());
306
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
307
  return handle(
308
      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
309 310 311 312
      isolate_);
}


313 314 315 316
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
  Handle<JSObject> holder = GetHolder<JSObject>();
  Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
  Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
317
  DCHECK(value->IsPropertyCell());
318
  return handle(PropertyCell::cast(value));
319 320 321
}


322
Handle<Object> LookupIterator::GetAccessors() const {
323
  DCHECK_EQ(ACCESSOR, state_);
324 325 326 327 328
  return FetchValue();
}


Handle<Object> LookupIterator::GetDataValue() const {
329
  DCHECK_EQ(DATA, state_);
330 331 332 333 334
  Handle<Object> value = FetchValue();
  return value;
}


335
void LookupIterator::WriteDataValue(Handle<Object> value) {
336
  DCHECK_EQ(DATA, state_);
337
  Handle<JSObject> holder = GetHolder<JSObject>();
338
  if (holder_map_->is_dictionary_map()) {
339 340
    Handle<NameDictionary> property_dictionary =
        handle(holder->property_dictionary());
341
    if (holder->IsGlobalObject()) {
342 343
      PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
                               property_details_);
344
    } else {
345
      property_dictionary->ValueAtPut(dictionary_entry(), *value);
346
    }
347
  } else if (property_details_.type() == v8::internal::DATA) {
348
    holder->WriteToField(descriptor_number(), *value);
349
  } else {
350
    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
351 352 353 354
  }
}


355 356 357 358 359 360 361 362 363
bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
  DCHECK(exotic_index_state_ != ExoticIndexState::kNoIndex);
  // Currently typed arrays are the only such objects.
  if (!holder->IsJSTypedArray()) return false;
  if (exotic_index_state_ == ExoticIndexState::kIndex) return true;
  DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
  bool result = false;
  // Compute and cache result.
  if (name()->IsString()) {
364
    Handle<String> name_string = Handle<String>::cast(name());
365
    if (name_string->length() != 0) {
dcarney's avatar
dcarney committed
366
      result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
367 368
    }
  }
369 370 371
  exotic_index_state_ =
      result ? ExoticIndexState::kIndex : ExoticIndexState::kNoIndex;
  return result;
372 373 374
}


375 376 377 378
void LookupIterator::InternalizeName() {
  if (name_->IsUniqueName()) return;
  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396


bool LookupIterator::SkipInterceptor(JSObject* holder) {
  auto info = holder->GetNamedInterceptor();
  // 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;
}
397
} }  // namespace v8::internal