[typedarray] Make JSTypedArray::length authoritative.

This is the first step towards full huge typed array support in V8.
Before this change, the JSTypedArray::length and the elements backing
store length (FixedTypedArrayBase::length) were used more or less
interchangeably to determine the number of elements in a JSTypedArray.

With this change we disentangle these two lengths, and instead make
JSTypedArray::length authoritative. For on-heap typed arrays, the
FixedTypedArrayBase::length will remain the number of elements in the
backing store, but for the off-heap typed arrays, this length will be
set to 0 (matching the fact that the FixedTypedArrayBase instance does
not contain any elements itself).

This also unifies the JSTypedArray::set_/length() and length_value()
methods to only have JSTypedArray::set_/length() which returns/takes
size_t values. Currently this still requires the values to be in Smi
range, but later we will extend this to allow arbitrary size_t values
(in the safe integer range).

Bug: v8:4153, v8:7881
Change-Id: Iff9089130bb31fa9e08e0cf913e7ab52c3dbf107
Cq-Include-Trybots: luci.chromium.try:linux-blink-rel
Doc: http://doc/1Z-wM2qwvAuxH46e9ivtkYvKzzwYZg8ymm0x0wJaomow
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1543729
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: Ben Titzer <titzer@chromium.org>
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60648}
diff --git a/src/api.cc b/src/api.cc
index 3946c96..dadad3c 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -7622,7 +7622,7 @@
 
 size_t v8::TypedArray::Length() {
   i::Handle<i::JSTypedArray> obj = Utils::OpenHandle(this);
-  return obj->WasDetached() ? 0 : obj->length_value();
+  return obj->WasDetached() ? 0 : obj->length();
 }
 
 static_assert(v8::TypedArray::kMaxLength == i::Smi::kMaxValue,
diff --git a/src/builtins/base.tq b/src/builtins/base.tq
index e1df1ff..1ef876a 100644
--- a/src/builtins/base.tq
+++ b/src/builtins/base.tq
@@ -426,9 +426,8 @@
 }
 
 extern class JSTypedArray extends JSArrayBufferView {
-  AttachOffHeapBuffer(
-      buffer: JSArrayBuffer, map: Map, length: PositiveSmi,
-      byteOffset: uintptr): void {
+  AttachOffHeapBuffer(buffer: JSArrayBuffer, map: Map, byteOffset: uintptr):
+      void {
     const basePointer: Smi = 0;
 
     // The max byteOffset is 8 * MaxSmi on the particular platform. 32 bit
@@ -449,7 +448,7 @@
     this.buffer = buffer;
     this.elements = new FixedTypedArrayBase{
       map,
-      length,
+      length: 0,
       base_pointer: basePointer,
       external_pointer: externalPointer
     };
diff --git a/src/builtins/builtins-sharedarraybuffer.cc b/src/builtins/builtins-sharedarraybuffer.cc
index 96c5558..60e2a8b 100644
--- a/src/builtins/builtins-sharedarraybuffer.cc
+++ b/src/builtins/builtins-sharedarraybuffer.cc
@@ -74,8 +74,7 @@
 
   size_t access_index;
   if (!TryNumberToSize(*access_index_obj, &access_index) ||
-      typed_array->WasDetached() ||
-      access_index >= typed_array->length_value()) {
+      typed_array->WasDetached() || access_index >= typed_array->length()) {
     isolate->Throw(*isolate->factory()->NewRangeError(
         MessageTemplate::kInvalidAtomicAccessIndex));
     return Nothing<size_t>();
diff --git a/src/builtins/builtins-typed-array.cc b/src/builtins/builtins-typed-array.cc
index ac1b23c..7a902ac 100644
--- a/src/builtins/builtins-typed-array.cc
+++ b/src/builtins/builtins-typed-array.cc
@@ -51,7 +51,7 @@
   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
 
-  int64_t len = array->length_value();
+  int64_t len = array->length();
   int64_t to = 0;
   int64_t from = 0;
   int64_t final = len;
@@ -124,7 +124,7 @@
                                        Object::ToNumber(isolate, obj_value));
   }
 
-  int64_t len = array->length_value();
+  int64_t len = array->length();
   int64_t start = 0;
   int64_t end = len;
 
@@ -171,7 +171,7 @@
 
   if (args.length() < 2) return ReadOnlyRoots(isolate).false_value();
 
-  int64_t len = array->length_value();
+  int64_t len = array->length();
   if (len == 0) return ReadOnlyRoots(isolate).false_value();
 
   int64_t index = 0;
@@ -203,7 +203,7 @@
   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
 
-  int64_t len = array->length_value();
+  int64_t len = array->length();
   if (len == 0) return Smi::FromInt(-1);
 
   int64_t index = 0;
@@ -234,7 +234,7 @@
   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
 
-  int64_t len = array->length_value();
+  int64_t len = array->length();
   if (len == 0) return Smi::FromInt(-1);
 
   int64_t index = len - 1;
diff --git a/src/builtins/typed-array-createtypedarray.tq b/src/builtins/typed-array-createtypedarray.tq
index 04630dc..208ec60 100644
--- a/src/builtins/typed-array-createtypedarray.tq
+++ b/src/builtins/typed-array-createtypedarray.tq
@@ -73,8 +73,7 @@
     label AttachOffHeapBuffer(bufferObj: Object) {
       const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable;
       const byteOffset: uintptr = 0;
-      typedArray.AttachOffHeapBuffer(
-          buffer, elementsInfo.map, length, byteOffset);
+      typedArray.AttachOffHeapBuffer(buffer, elementsInfo.map, byteOffset);
     }
 
     const byteOffset: uintptr = 0;
@@ -237,8 +236,7 @@
       }
 
       SetupTypedArray(typedArray, newLength, offset, newByteLength);
-      typedArray.AttachOffHeapBuffer(
-          buffer, elementsInfo.map, newLength, offset);
+      typedArray.AttachOffHeapBuffer(buffer, elementsInfo.map, offset);
     }
     label IfInvalidAlignment(problemString: String) deferred {
       ThrowInvalidTypedArrayAlignment(typedArray.map, problemString);
diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
index 7046d21..0b86a46 100644
--- a/src/code-stub-assembler.cc
+++ b/src/code-stub-assembler.cc
@@ -10552,12 +10552,6 @@
   TVARIABLE(UintPtrT, var_high);
   BigIntToRawBytes(bigint_value, &var_low, &var_high);
 
-  // Assert that offset < elements.length. Given that it's an offset for a raw
-  // pointer we correct it by the usual kHeapObjectTag offset.
-  CSA_ASSERT(
-      this, IsOffsetInBounds(offset, LoadAndUntagFixedArrayBaseLength(elements),
-                             kHeapObjectTag, BIGINT64_ELEMENTS));
-
   MachineRepresentation rep = WordT::kMachineRepresentation;
 #if defined(V8_TARGET_BIG_ENDIAN)
   if (!Is64()) {
diff --git a/src/compiler/js-heap-broker.cc b/src/compiler/js-heap-broker.cc
index c86f8bd..dec7da9 100644
--- a/src/compiler/js-heap-broker.cc
+++ b/src/compiler/js-heap-broker.cc
@@ -242,7 +242,7 @@
                    Handle<JSTypedArray> object);
 
   bool is_on_heap() const { return is_on_heap_; }
-  size_t length_value() const { return length_value_; }
+  size_t length() const { return length_; }
   void* elements_external_pointer() const { return elements_external_pointer_; }
 
   void Serialize(JSHeapBroker* broker);
@@ -252,7 +252,7 @@
 
  private:
   bool const is_on_heap_;
-  size_t const length_value_;
+  size_t const length_;
   void* const elements_external_pointer_;
 
   bool serialized_ = false;
@@ -263,7 +263,7 @@
                                    Handle<JSTypedArray> object)
     : JSObjectData(broker, storage, object),
       is_on_heap_(object->is_on_heap()),
