js-objects-inl.h 30.5 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2018 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.

#ifndef V8_OBJECTS_JS_OBJECTS_INL_H_
#define V8_OBJECTS_JS_OBJECTS_INL_H_

8
#include "src/common/globals.h"
9
#include "src/heap/heap-write-barrier.h"
10
#include "src/objects/elements.h"
11
#include "src/objects/embedder-data-slot-inl.h"
12
#include "src/objects/feedback-vector.h"
13
#include "src/objects/field-index-inl.h"
14
#include "src/objects/hash-table-inl.h"
15
#include "src/objects/heap-number-inl.h"
16
#include "src/objects/js-objects.h"
17
#include "src/objects/keys.h"
18
#include "src/objects/lookup-inl.h"
19
#include "src/objects/property-array-inl.h"
20
#include "src/objects/prototype-inl.h"
21
#include "src/objects/shared-function-info.h"
22
#include "src/objects/slots.h"
23
#include "src/objects/smi-inl.h"
24
#include "src/objects/swiss-name-dictionary-inl.h"
25 26 27 28 29 30 31

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

32 33
#include "torque-generated/src/objects/js-objects-tq-inl.inc"

34
TQ_OBJECT_CONSTRUCTORS_IMPL(JSReceiver)
35
TQ_OBJECT_CONSTRUCTORS_IMPL(JSObject)
36 37
TQ_OBJECT_CONSTRUCTORS_IMPL(JSCustomElementsObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSSpecialObject)
38 39
TQ_OBJECT_CONSTRUCTORS_IMPL(JSAsyncFromSyncIterator)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSDate)
40
TQ_OBJECT_CONSTRUCTORS_IMPL(JSGlobalObject)
41
TQ_OBJECT_CONSTRUCTORS_IMPL(JSGlobalProxy)
42
JSIteratorResult::JSIteratorResult(Address ptr) : JSObject(ptr) {}
43
TQ_OBJECT_CONSTRUCTORS_IMPL(JSMessageObject)
44
TQ_OBJECT_CONSTRUCTORS_IMPL(JSPrimitiveWrapper)
45
TQ_OBJECT_CONSTRUCTORS_IMPL(JSStringIterator)
46 47 48

NEVER_READ_ONLY_SPACE_IMPL(JSReceiver)

49
CAST_ACCESSOR(JSIteratorResult)
50

51
DEF_GETTER(JSObject, elements, FixedArrayBase) {
52
  return TaggedField<FixedArrayBase, kElementsOffset>::load(cage_base, *this);
53 54
}

55 56 57 58 59
FixedArrayBase JSObject::elements(RelaxedLoadTag tag) const {
  PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
  return elements(cage_base, tag);
}

60 61 62
FixedArrayBase JSObject::elements(PtrComprCageBase cage_base,
                                  RelaxedLoadTag) const {
  return TaggedField<FixedArrayBase, kElementsOffset>::Relaxed_Load(cage_base,
63 64 65 66 67 68 69 70 71
                                                                    *this);
}

void JSObject::set_elements(FixedArrayBase value, WriteBarrierMode mode) {
  // Note the relaxed atomic store.
  TaggedField<FixedArrayBase, kElementsOffset>::Relaxed_Store(*this, value);
  CONDITIONAL_WRITE_BARRIER(*this, kElementsOffset, value, mode);
}

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate,
                                            Handle<JSReceiver> receiver,
                                            Handle<Name> name) {
  LookupIterator it(isolate, receiver, name, receiver);
  if (!it.IsFound()) return it.factory()->undefined_value();
  return Object::GetProperty(&it);
}

MaybeHandle<Object> JSReceiver::GetElement(Isolate* isolate,
                                           Handle<JSReceiver> receiver,
                                           uint32_t index) {
  LookupIterator it(isolate, receiver, index, receiver);
  if (!it.IsFound()) return it.factory()->undefined_value();
  return Object::GetProperty(&it);
}

Handle<Object> JSReceiver::GetDataProperty(Handle<JSReceiver> object,
                                           Handle<Name> name) {
90
  LookupIterator it(object->GetIsolate(), object, name, object,
91 92 93 94 95
                    LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
  if (!it.IsFound()) return it.factory()->undefined_value();
  return GetDataProperty(&it);
}

96 97
MaybeHandle<HeapObject> JSReceiver::GetPrototype(Isolate* isolate,
                                                 Handle<JSReceiver> receiver) {
98 99 100 101 102
  // We don't expect access checks to be needed on JSProxy objects.
  DCHECK(!receiver->IsAccessCheckNeeded() || receiver->IsJSObject());
  PrototypeIterator iter(isolate, receiver, kStartAtReceiver,
                         PrototypeIterator::END_AT_NON_HIDDEN);
  do {
103
    if (!iter.AdvanceFollowingProxies()) return MaybeHandle<HeapObject>();
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
  } while (!iter.IsAtEnd());
  return PrototypeIterator::GetCurrent(iter);
}

MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate,
                                            Handle<JSReceiver> receiver,
                                            const char* name) {
  Handle<String> str = isolate->factory()->InternalizeUtf8String(name);
  return GetProperty(isolate, receiver, str);
}

// static
V8_WARN_UNUSED_RESULT MaybeHandle<FixedArray> JSReceiver::OwnPropertyKeys(
    Handle<JSReceiver> object) {
  return KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
                                 ALL_PROPERTIES,
                                 GetKeysConversion::kConvertToString);
}

123
bool JSObject::PrototypeHasNoElements(Isolate* isolate, JSObject object) {
124
  DisallowGarbageCollection no_gc;
125
  HeapObject prototype = HeapObject::cast(object.map().prototype());
126
  ReadOnlyRoots roots(isolate);
127
  HeapObject null = roots.null_value();
128 129
  FixedArrayBase empty_fixed_array = roots.empty_fixed_array();
  FixedArrayBase empty_slow_element_dictionary =
130 131
      roots.empty_slow_element_dictionary();
  while (prototype != null) {
132 133 134
    Map map = prototype.map();
    if (map.IsCustomElementsReceiverMap()) return false;
    FixedArrayBase elements = JSObject::cast(prototype).elements();
135 136 137 138
    if (elements != empty_fixed_array &&
        elements != empty_slow_element_dictionary) {
      return false;
    }
139
    prototype = HeapObject::cast(map.prototype());
140 141 142 143 144
  }
  return true;
}

ACCESSORS(JSReceiver, raw_properties_or_hash, Object, kPropertiesOrHashOffset)
145 146
RELAXED_ACCESSORS(JSReceiver, raw_properties_or_hash, Object,
                  kPropertiesOrHashOffset)
147 148 149

void JSObject::EnsureCanContainHeapObjectElements(Handle<JSObject> object) {
  JSObject::ValidateElements(*object);
150
  ElementsKind elements_kind = object->map().elements_kind();
151 152 153 154 155 156 157 158 159
  if (!IsObjectElementsKind(elements_kind)) {
    if (IsHoleyElementsKind(elements_kind)) {
      TransitionElementsKind(object, HOLEY_ELEMENTS);
    } else {
      TransitionElementsKind(object, PACKED_ELEMENTS);
    }
  }
}

160 161 162
template <typename TSlot>
void JSObject::EnsureCanContainElements(Handle<JSObject> object, TSlot objects,
                                        uint32_t count,
163
                                        EnsureElementsMode mode) {
164 165 166
  static_assert(std::is_same<TSlot, FullObjectSlot>::value ||
                    std::is_same<TSlot, ObjectSlot>::value,
                "Only ObjectSlot and FullObjectSlot are expected here");
167 168 169
  ElementsKind current_kind = object->GetElementsKind();
  ElementsKind target_kind = current_kind;
  {
170
    DisallowGarbageCollection no_gc;
171 172 173
    DCHECK(mode != ALLOW_COPIED_DOUBLE_ELEMENTS);
    bool is_holey = IsHoleyElementsKind(current_kind);
    if (current_kind == HOLEY_ELEMENTS) return;
174
    Object the_hole = object->GetReadOnlyRoots().the_hole_value();
175
    for (uint32_t i = 0; i < count; ++i, ++objects) {
176
      Object current = *objects;
177 178 179
      if (current == the_hole) {
        is_holey = true;
        target_kind = GetHoleyElementsKind(target_kind);
180 181
      } else if (!current.IsSmi()) {
        if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && current.IsNumber()) {
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
          if (IsSmiElementsKind(target_kind)) {
            if (is_holey) {
              target_kind = HOLEY_DOUBLE_ELEMENTS;
            } else {
              target_kind = PACKED_DOUBLE_ELEMENTS;
            }
          }
        } else if (is_holey) {
          target_kind = HOLEY_ELEMENTS;
          break;
        } else {
          target_kind = PACKED_ELEMENTS;
        }
      }
    }
  }
  if (target_kind != current_kind) {
    TransitionElementsKind(object, target_kind);
  }
}