-      length_value_(object->length_value()),
+      length_(object->length()),
       elements_external_pointer_(
           FixedTypedArrayBase::cast(object->elements())->external_pointer()) {}
 
@@ -2430,7 +2430,7 @@
 BIMODAL_ACCESSOR(JSFunction, FeedbackVector, feedback_vector)
 
 BIMODAL_ACCESSOR_C(JSTypedArray, bool, is_on_heap)
-BIMODAL_ACCESSOR_C(JSTypedArray, size_t, length_value)
+BIMODAL_ACCESSOR_C(JSTypedArray, size_t, length)
 BIMODAL_ACCESSOR(JSTypedArray, HeapObject, buffer)
 
 BIMODAL_ACCESSOR_B(Map, bit_field2, elements_kind, Map::ElementsKindBits)
diff --git a/src/compiler/js-heap-broker.h b/src/compiler/js-heap-broker.h
index f52d5a3..39434b7 100644
--- a/src/compiler/js-heap-broker.h
+++ b/src/compiler/js-heap-broker.h
@@ -585,7 +585,7 @@
   Handle<JSTypedArray> object() const;
 
   bool is_on_heap() const;
-  size_t length_value() const;
+  size_t length() const;
   void* elements_external_pointer() const;
 
   void Serialize();
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
index 8ff7f86..37c2a82 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -2648,8 +2648,7 @@
         GetTypedArrayConstant(broker(), receiver);
     if (typed_array.has_value()) {
       buffer = jsgraph()->Constant(typed_array->buffer());
-      length =
-          jsgraph()->Constant(static_cast<double>(typed_array->length_value()));
+      length = jsgraph()->Constant(static_cast<double>(typed_array->length()));
 
       // Load the (known) base and external pointer for the {receiver}. The
       // {external_pointer} might be invalid if the {buffer} was detached, so
diff --git a/src/debug/debug-property-iterator.cc b/src/debug/debug-property-iterator.cc
index 1bef581..52d7bc0 100644
--- a/src/debug/debug-property-iterator.cc
+++ b/src/debug/debug-property-iterator.cc
@@ -148,8 +148,9 @@
   bool has_exotic_indices = receiver->IsJSTypedArray();
   if (stage_ == kExoticIndices) {
     if (!has_exotic_indices) return;
-    exotic_length_ = static_cast<uint32_t>(
-        Handle<JSTypedArray>::cast(receiver)->length_value());
+    // TODO(bmeurer, v8:4153): Change this to size_t later.
+    exotic_length_ =
+        static_cast<uint32_t>(Handle<JSTypedArray>::cast(receiver)->length());
     return;
   }
   bool skip_indices = has_exotic_indices;
diff --git a/src/elements.cc b/src/elements.cc
index a29801c..a8f81b1 100644
--- a/src/elements.cc
+++ b/src/elements.cc
@@ -2915,8 +2915,10 @@
 
   static uint32_t GetCapacityImpl(JSObject holder,
                                   FixedArrayBase backing_store) {
-    if (WasDetached(holder)) return 0;
-    return backing_store->length();
+    JSTypedArray typed_array = JSTypedArray::cast(holder);
+    if (WasDetached(typed_array)) return 0;
+    // TODO(bmeurer, v8:4153): We need to support arbitrary size_t here.
+    return static_cast<uint32_t>(typed_array->length());
   }
 
   static uint32_t NumberOfElementsImpl(JSObject receiver,
@@ -2968,7 +2970,7 @@
     // Ensure indexes are within array bounds
     CHECK_LE(0, start);
     CHECK_LE(start, end);
-    CHECK_LE(end, array->length_value());
+    CHECK_LE(end, array->length());
 
     DisallowHeapAllocation no_gc;
     BackingStore elements = BackingStore::cast(receiver->elements());
@@ -2988,23 +2990,24 @@
                                        Handle<Object> value,
                                        uint32_t start_from, uint32_t length) {
     DisallowHeapAllocation no_gc;
+    JSTypedArray typed_array = JSTypedArray::cast(*receiver);
 
     // TODO(caitp): return Just(false) here when implementing strict throwing on
     // detached views.
-    if (WasDetached(*receiver)) {
+    if (WasDetached(typed_array)) {
       return Just(value->IsUndefined(isolate) && length > start_from);
     }
 
-    BackingStore elements = BackingStore::cast(receiver->elements());
-    if (value->IsUndefined(isolate) &&
-        length > static_cast<uint32_t>(elements->length())) {
+    BackingStore elements = BackingStore::cast(typed_array->elements());
+    if (value->IsUndefined(isolate) && length > typed_array->length()) {
       return Just(true);
     }
     ctype typed_search_value;
     // Prototype has no elements, and not searching for the hole --- limit
     // search to backing store length.
-    if (static_cast<uint32_t>(elements->length()) < length) {
-      length = elements->length();
+    if (typed_array->length() < length) {
+      // TODO(bmeurer, v8:4153): Don't cast to uint32_t here.
+      length = static_cast<uint32_t>(typed_array->length());
     }
 
     if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
@@ -3050,10 +3053,11 @@
                                          Handle<Object> value,
                                          uint32_t start_from, uint32_t length) {
     DisallowHeapAllocation no_gc;
+    JSTypedArray typed_array = JSTypedArray::cast(*receiver);
 
-    if (WasDetached(*receiver)) return Just<int64_t>(-1);
+    if (WasDetached(typed_array)) return Just<int64_t>(-1);
 
-    BackingStore elements = BackingStore::cast(receiver->elements());
+    BackingStore elements = BackingStore::cast(typed_array->elements());
     ctype typed_search_value;
 
     if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
@@ -3085,8 +3089,9 @@
 
     // Prototype has no elements, and not searching for the hole --- limit
     // search to backing store length.
-    if (static_cast<uint32_t>(elements->length()) < length) {
-      length = elements->length();
+    if (typed_array->length() < length) {
+      // TODO(bmeurer, v8:4153): Don't cast to uint32_t here.
+      length = static_cast<uint32_t>(typed_array->length());
     }
 
     for (uint32_t k = start_from; k < length; ++k) {
@@ -3100,9 +3105,11 @@
                                              Handle<Object> value,
                                              uint32_t start_from) {
     DisallowHeapAllocation no_gc;
-    DCHECK(!WasDetached(*receiver));
+    JSTypedArray typed_array = JSTypedArray::cast(*receiver);
 
-    BackingStore elements = BackingStore::cast(receiver->elements());
+    DCHECK(!WasDetached(typed_array));
+
+    BackingStore elements = BackingStore::cast(typed_array->elements());
     ctype typed_search_value;
 
     if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
@@ -3132,8 +3139,7 @@
       }
     }
 
-    DCHECK_LT(start_from, elements->length());
-
+    DCHECK_LT(start_from, typed_array->length());
     uint32_t k = start_from;
     do {
       ctype element_k = elements->get_scalar(k);
@@ -3144,11 +3150,13 @@
 
   static void ReverseImpl(JSObject receiver) {
     DisallowHeapAllocation no_gc;
-    DCHECK(!WasDetached(receiver));
+    JSTypedArray typed_array = JSTypedArray::cast(receiver);
 
-    BackingStore elements = BackingStore::cast(receiver->elements());
+    DCHECK(!WasDetached(typed_array));
 
-    uint32_t len = elements->length();
+    BackingStore elements = BackingStore::cast(typed_array->elements());
+
+    size_t len = typed_array->length();
     if (len == 0) return;
 
     ctype* data = static_cast<ctype*>(elements->DataPtr());
@@ -3184,10 +3192,10 @@
     CHECK(!source->WasDetached());
     CHECK(!destination->WasDetached());
     DCHECK_LE(start, end);
-    DCHECK_LE(end, source->length_value());
+    DCHECK_LE(end, source->length());
 
     size_t count = end - start;
-    DCHECK_LE(count, destination->length_value());
+    DCHECK_LE(count, destination->length());
 
     FixedTypedArrayBase src_elements =
         FixedTypedArrayBase::cast(source->elements());
@@ -3259,10 +3267,9 @@
     BackingStore destination_elements =
         BackingStore::cast(destination->elements());
 
-    DCHECK_LE(offset, destination->length_value());
-    DCHECK_LE(length, destination->length_value() - offset);
-    DCHECK(source->length()->IsSmi());
-    DCHECK_LE(length, source->length_value());
+    DCHECK_LE(offset, destination->length());
+    DCHECK_LE(length, destination->length() - offset);
+    DCHECK_LE(length, source->length());
 
     InstanceType source_type = source_elements->map()->instance_type();
     InstanceType destination_type =
@@ -3352,7 +3359,7 @@
            length <= current_length);
     USE(current_length);
 
-    size_t dest_length = destination->length_value();
+    size_t dest_length = destination->length();
     DCHECK(length + offset <= dest_length);
     USE(dest_length);
 
@@ -3464,7 +3471,7 @@
     Isolate* isolate = destination->GetIsolate();
     Handle<JSTypedArray> destination_ta =
         Handle<JSTypedArray>::cast(destination);
-    DCHECK_LE(offset + length, destination_ta->length_value());
+    DCHECK_LE(offset + length, destination_ta->length());
     CHECK(!destination_ta->WasDetached());
 
     if (length == 0) return *isolate->factory()->undefined_value();
@@ -3492,8 +3499,7 @@
       }
       // If we have to copy more elements than we have in the source, we need to
       // do special handling and conversion; that happens in the slow case.
-      if (!source_ta->WasDetached() &&
-          length + offset <= source_ta->length_value()) {
+      if (!source_ta->WasDetached() && length + offset <= source_ta->length()) {
         CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
         return *isolate->factory()->undefined_value();
       }
diff --git a/src/heap/factory.cc b/src/heap/factory.cc
index 077a8145..15d2da3 100644
--- a/src/heap/factory.cc
+++ b/src/heap/factory.cc
@@ -1836,10 +1836,8 @@
 }
 
 Handle<FixedTypedArrayBase> Factory::NewFixedTypedArrayWithExternalPointer(
-    int length, ExternalArrayType array_type, void* external_pointer,
+    ExternalArrayType array_type, void* external_pointer,
     AllocationType allocation) {
-  // TODO(7881): Smi length check
-  DCHECK(0 <= length && length <= Smi::kMaxValue);
   int size = FixedTypedArrayBase::kHeaderSize;
   HeapObject result = AllocateRawWithImmortalMap(
       size, allocation,
@@ -1848,7 +1846,7 @@
                                        isolate());
   elements->set_base_pointer(Smi::kZero, SKIP_WRITE_BARRIER);
   elements->set_external_pointer(external_pointer);
-  elements->set_length(length);
+  elements->set_length(0);
   return elements;
 }
 
@@ -3430,12 +3428,11 @@
   size_t byte_length = length * element_size;
   SetupArrayBufferView(isolate(), obj, buffer, byte_offset, byte_length);
 
-  Handle<Object> length_object = NewNumberFromSize(length, allocation);
-  obj->set_length(*length_object);
+  obj->set_length(length);
 
   Handle<FixedTypedArrayBase> elements = NewFixedTypedArrayWithExternalPointer(
-      static_cast<int>(length), type,
-      static_cast<uint8_t*>(buffer->backing_store()) + byte_offset, allocation);
+      type, static_cast<uint8_t*>(buffer->backing_store()) + byte_offset,
+      allocation);
   Handle<Map> map = JSObject::GetElementsTransitionMap(obj, elements_kind);
   JSObject::SetMapAndElements(obj, map, elements);
   return obj;
@@ -3463,7 +3460,7 @@
 
   obj->set_byte_offset(0);
   obj->set_byte_length(byte_length);
-  obj->set_length(Smi::FromIntptr(static_cast<intptr_t>(number_of_elements)));
+  obj->set_length(number_of_elements);
 
   Handle<JSArrayBuffer> buffer =
       NewJSArrayBuffer(SharedFlag::kNotShared, allocation);
diff --git a/src/heap/factory.h b/src/heap/factory.h
index 9464651..5c2ef96 100644
--- a/src/heap/factory.h
+++ b/src/heap/factory.h
@@ -489,7 +489,7 @@
                                          Handle<FixedArray> constant_pool);
 
   Handle<FixedTypedArrayBase> NewFixedTypedArrayWithExternalPointer(
-      int length, ExternalArrayType array_type, void* external_pointer,
+      ExternalArrayType array_type, void* external_pointer,
       AllocationType allocation = AllocationType::kYoung);
 
   Handle<FixedTypedArrayBase> NewFixedTypedArray(
diff --git a/src/ic/ic.cc b/src/ic/ic.cc
index 2718c50..7e51a7a 100644
--- a/src/ic/ic.cc
+++ b/src/ic/ic.cc
@@ -1253,13 +1253,15 @@
 }
 
 bool IsOutOfBoundsAccess(Handle<Object> receiver, uint32_t index) {
-  uint32_t length = 0;
+  size_t length;
   if (receiver->IsJSArray()) {
-    JSArray::cast(*receiver)->length()->ToArrayLength(&length);
-  } else if (receiver->IsString()) {
-    length = String::cast(*receiver)->length();
+    length = JSArray::cast(*receiver)->length()->Number();
+  } else if (receiver->IsJSTypedArray()) {
+    length = JSTypedArray::cast(*receiver)->length();
   } else if (receiver->IsJSObject()) {
     length = JSObject::cast(*receiver)->elements()->length();
+  } else if (receiver->IsString()) {
+    length = String::cast(*receiver)->length();
   } else {
     return false;
   }
diff --git a/src/json-stringifier.cc b/src/json-stringifier.cc
index b06de2e..1ad2209 100644
--- a/src/json-stringifier.cc
+++ b/src/json-stringifier.cc
@@ -761,7 +761,10 @@
 
   if (property_list_.is_null() &&
       !object->map()->IsCustomElementsReceiverMap() &&
-      object->HasFastProperties() && object->elements()->length() == 0) {
+      object->HasFastProperties() &&
+      (object->elements() == ReadOnlyRoots(isolate_).empty_fixed_array() ||
+       object->elements() ==
+           ReadOnlyRoots(isolate_).empty_slow_element_dictionary())) {
     DCHECK(!object->IsJSGlobalProxy());
     DCHECK(!object->HasIndexedInterceptor());
     DCHECK(!object->HasNamedInterceptor());
diff --git a/src/keys.cc b/src/keys.cc
index 99fa2be..0926362 100644
--- a/src/keys.cc
+++ b/src/keys.cc
@@ -448,7 +448,9 @@
   Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
   // Uninitalized enum cache
   Map map = object->map();
-  if (object->elements()->length() != 0) {
+  if (object->elements() != ReadOnlyRoots(isolate_).empty_fixed_array() &&
+      object->elements() !=
+          ReadOnlyRoots(isolate_).empty_slow_element_dictionary()) {
     // Assume that there are elements.
     return MaybeHandle<FixedArray>();
   }
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index ab4de3f..72d8463 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -520,7 +520,8 @@
     CHECK_EQ(reinterpret_cast<Address>(external_pointer()),
              FixedTypedArrayBase::kDataOffset - kHeapObjectTag);
   } else {
-    CHECK_EQ(base_pointer(), Smi::kZero);
+    CHECK_EQ(Smi::kZero, base_pointer());
+    CHECK_EQ(0, length());
   }
 }
 
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index c359045..146e32b 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -1372,7 +1372,7 @@
   os << "\n - buffer: " << Brief(buffer());
   os << "\n - byte_offset: " << byte_offset();
   os << "\n - byte_length: " << byte_length();
-  os << "\n - length: " << Brief(length());
+  os << "\n - length: " << length();
   if (!buffer()->IsJSArrayBuffer()) {
     os << "\n <invalid buffer>\n";
     return;
diff --git a/src/objects.cc b/src/objects.cc
index f27f067..88658fb 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -948,7 +948,7 @@
           isolate, array, length);
     } else if (object->IsJSTypedArray()) {
       Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object);
-      size_t length = array->length_value();
+      size_t length = array->length();
       if (array->WasDetached() ||
           length > static_cast<size_t>(FixedArray::kMaxLength)) {
         return MaybeHandle<FixedArray>();
diff --git a/src/objects/fixed-array-inl.h b/src/objects/fixed-array-inl.h
index d494f8d..aaa9e01 100644
--- a/src/objects/fixed-array-inl.h
+++ b/src/objects/fixed-array-inl.h
@@ -641,7 +641,9 @@
 
 template <class Traits>
 typename Traits::ElementType FixedTypedArray<Traits>::get_scalar(int index) {
-  DCHECK((index >= 0) && (index < this->length()));
+  // TODO(bmeurer, v8:4153): Solve this differently.
+  // DCHECK((index < this->length()));
+  CHECK_GE(index, 0);
   return FixedTypedArray<Traits>::get_scalar_from_data_ptr(DataPtr(), index);
 }
 
@@ -676,7 +678,9 @@
 
 template <class Traits>
 void FixedTypedArray<Traits>::set(int index, ElementType value) {
-  CHECK((index >= 0) && (index < this->length()));
+  // TODO(bmeurer, v8:4153): Solve this differently.
+  // CHECK((index < this->length()));
+  CHECK_GE(index, 0);
   // See the comment in FixedTypedArray<Traits>::get_scalar.
   auto* ptr = reinterpret_cast<ElementType*>(DataPtr());
   TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
diff --git a/src/objects/js-array-buffer-inl.h b/src/objects/js-array-buffer-inl.h
index 3967709..98b6f90 100644
--- a/src/objects/js-array-buffer-inl.h
+++ b/src/objects/js-array-buffer-inl.h
@@ -132,20 +132,17 @@
   return JSArrayBuffer::cast(buffer())->was_detached();
 }
 
-Object JSTypedArray::length() const { return READ_FIELD(*this, kLengthOffset); }
-
-size_t JSTypedArray::length_value() const {
-  double val = length()->Number();
-  DCHECK_LE(val, kMaxSafeInteger);   // 2^53-1
-  DCHECK_GE(val, -kMaxSafeInteger);  // -2^53+1
-  DCHECK_LE(val, std::numeric_limits<size_t>::max());
-  DCHECK_GE(val, std::numeric_limits<size_t>::min());
-  return static_cast<size_t>(val);
+size_t JSTypedArray::length() const {
+  // TODO(bmeurer, v8:4153): Change this to size_t later.
+  int length = Smi::cast(raw_length())->value();
+  DCHECK_LE(0, length);
+  return length;
 }
 
-void JSTypedArray::set_length(Object value, WriteBarrierMode mode) {
-  WRITE_FIELD(*this, kLengthOffset, value);
-  CONDITIONAL_WRITE_BARRIER(*this, kLengthOffset, value, mode);
+void JSTypedArray::set_length(size_t value) {
+  // TODO(bmeurer, v8:4153): Change this to size_t later.
+  CHECK_LE(value, Smi::kMaxValue);
+  set_raw_length(Smi::FromInt(static_cast<int>(value)), SKIP_WRITE_BARRIER);
 }
 
 bool JSTypedArray::is_on_heap() const {
@@ -178,9 +175,7 @@
   return array;
 }
 
-#ifdef VERIFY_HEAP
 ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset)
-#endif
 
 }  // namespace internal
 }  // namespace v8
diff --git a/src/objects/js-array-buffer.cc b/src/objects/js-array-buffer.cc
index f96ae7e..32ae76d 100644
--- a/src/objects/js-array-buffer.cc
+++ b/src/objects/js-array-buffer.cc
@@ -181,8 +181,7 @@
          fixed_typed_array->DataSize());
   Handle<FixedTypedArrayBase> new_elements =
       isolate->factory()->NewFixedTypedArrayWithExternalPointer(
-          fixed_typed_array->length(), typed_array->type(),
-          static_cast<uint8_t*>(buffer->backing_store()));
+          typed_array->type(), static_cast<uint8_t*>(buffer->backing_store()));
 
   typed_array->set_elements(*new_elements);
   DCHECK(!typed_array->is_on_heap());
@@ -226,7 +225,7 @@
                        NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
       }
       // 3b iv. Let length be O.[[ArrayLength]].