void JSObject::EnsureCanContainElements(Handle<JSObject> object,
                                        Handle<FixedArrayBase> elements,
                                        uint32_t length,
                                        EnsureElementsMode mode) {
  ReadOnlyRoots roots = object->GetReadOnlyRoots();
  if (elements->map() != roots.fixed_double_array_map()) {
    DCHECK(elements->map() == roots.fixed_array_map() ||
           elements->map() == roots.fixed_cow_array_map());
    if (mode == ALLOW_COPIED_DOUBLE_ELEMENTS) {
      mode = DONT_ALLOW_DOUBLE_ELEMENTS;
    }
214
    ObjectSlot objects =
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
        Handle<FixedArray>::cast(elements)->GetFirstElementAddress();
    EnsureCanContainElements(object, objects, length, mode);
    return;
  }

  DCHECK(mode == ALLOW_COPIED_DOUBLE_ELEMENTS);
  if (object->GetElementsKind() == HOLEY_SMI_ELEMENTS) {
    TransitionElementsKind(object, HOLEY_DOUBLE_ELEMENTS);
  } else if (object->GetElementsKind() == PACKED_SMI_ELEMENTS) {
    Handle<FixedDoubleArray> double_array =
        Handle<FixedDoubleArray>::cast(elements);
    for (uint32_t i = 0; i < length; ++i) {
      if (double_array->is_the_hole(i)) {
        TransitionElementsKind(object, HOLEY_DOUBLE_ELEMENTS);
        return;
      }
    }
    TransitionElementsKind(object, PACKED_DOUBLE_ELEMENTS);
  }
}

void JSObject::SetMapAndElements(Handle<JSObject> object, Handle<Map> new_map,
                                 Handle<FixedArrayBase> value) {
238 239
  Isolate* isolate = object->GetIsolate();
  JSObject::MigrateToMap(isolate, object, new_map);
240
  DCHECK((object->map().has_fast_smi_or_object_elements() ||
241
          (*value == ReadOnlyRoots(isolate).empty_fixed_array()) ||
242
          object->map().has_fast_string_wrapper_elements()) ==
243 244 245
         (value->map() == ReadOnlyRoots(isolate).fixed_array_map() ||
          value->map() == ReadOnlyRoots(isolate).fixed_cow_array_map()));
  DCHECK((*value == ReadOnlyRoots(isolate).empty_fixed_array()) ||
246
         (object->map().has_fast_double_elements() ==
247 248 249 250 251
          value->IsFixedDoubleArray()));
  object->set_elements(*value);
}

void JSObject::initialize_elements() {
252
  FixedArrayBase elements = map().GetInitialElements();
253
  set_elements(elements, SKIP_WRITE_BARRIER);
254 255
}

256
DEF_GETTER(JSObject, GetIndexedInterceptor, InterceptorInfo) {
257
  return map(cage_base).GetIndexedInterceptor(cage_base);
258 259
}

260
DEF_GETTER(JSObject, GetNamedInterceptor, InterceptorInfo) {
261
  return map(cage_base).GetNamedInterceptor(cage_base);
262 263
}

264 265
// static
int JSObject::GetHeaderSize(Map map) {
266 267 268
  // Check for the most common kind of JavaScript object before
  // falling into the generic switch. This speeds up the internal
  // field operations considerably on average.
269
  InstanceType instance_type = map.instance_type();
270 271
  return instance_type == JS_OBJECT_TYPE
             ? JSObject::kHeaderSize
272
             : GetHeaderSize(instance_type, map.has_prototype_slot());
273 274
}

275
// static
276
int JSObject::GetEmbedderFieldsStartOffset(Map map) {
277 278
  // Embedder fields are located after the object header.
  return GetHeaderSize(map);
279 280 281 282 283 284
}

int JSObject::GetEmbedderFieldsStartOffset() {
  return GetEmbedderFieldsStartOffset(map());
}

285
// static
286
int JSObject::GetEmbedderFieldCount(Map map) {
287
  int instance_size = map.instance_size();
288
  if (instance_size == kVariableSizeSentinel) return 0;
289 290 291 292 293 294 295
  // Embedder fields are located after the object header, whereas in-object
  // properties are located at the end of the object. We don't have to round up
  // the header size here because division by kEmbedderDataSlotSizeInTaggedSlots
  // will swallow potential padding in case of (kTaggedSize !=
  // kSystemPointerSize) anyway.
  return (((instance_size - GetEmbedderFieldsStartOffset(map)) >>
           kTaggedSizeLog2) -
296
          map.GetInObjectProperties()) /
297
         kEmbedderDataSlotSizeInTaggedSlots;
298 299 300 301 302 303 304
}

int JSObject::GetEmbedderFieldCount() const {
  return GetEmbedderFieldCount(map());
}

int JSObject::GetEmbedderFieldOffset(int index) {
305 306 307
  DCHECK_LT(static_cast<unsigned>(index),
            static_cast<unsigned>(GetEmbedderFieldCount()));
  return GetEmbedderFieldsStartOffset() + (kEmbedderDataSlotSize * index);
308 309
}

310 311 312 313
void JSObject::InitializeEmbedderField(Isolate* isolate, int index) {
  EmbedderDataSlot(*this, index).AllocateExternalPointerEntry(isolate);
}

314
Object JSObject::GetEmbedderField(int index) {
315
  return EmbedderDataSlot(*this, index).load_tagged();
316 317
}

318
void JSObject::SetEmbedderField(int index, Object value) {
319
  EmbedderDataSlot::store_tagged(*this, index, value);
320 321
}

322
void JSObject::SetEmbedderField(int index, Smi value) {
323
  EmbedderDataSlot(*this, index).store_smi(value);
324 325 326 327 328
}

// Access fast-case object properties at index. The use of these routines
// is needed to correctly distinguish between properties stored in-object and
// properties stored in the properties array.
329
Object JSObject::RawFastPropertyAt(FieldIndex index) const {
330 331
  PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
  return RawFastPropertyAt(cage_base, index);
332 333
}

334
Object JSObject::RawFastPropertyAt(PtrComprCageBase cage_base,
335
                                   FieldIndex index) const {
336
  if (index.is_inobject()) {
337
    return TaggedField<Object>::load(cage_base, *this, index.offset());
338
  } else {
339 340
    return property_array(cage_base).get(cage_base,
                                         index.outobject_array_index());
341 342 343
  }
}

344 345
base::Optional<Object> JSObject::RawInobjectPropertyAt(Map original_map,
                                                       FieldIndex index) const {
346 347
  PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
  CHECK(index.is_inobject());
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

  // This method implements a "snapshot" protocol to protect against reading out
  // of bounds of an object. It's used to access a fast in-object property from
  // a background thread with no locking. That caller does have the guarantee
  // that a garbage collection cannot happen during its query. However, it must
  // contend with the main thread altering the object in heavy ways through
  // object migration. Specifically, the object can get smaller. Initially, this
  // may seem benign, because object migration fills the freed-up space with
  // FillerMap words which, even though they offer wrong values, are at
  // least tagged values.

  // However, there is an additional danger. Sweeper threads may discover the
  // filler words and offer that space to the main thread for allocation. Should
  // a HeapNumber be allocated into that space while we're reading a property at
  // that location (from our out-of-date information), we risk interpreting a
  // double value as a pointer. This must be prevented.
  //
  // We do this by:
  //
  // a) Reading the map first
  // b) Reading the property with acquire semantics (but do not inspect it!)
  // c) Re-read the map with acquire semantics.
  //
  // Only if the maps match can the property be inspected. It may have a "wrong"
  // value, but it will be within the bounds of the objects instance size as
  // given by the map and it will be a valid Smi or object pointer.
  Object maybe_tagged_object =
      TaggedField<Object>::Acquire_Load(cage_base, *this, index.offset());
  if (original_map != map(kAcquireLoad)) return {};
  return maybe_tagged_object;
378 379
}

380 381 382 383
void JSObject::RawFastInobjectPropertyAtPut(FieldIndex index, Object value,
                                            WriteBarrierMode mode) {
  DCHECK(index.is_inobject());
  int offset = index.offset();
384
  RELAXED_WRITE_FIELD(*this, offset, value);
385 386 387
  CONDITIONAL_WRITE_BARRIER(*this, offset, value, mode);
}

388 389
void JSObject::FastPropertyAtPut(FieldIndex index, Object value,
                                 WriteBarrierMode mode) {
390
  if (index.is_inobject()) {
391
    RawFastInobjectPropertyAtPut(index, value, mode);
392
  } else {
393
    DCHECK_EQ(UPDATE_WRITE_BARRIER, mode);
394
    property_array().set(index.outobject_array_index(), value);
395 396 397
  }
}

398
void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details,
399
                            Object value) {
400
  DCHECK_EQ(PropertyLocation::kField, details.location());
401
  DCHECK_EQ(kData, details.kind());
402
  DisallowGarbageCollection no_gc;
403 404 405 406 407 408 409
  FieldIndex index = FieldIndex::ForDescriptor(map(), descriptor);
  if (details.representation().IsDouble()) {
    // Manipulating the signaling NaN used for the hole and uninitialized
    // double field sentinel in C++, e.g. with bit_cast or value()/set_value(),
    // will change its value on ia32 (the x87 stack is used to return values
    // and stores to the stack silently clear the signalling bit).
    uint64_t bits;
410
    if (value.IsSmi()) {
411
      bits = bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value)));