-      size_t length = o->length_value();
+      size_t length = o->length();
       // 3b v. If numericIndex ≥ length, return false.
       if (o->WasDetached() || index >= length) {
         RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
diff --git a/src/objects/js-array-buffer.h b/src/objects/js-array-buffer.h
index 40eac99..4d9ea99 100644
--- a/src/objects/js-array-buffer.h
+++ b/src/objects/js-array-buffer.h
@@ -181,8 +181,7 @@
 class JSTypedArray : public JSArrayBufferView {
  public:
   // [length]: length of typed array in elements.
-  DECL_ACCESSORS(length, Object)
-  inline size_t length_value() const;
+  DECL_PRIMITIVE_ACCESSORS(length, size_t)
 
   // ES6 9.4.5.3
   V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty(
@@ -225,9 +224,8 @@
  private:
   static Handle<JSArrayBuffer> MaterializeArrayBuffer(
       Handle<JSTypedArray> typed_array);
-#ifdef VERIFY_HEAP
+
   DECL_ACCESSORS(raw_length, Object)
-#endif
 
   OBJECT_CONSTRUCTORS(JSTypedArray, JSArrayBufferView);
 };
diff --git a/src/runtime/runtime-futex.cc b/src/runtime/runtime-futex.cc
index e8b4025..e2a0c96 100644
--- a/src/runtime/runtime-futex.cc
+++ b/src/runtime/runtime-futex.cc
@@ -27,7 +27,7 @@
   CONVERT_SIZE_ARG_CHECKED(index, 1);
   CHECK(!sta->WasDetached());
   CHECK(sta->GetBuffer()->is_shared());
-  CHECK_LT(index, NumberToSize(sta->length()));
+  CHECK_LT(index, sta->length());
   CHECK_EQ(sta->type(), kExternalInt32Array);
 
   Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
diff --git a/src/runtime/runtime-typedarray.cc b/src/runtime/runtime-typedarray.cc
index 3d99b1b..c8097b5 100644
--- a/src/runtime/runtime-typedarray.cc
+++ b/src/runtime/runtime-typedarray.cc
@@ -60,13 +60,6 @@
   return accessor->CopyElements(source, target, length);
 }
 
-RUNTIME_FUNCTION(Runtime_TypedArrayGetLength) {
-  HandleScope scope(isolate);
-  DCHECK_EQ(1, args.length());
-  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
-  return holder->length();
-}
-
 RUNTIME_FUNCTION(Runtime_ArrayBufferViewWasDetached) {
   HandleScope scope(isolate);
   DCHECK_EQ(1, args.length());
@@ -119,7 +112,7 @@
   // if array.[[ViewedArrayBuffer]] is detached(v8:4648)
   if (V8_UNLIKELY(array->WasDetached())) return *array;
 
-  size_t length = array->length_value();
+  size_t length = array->length();
   if (length <= 1) return *array;
 
   Handle<FixedTypedArrayBase> elements(
@@ -194,7 +187,7 @@
   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
                                      Object::ToLength(isolate, len));
 
-  if (uint_offset + len->Number() > target->length_value()) {
+  if (uint_offset + len->Number() > target->length()) {
     THROW_NEW_ERROR_RETURN_FAILURE(
         isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
   }
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index b193ead..a22ec60 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -525,7 +525,6 @@
   I(IsTypedArray, 1, 1)                     \
   F(TypedArrayCopyElements, 3, 1)           \
   F(TypedArrayGetBuffer, 1, 1)              \
-  F(TypedArrayGetLength, 1, 1)              \
   F(TypedArraySet, 2, 1)                    \
   F(TypedArraySortFast, 1, 1)
 
diff --git a/test/mjsunit/json-stringify-typedarray.js b/test/mjsunit/json-stringify-typedarray.js
new file mode 100644
index 0000000..48a4fbb
--- /dev/null
+++ b/test/mjsunit/json-stringify-typedarray.js
@@ -0,0 +1,18 @@
+// Copyright 2019 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.
+
+[Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array,
+ Uint32Array, Float32Array, Float64Array]
+    .forEach(constructor => {
+      const empty = new constructor(0);
+      assertEquals('{}', JSON.stringify(empty));
+
+      const tiny = new constructor(2).fill(123);
+      assertEquals('{"0":123,"1":123}', JSON.stringify(tiny));
+
+      const huge = new constructor(64).fill(123);
+      assertEquals(
+          '{"0":123,"1":123,"2":123,"3":123,"4":123,"5":123,"6":123,"7":123,"8":123,"9":123,"10":123,"11":123,"12":123,"13":123,"14":123,"15":123,"16":123,"17":123,"18":123,"19":123,"20":123,"21":123,"22":123,"23":123,"24":123,"25":123,"26":123,"27":123,"28":123,"29":123,"30":123,"31":123,"32":123,"33":123,"34":123,"35":123,"36":123,"37":123,"38":123,"39":123,"40":123,"41":123,"42":123,"43":123,"44":123,"45":123,"46":123,"47":123,"48":123,"49":123,"50":123,"51":123,"52":123,"53":123,"54":123,"55":123,"56":123,"57":123,"58":123,"59":123,"60":123,"61":123,"62":123,"63":123}',
+          JSON.stringify(huge));
+    });
diff --git a/test/mjsunit/object-keys-typedarray.js b/test/mjsunit/object-keys-typedarray.js
new file mode 100644
index 0000000..b80608b
--- /dev/null
+++ b/test/mjsunit/object-keys-typedarray.js
@@ -0,0 +1,17 @@
+// Copyright 2019 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.
+
+[Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array,
+ Uint32Array, Float32Array, Float64Array]
+    .forEach(constructor => {
+      const huge = new constructor(128);
+      assertEquals(Array.from({length: 128}).map((_, i) => String(i)),
+                   Object.keys(huge));
+
+      const tiny = new constructor(2);
+      assertEquals(["0", "1"], Object.keys(tiny));
+
+      const empty = new constructor(0);
+      assertEquals([], Object.keys(empty));
+});