412 413
    } else if (value.IsUninitialized()) {
      bits = kHoleNanInt64;
414
    } else {
415
      DCHECK(value.IsHeapNumber());
416
      bits = HeapNumber::cast(value).value_as_bits(kRelaxedLoad);
417
    }
418
    auto box = HeapNumber::cast(RawFastPropertyAt(index));
419
    box.set_value_as_bits(bits, kRelaxedStore);
420
  } else {
421
    FastPropertyAtPut(index, value);
422 423 424 425
  }
}

int JSObject::GetInObjectPropertyOffset(int index) {
426
  return map().GetInObjectPropertyOffset(index);
427 428
}

429
Object JSObject::InObjectPropertyAt(int index) {
430
  int offset = GetInObjectPropertyOffset(index);
431
  return TaggedField<Object>::load(*this, offset);
432 433
}

434 435
Object JSObject::InObjectPropertyAtPut(int index, Object value,
                                       WriteBarrierMode mode) {
436 437
  // Adjust for the number of properties stored in the object.
  int offset = GetInObjectPropertyOffset(index);
438 439
  WRITE_FIELD(*this, offset, value);
  CONDITIONAL_WRITE_BARRIER(*this, offset, value, mode);
440 441 442
  return value;
}

443
void JSObject::InitializeBody(Map map, int start_offset,
444 445
                              bool is_slack_tracking_in_progress,
                              MapWord filler_map, Object undefined_filler) {
446
  int size = map.instance_size();
447
  int offset = start_offset;
448
  if (is_slack_tracking_in_progress) {
449
    int end_of_pre_allocated_offset =
450
        size - (map.UnusedPropertyFields() * kTaggedSize);
451
    DCHECK_LE(kHeaderSize, end_of_pre_allocated_offset);
452
    // fill start with references to the undefined value object
453
    while (offset < end_of_pre_allocated_offset) {
454 455 456 457 458 459 460 461 462 463 464 465 466
      WRITE_FIELD(*this, offset, undefined_filler);
      offset += kTaggedSize;
    }
    // fill the remainder with one word filler objects (ie just a map word)
    while (offset < size) {
      Object fm = Object(filler_map.ptr());
      WRITE_FIELD(*this, offset, fm);
      offset += kTaggedSize;
    }
  } else {
    while (offset < size) {
      // fill with references to the undefined value object
      WRITE_FIELD(*this, offset, undefined_filler);
467
      offset += kTaggedSize;
468 469 470 471
    }
  }
}

472
DEF_GETTER(JSGlobalObject, native_context_unchecked, Object) {
473
  return TaggedField<Object, kNativeContextOffset>::load(cage_base, *this);
474 475
}

476
bool JSMessageObject::DidEnsureSourcePositionsAvailable() const {
477
  return shared_info().IsUndefined();
478 479 480 481 482 483 484 485 486 487 488 489
}

int JSMessageObject::GetStartPosition() const {
  DCHECK(DidEnsureSourcePositionsAvailable());
  return start_position();
}

int JSMessageObject::GetEndPosition() const {
  DCHECK(DidEnsureSourcePositionsAvailable());
  return end_position();
}

490
MessageTemplate JSMessageObject::type() const {
491
  return MessageTemplateFromInt(raw_type());
492
}
493

494
void JSMessageObject::set_type(MessageTemplate value) {
495
  set_raw_type(static_cast<int>(value));
496
}
497 498 499

ACCESSORS(JSMessageObject, shared_info, HeapObject, kSharedInfoOffset)
ACCESSORS(JSMessageObject, bytecode_offset, Smi, kBytecodeOffsetOffset)
500 501 502
SMI_ACCESSORS(JSMessageObject, start_position, kStartPositionOffset)
SMI_ACCESSORS(JSMessageObject, end_position, kEndPositionOffset)
SMI_ACCESSORS(JSMessageObject, error_level, kErrorLevelOffset)
503
SMI_ACCESSORS(JSMessageObject, raw_type, kMessageTypeOffset)
504

505
DEF_GETTER(JSObject, GetElementsKind, ElementsKind) {
506
  ElementsKind kind = map(cage_base).elements_kind();
507
#if VERIFY_HEAP && DEBUG
508
  FixedArrayBase fixed_array = FixedArrayBase::unchecked_cast(
509
      TaggedField<HeapObject, kElementsOffset>::load(cage_base, *this));
510 511 512

  // If a GC was caused while constructing this object, the elements
  // pointer may point to a one pointer filler map.
513 514
  if (ElementsAreSafeToExamine(cage_base)) {
    Map map = fixed_array.map(cage_base);
515
    if (IsSmiOrObjectElementsKind(kind)) {
516 517
      DCHECK(map == GetReadOnlyRoots(cage_base).fixed_array_map() ||
             map == GetReadOnlyRoots(cage_base).fixed_cow_array_map());
518
    } else if (IsDoubleElementsKind(kind)) {
519 520
      DCHECK(fixed_array.IsFixedDoubleArray(cage_base) ||
             fixed_array == GetReadOnlyRoots(cage_base).empty_fixed_array());
521
    } else if (kind == DICTIONARY_ELEMENTS) {
522 523
      DCHECK(fixed_array.IsFixedArray(cage_base));
      DCHECK(fixed_array.IsNumberDictionary(cage_base));
524
    } else {
525 526
      DCHECK(kind > DICTIONARY_ELEMENTS ||
             IsAnyNonextensibleElementsKind(kind));
527
    }
528
    DCHECK(!IsSloppyArgumentsElementsKind(kind) ||
529
           elements(cage_base).IsSloppyArgumentsElements());
530 531 532 533 534
  }
#endif
  return kind;
}

535
DEF_GETTER(JSObject, GetElementsAccessor, ElementsAccessor*) {
536
  return ElementsAccessor::ForKind(GetElementsKind(cage_base));
537 538 539
}

DEF_GETTER(JSObject, HasObjectElements, bool) {
540
  return IsObjectElementsKind(GetElementsKind(cage_base));
541 542
}

543
DEF_GETTER(JSObject, HasSmiElements, bool) {
544
  return IsSmiElementsKind(GetElementsKind(cage_base));
545
}
546

547
DEF_GETTER(JSObject, HasSmiOrObjectElements, bool) {
548
  return IsSmiOrObjectElementsKind(GetElementsKind(cage_base));
549 550
}

551
DEF_GETTER(JSObject, HasDoubleElements, bool) {
552
  return IsDoubleElementsKind(GetElementsKind(cage_base));
553 554
}

555
DEF_GETTER(JSObject, HasHoleyElements, bool) {
556
  return IsHoleyElementsKind(GetElementsKind(cage_base));
557 558
}

559
DEF_GETTER(JSObject, HasFastElements, bool) {
560
  return IsFastElementsKind(GetElementsKind(cage_base));
561 562
}

563
DEF_GETTER(JSObject, HasFastPackedElements, bool) {
564
  return IsFastPackedElementsKind(GetElementsKind(cage_base));
565 566
}

567
DEF_GETTER(JSObject, HasDictionaryElements, bool) {
568
  return IsDictionaryElementsKind(GetElementsKind(cage_base));
569 570
}

571
DEF_GETTER(JSObject, HasPackedElements, bool) {
572
  return GetElementsKind(cage_base) == PACKED_ELEMENTS;
573 574
}

575
DEF_GETTER(JSObject, HasAnyNonextensibleElements, bool) {
576
  return IsAnyNonextensibleElementsKind(GetElementsKind(cage_base));
577 578
}

579
DEF_GETTER(JSObject, HasSealedElements, bool) {
580
  return IsSealedElementsKind(GetElementsKind(cage_base));
581 582
}

583
DEF_GETTER(JSObject, HasNonextensibleElements, bool) {
584
  return IsNonextensibleElementsKind(GetElementsKind(cage_base));
585 586
}

587
DEF_GETTER(JSObject, HasFastArgumentsElements, bool) {
588
  return IsFastArgumentsElementsKind(GetElementsKind(cage_base));
589 590
}

591
DEF_GETTER(JSObject, HasSlowArgumentsElements, bool) {
592
  return IsSlowArgumentsElementsKind(GetElementsKind(cage_base));
593 594
}

595
DEF_GETTER(JSObject, HasSloppyArgumentsElements, bool) {
596
  return IsSloppyArgumentsElementsKind(GetElementsKind(cage_base));
597 598
}

599
DEF_GETTER(JSObject, HasStringWrapperElements, bool) {
600
  return IsStringWrapperElementsKind(GetElementsKind(cage_base));
601 602
}

603
DEF_GETTER(JSObject, HasFastStringWrapperElements, bool) {
604
  return GetElementsKind(cage_base) == FAST_STRING_WRAPPER_ELEMENTS;
605 606
}

607
DEF_GETTER(JSObject, HasSlowStringWrapperElements, bool) {
608
  return GetElementsKind(cage_base) == SLOW_STRING_WRAPPER_ELEMENTS;
609 610
}

611
DEF_GETTER(JSObject, HasTypedArrayElements, bool) {
612 613
  DCHECK(!elements(cage_base).is_null());
  return map(cage_base).has_typed_array_elements();
614 615
}

616 617 618 619 620
DEF_GETTER(JSObject, HasTypedArrayOrRabGsabTypedArrayElements, bool) {
  DCHECK(!elements(cage_base).is_null());
  return map(cage_base).has_typed_array_or_rab_gsab_typed_array_elements();
}

621 622 623
#define FIXED_TYPED_ELEMENTS_CHECK(Type, type, TYPE, ctype)   \
  DEF_GETTER(JSObject, HasFixed##Type##Elements, bool) {      \
    return map(cage_base).elements_kind() == TYPE##_ELEMENTS; \
624 625 626 627 628 629
  }

TYPED_ARRAYS(FIXED_TYPED_ELEMENTS_CHECK)

#undef FIXED_TYPED_ELEMENTS_CHECK

630
DEF_GETTER(JSObject, HasNamedInterceptor, bool) {
631
  return map(cage_base).has_named_interceptor();
632
}
633

634
DEF_GETTER(JSObject, HasIndexedInterceptor, bool) {
635
  return map(cage_base).has_indexed_interceptor();
636 637
}

638 639
RELEASE_ACQUIRE_ACCESSORS_CHECKED2(JSGlobalObject, global_dictionary,
                                   GlobalDictionary, kPropertiesOrHashOffset,
640
                                   !HasFastProperties(cage_base), true)
641

642
DEF_GETTER(JSObject, element_dictionary, NumberDictionary) {
643 644 645
  DCHECK(HasDictionaryElements(cage_base) ||
         HasSlowStringWrapperElements(cage_base));
  return NumberDictionary::cast(elements(cage_base));
646 647
}

648 649
void JSReceiver::initialize_properties(Isolate* isolate) {
  ReadOnlyRoots roots(isolate);
650 651
  DCHECK(!ObjectInYoungGeneration(roots.empty_fixed_array()));
  DCHECK(!ObjectInYoungGeneration(roots.empty_property_dictionary()));
652
  DCHECK(!ObjectInYoungGeneration(roots.empty_ordered_property_dictionary()));
653
  if (map(isolate).is_dictionary_map()) {
654
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
655
      WRITE_FIELD(*this, kPropertiesOrHashOffset,
656
                  roots.empty_swiss_property_dictionary());
657 658 659 660
    } else {
      WRITE_FIELD(*this, kPropertiesOrHashOffset,
                  roots.empty_property_dictionary());
    }
661
  } else {
662
    WRITE_FIELD(*this, kPropertiesOrHashOffset, roots.empty_fixed_array());
663 664 665
  }
}

666
DEF_GETTER(JSReceiver, HasFastProperties, bool) {
667 668 669 670 671 672
  DCHECK(raw_properties_or_hash(cage_base).IsSmi() ||
         ((raw_properties_or_hash(cage_base).IsGlobalDictionary(cage_base) ||
           raw_properties_or_hash(cage_base).IsNameDictionary(cage_base) ||
           raw_properties_or_hash(cage_base).IsSwissNameDictionary(
               cage_base)) == map(cage_base).is_dictionary_map()));
  return !map(cage_base).is_dictionary_map();
673 674
}

675
DEF_GETTER(JSReceiver, property_dictionary, NameDictionary) {
676 677
  DCHECK(!IsJSGlobalObject(cage_base));
  DCHECK(!HasFastProperties(cage_base));
678
  DCHECK(!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL);
679

680
  Object prop = raw_properties_or_hash(cage_base);
681
  if (prop.IsSmi()) {
682
    return GetReadOnlyRoots(cage_base).empty_property_dictionary();
683 684 685 686
  }
  return NameDictionary::cast(prop);
}

687
DEF_GETTER(JSReceiver, property_dictionary_swiss, SwissNameDictionary) {
688 689
  DCHECK(!IsJSGlobalObject(cage_base));
  DCHECK(!HasFastProperties(cage_base));
690
  DCHECK(V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL);
691

692
  Object prop = raw_properties_or_hash(cage_base);
693
  if (prop.IsSmi()) {
694
    return GetReadOnlyRoots(cage_base).empty_swiss_property_dictionary();
695 696 697 698
  }
  return SwissNameDictionary::cast(prop);
}

699 700
// TODO(gsathya): Pass isolate directly to this function and access
// the heap from this.
701
DEF_GETTER(JSReceiver, property_array, PropertyArray) {
702 703 704 705
  DCHECK(HasFastProperties(cage_base));
  Object prop = raw_properties_or_hash(cage_base);
  if (prop.IsSmi() || prop == GetReadOnlyRoots(cage_base).empty_fixed_array()) {
    return GetReadOnlyRoots(cage_base).empty_property_array();
706 707 708 709 710 711
  }
  return PropertyArray::cast(prop);
}

Maybe<bool> JSReceiver::HasProperty(Handle<JSReceiver> object,
                                    Handle<Name> name) {
712
  Isolate* isolate = object->GetIsolate();
713
  PropertyKey key(isolate, name);
714
  LookupIterator it(isolate, object, key, object);
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
  return HasProperty(&it);
}

Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object,
                                       uint32_t index) {
  if (object->IsJSObject()) {  // Shortcut.
    LookupIterator it(object->GetIsolate(), object, index, object,
                      LookupIterator::OWN);
    return HasProperty(&it);
  }

  Maybe<PropertyAttributes> attributes =
      JSReceiver::GetOwnPropertyAttributes(object, index);
  MAYBE_RETURN(attributes, Nothing<bool>());
  return Just(attributes.FromJust() != ABSENT);
}

Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
    Handle<JSReceiver> object, Handle<Name> name) {
734
  Isolate* isolate = object->GetIsolate();
735
  PropertyKey key(isolate, name);
736
  LookupIterator it(isolate, object, key, object);
737 738 739 740 741
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes(
    Handle<JSReceiver> object, Handle<Name> name) {
742
  Isolate* isolate = object->GetIsolate();
743
  PropertyKey key(isolate, name);
744
  LookupIterator it(isolate, object, key, object, LookupIterator::OWN);
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes(
    Handle<JSReceiver> object, uint32_t index) {
  LookupIterator it(object->GetIsolate(), object, index, object,
                    LookupIterator::OWN);
  return GetPropertyAttributes(&it);
}

Maybe<bool> JSReceiver::HasElement(Handle<JSReceiver> object, uint32_t index) {
  LookupIterator it(object->GetIsolate(), object, index, object);
  return HasProperty(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetElementAttributes(
    Handle<JSReceiver> object, uint32_t index) {
  Isolate* isolate = object->GetIsolate();
  LookupIterator it(isolate, object, index, object);
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetOwnElementAttributes(
    Handle<JSReceiver> object, uint32_t index) {
  Isolate* isolate = object->GetIsolate();
  LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
  return GetPropertyAttributes(&it);
}

bool JSGlobalObject::IsDetached() {
775
  return global_proxy().IsDetachedFrom(*this);
776 777
}

778 779
bool JSGlobalProxy::IsDetachedFrom(JSGlobalObject global) const {
  const PrototypeIterator iter(this->GetIsolate(), *this);
780 781 782 783 784
  return iter.GetCurrent() != global;
}

inline int JSGlobalProxy::SizeWithEmbedderFields(int embedder_field_count) {
  DCHECK_GE(embedder_field_count, 0);
785
  return kHeaderSize + embedder_field_count * kEmbedderDataSlotSize;
786 787 788 789 790
}

ACCESSORS(JSIteratorResult, value, Object, kValueOffset)
ACCESSORS(JSIteratorResult, done, Object, kDoneOffset)

791 792 793 794 795 796 797 798 799 800 801
// If the fast-case backing storage takes up much more memory than a dictionary
// backing storage would, the object should have slow elements.
// static
static inline bool ShouldConvertToSlowElements(uint32_t used_elements,
                                               uint32_t new_capacity) {
  uint32_t size_threshold = NumberDictionary::kPreferFastElementsSizeFactor *
                            NumberDictionary::ComputeCapacity(used_elements) *
                            NumberDictionary::kEntrySize;
  return size_threshold <= new_capacity;
}

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
static inline bool ShouldConvertToSlowElements(JSObject object,
                                               uint32_t capacity,
                                               uint32_t index,
                                               uint32_t* new_capacity) {
  STATIC_ASSERT(JSObject::kMaxUncheckedOldFastElementsLength <=
                JSObject::kMaxUncheckedFastElementsLength);
  if (index < capacity) {
    *new_capacity = capacity;
    return false;
  }
  if (index - capacity >= JSObject::kMaxGap) return true;
  *new_capacity = JSObject::NewElementsCapacity(index + 1);
  DCHECK_LT(index, *new_capacity);
  if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength ||
      (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength &&
817
       ObjectInYoungGeneration(object))) {
818 819
    return false;
  }
820
  return ShouldConvertToSlowElements(object.GetFastElementsUsage(),
821
                                     *new_capacity);
822 823
}

824 825 826 827 828 829
}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_JS_OBJECTS_INL_H_