Commit 0697a1b0 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[objects] Add JSObject::kMaxElementIndex and JSArray::kMaxArrayLength

The available constants are now:

 JSObject {
  kMaxElementCount = kMaxUInt32,
  kMaxElementIndex = kMaxElementCount - 1,
 }

 JSArray {
  kMaxArrayLength = JSObject::kMaxElementCount,
  kMaxArrayIndex = JSObject::kMaxElementIndex,
 }

I also updated the codebase to use the new constants.

Change-Id: I3142f9ff9627c9acb1d4493729b490150fdcdf50
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2712755Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73006}
parent e278b6d7
...@@ -3,8 +3,12 @@ ...@@ -3,8 +3,12 @@
// found in the LICENSE file. // found in the LICENSE file.
namespace array { namespace array {
type LoadJoinElementFn = builtin(Context, JSReceiver, uintptr) => JSAny; type LoadJoinElementFn = builtin(Context, JSReceiver, uintptr) => JSAny;
const kMaxArrayLength:
constexpr uint32 generates 'JSArray::kMaxArrayLength';
// Fast C call to write a fixed array (see Buffer.fixedArray) to a single // Fast C call to write a fixed array (see Buffer.fixedArray) to a single
// string. // string.
extern macro extern macro
...@@ -555,8 +559,9 @@ ArrayPrototypeJoin( ...@@ -555,8 +559,9 @@ ArrayPrototypeJoin(
// Only handle valid array lengths. Although the spec allows larger // Only handle valid array lengths. Although the spec allows larger
// values, this matches historical V8 behavior. // values, this matches historical V8 behavior.
if (len > kMaxArrayIndex + 1) if (len > kMaxArrayLength) {
ThrowTypeError(MessageTemplate::kInvalidArrayLength); ThrowTypeError(MessageTemplate::kInvalidArrayLength);
}
return CycleProtectedArrayJoin<JSArray>( return CycleProtectedArrayJoin<JSArray>(
false, o, len, separator, Undefined, Undefined); false, o, len, separator, Undefined, Undefined);
...@@ -576,8 +581,9 @@ transitioning javascript builtin ArrayPrototypeToLocaleString( ...@@ -576,8 +581,9 @@ transitioning javascript builtin ArrayPrototypeToLocaleString(
// Only handle valid array lengths. Although the spec allows larger // Only handle valid array lengths. Although the spec allows larger
// values, this matches historical V8 behavior. // values, this matches historical V8 behavior.
if (len > kMaxArrayIndex + 1) if (len > kMaxArrayLength) {
ThrowTypeError(MessageTemplate::kInvalidArrayLength); ThrowTypeError(MessageTemplate::kInvalidArrayLength);
}
return CycleProtectedArrayJoin<JSArray>(true, o, len, ',', locales, options); return CycleProtectedArrayJoin<JSArray>(true, o, len, ',', locales, options);
} }
......
...@@ -415,8 +415,6 @@ extern enum PropertyAttributes extends int31 { ...@@ -415,8 +415,6 @@ extern enum PropertyAttributes extends int31 {
... ...
} }
const kMaxArrayIndex:
constexpr uint32 generates 'JSArray::kMaxArrayIndex';
const kArrayBufferMaxByteLength: const kArrayBufferMaxByteLength:
constexpr uintptr generates 'JSArrayBuffer::kMaxByteLength'; constexpr uintptr generates 'JSArrayBuffer::kMaxByteLength';
const kTypedArrayMaxLength: const kTypedArrayMaxLength:
......
...@@ -333,7 +333,7 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate, ...@@ -333,7 +333,7 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
Handle<Object> element = args->at(i + 1); Handle<Object> element = args->at(i + 1);
// b. Perform ? Set(O, ! ToString(len), E, true). // b. Perform ? Set(O, ! ToString(len), E, true).
if (length <= static_cast<double>(JSArray::kMaxArrayIndex)) { if (length <= JSObject::kMaxElementIndex) {
RETURN_FAILURE_ON_EXCEPTION( RETURN_FAILURE_ON_EXCEPTION(
isolate, Object::SetElement(isolate, receiver, length, element, isolate, Object::SetElement(isolate, receiver, length, element,
ShouldThrow::kThrowOnError)); ShouldThrow::kThrowOnError));
...@@ -662,7 +662,7 @@ class ArrayConcatVisitor { ...@@ -662,7 +662,7 @@ class ArrayConcatVisitor {
V8_WARN_UNUSED_RESULT bool visit(uint32_t i, Handle<Object> elm) { V8_WARN_UNUSED_RESULT bool visit(uint32_t i, Handle<Object> elm) {
uint32_t index = index_offset_ + i; uint32_t index = index_offset_ + i;
if (i >= JSObject::kMaxElementCount - index_offset_) { if (i > JSArray::kMaxArrayIndex - index_offset_) {
set_exceeds_array_limit(true); set_exceeds_array_limit(true);
// Exception hasn't been thrown at this point. Return true to // Exception hasn't been thrown at this point. Return true to
// break out, and caller will throw. !visit would imply that // break out, and caller will throw. !visit would imply that
...@@ -707,8 +707,8 @@ class ArrayConcatVisitor { ...@@ -707,8 +707,8 @@ class ArrayConcatVisitor {
uint32_t index_offset() const { return index_offset_; } uint32_t index_offset() const { return index_offset_; }
void increase_index_offset(uint32_t delta) { void increase_index_offset(uint32_t delta) {
if (JSObject::kMaxElementCount - index_offset_ < delta) { if (JSArray::kMaxArrayLength - index_offset_ < delta) {
index_offset_ = JSObject::kMaxElementCount; index_offset_ = JSArray::kMaxArrayLength;
} else { } else {
index_offset_ += delta; index_offset_ += delta;
} }
...@@ -813,7 +813,7 @@ class ArrayConcatVisitor { ...@@ -813,7 +813,7 @@ class ArrayConcatVisitor {
Isolate* isolate_; Isolate* isolate_;
Handle<Object> storage_; // Always a global handle. Handle<Object> storage_; // Always a global handle.
// Index after last seen index. Always less than or equal to // Index after last seen index. Always less than or equal to
// JSObject::kMaxElementCount. // JSArray::kMaxArrayLength.
uint32_t index_offset_; uint32_t index_offset_;
uint32_t bit_field_; uint32_t bit_field_;
}; };
...@@ -1249,14 +1249,14 @@ Object Slow_ArrayConcat(BuiltinArguments* args, Handle<Object> species, ...@@ -1249,14 +1249,14 @@ Object Slow_ArrayConcat(BuiltinArguments* args, Handle<Object> species,
length_estimate = 1; length_estimate = 1;
element_estimate = 1; element_estimate = 1;
} }
// Avoid overflows by capping at kMaxElementCount. // Avoid overflows by capping at kMaxArrayLength.
if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) { if (JSArray::kMaxArrayLength - estimate_result_length < length_estimate) {
estimate_result_length = JSObject::kMaxElementCount; estimate_result_length = JSArray::kMaxArrayLength;
} else { } else {
estimate_result_length += length_estimate; estimate_result_length += length_estimate;
} }
if (JSObject::kMaxElementCount - estimate_nof < element_estimate) { if (JSArray::kMaxArrayLength - estimate_nof < element_estimate) {
estimate_nof = JSObject::kMaxElementCount; estimate_nof = JSArray::kMaxArrayLength;
} else { } else {
estimate_nof += element_estimate; estimate_nof += element_estimate;
} }
......
...@@ -9284,7 +9284,7 @@ void CodeStubAssembler::TryLookupElement( ...@@ -9284,7 +9284,7 @@ void CodeStubAssembler::TryLookupElement(
{ {
// Negative and too-large keys must be converted to property names. // Negative and too-large keys must be converted to property names.
if (Is64()) { if (Is64()) {
GotoIf(UintPtrLessThan(IntPtrConstant(JSArray::kMaxArrayIndex), GotoIf(UintPtrLessThan(IntPtrConstant(JSObject::kMaxElementIndex),
intptr_index), intptr_index),
if_bailout); if_bailout);
} else { } else {
...@@ -9323,7 +9323,7 @@ void CodeStubAssembler::TryLookupElement( ...@@ -9323,7 +9323,7 @@ void CodeStubAssembler::TryLookupElement(
// Positive OOB indices mean "not found", negative indices and indices // Positive OOB indices mean "not found", negative indices and indices
// out of array index range must be converted to property names. // out of array index range must be converted to property names.
if (Is64()) { if (Is64()) {
GotoIf(UintPtrLessThan(IntPtrConstant(JSArray::kMaxArrayIndex), GotoIf(UintPtrLessThan(IntPtrConstant(JSObject::kMaxElementIndex),
intptr_index), intptr_index),
if_bailout); if_bailout);
} else { } else {
......
...@@ -1951,8 +1951,10 @@ Reduction JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant( ...@@ -1951,8 +1951,10 @@ Reduction JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant(
// Check whether we're accessing a known element on the {receiver} and can // Check whether we're accessing a known element on the {receiver} and can
// constant-fold the load. // constant-fold the load.
NumberMatcher mkey(key); NumberMatcher mkey(key);
if (mkey.IsInteger() && mkey.IsInRange(0.0, kMaxUInt32 - 1.0)) { if (mkey.IsInteger() &&
uint32_t index = static_cast<uint32_t>(mkey.ResolvedValue()); mkey.IsInRange(0.0, static_cast<double>(JSObject::kMaxElementIndex))) {
STATIC_ASSERT(JSObject::kMaxElementIndex <= kMaxUInt32);
const uint32_t index = static_cast<uint32_t>(mkey.ResolvedValue());
base::Optional<ObjectRef> element; base::Optional<ObjectRef> element;
if (receiver_ref.IsJSObject()) { if (receiver_ref.IsJSObject()) {
......
...@@ -392,7 +392,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase( ...@@ -392,7 +392,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
if (Is64()) { if (Is64()) {
GotoIfNot( GotoIfNot(
UintPtrLessThanOrEqual(var_intptr_index.value(), UintPtrLessThanOrEqual(var_intptr_index.value(),
IntPtrConstant(JSArray::kMaxArrayIndex)), IntPtrConstant(JSObject::kMaxElementIndex)),
miss); miss);
} else { } else {
GotoIf(IntPtrLessThan(var_intptr_index.value(), IntPtrConstant(0)), GotoIf(IntPtrLessThan(var_intptr_index.value(), IntPtrConstant(0)),
...@@ -2137,7 +2137,7 @@ void AccessorAssembler::EmitElementLoad( ...@@ -2137,7 +2137,7 @@ void AccessorAssembler::EmitElementLoad(
{ {
Comment("dictionary elements"); Comment("dictionary elements");
if (Is64()) { if (Is64()) {
GotoIf(UintPtrLessThan(IntPtrConstant(JSArray::kMaxArrayIndex), GotoIf(UintPtrLessThan(IntPtrConstant(JSObject::kMaxElementIndex),
intptr_index), intptr_index),
out_of_bounds); out_of_bounds);
} else { } else {
...@@ -2328,11 +2328,11 @@ void AccessorAssembler::GenericElementLoad( ...@@ -2328,11 +2328,11 @@ void AccessorAssembler::GenericElementLoad(
// without ever checking the prototype chain. // without ever checking the prototype chain.
GotoIf(IsJSTypedArrayInstanceType(lookup_start_object_instance_type), GotoIf(IsJSTypedArrayInstanceType(lookup_start_object_instance_type),
&return_undefined); &return_undefined);
// Positive OOB indices within JSArray index range are effectively the same // Positive OOB indices within elements index range are effectively the same
// as hole loads. Larger keys and negative keys are named loads. // as hole loads. Larger keys and negative keys are named loads.
if (Is64()) { if (Is64()) {
Branch(UintPtrLessThanOrEqual(index, Branch(UintPtrLessThanOrEqual(index,
IntPtrConstant(JSArray::kMaxArrayIndex)), IntPtrConstant(JSObject::kMaxElementIndex)),
&if_element_hole, slow); &if_element_hole, slow);
} else { } else {
Branch(IntPtrLessThan(index, IntPtrConstant(0)), slow, &if_element_hole); Branch(IntPtrLessThan(index, IntPtrConstant(0)), slow, &if_element_hole);
......
...@@ -1301,10 +1301,14 @@ bool IntPtrKeyToSize(intptr_t index, Handle<HeapObject> receiver, size_t* out) { ...@@ -1301,10 +1301,14 @@ bool IntPtrKeyToSize(intptr_t index, Handle<HeapObject> receiver, size_t* out) {
return false; return false;
} }
#if V8_HOST_ARCH_64_BIT #if V8_HOST_ARCH_64_BIT
// On 32-bit platforms, any intptr_t is less than kMaxArrayIndex. if (index > JSObject::kMaxElementIndex && !receiver->IsJSTypedArray()) {
if (index > JSArray::kMaxArrayIndex && !receiver->IsJSTypedArray()) {
return false; return false;
} }
#else
// On 32-bit platforms, any intptr_t is less than kMaxElementIndex.
STATIC_ASSERT(
static_cast<double>(std::numeric_limits<decltype(index)>::max()) <=
static_cast<double>(JSObject::kMaxElementIndex));
#endif #endif
*out = static_cast<size_t>(index); *out = static_cast<size_t>(index);
return true; return true;
......
...@@ -555,8 +555,8 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore( ...@@ -555,8 +555,8 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
// Out-of-capacity accesses (index >= capacity) jump here. Additionally, // Out-of-capacity accesses (index >= capacity) jump here. Additionally,
// an ElementsKind transition might be necessary. // an ElementsKind transition might be necessary.
// The index can also be negative or larger than kMaxArrayIndex at this point! // The index can also be negative or larger than kMaxElementIndex at this
// Jump to the runtime in that case to convert it to a named property. // point! Jump to the runtime in that case to convert it to a named property.
BIND(&if_grow); BIND(&if_grow);
{ {
Comment("Grow backing store"); Comment("Grow backing store");
......
...@@ -126,8 +126,15 @@ class JSArray : public JSObject { ...@@ -126,8 +126,15 @@ class JSArray : public JSObject {
// Max. number of elements being copied in Array builtins. // Max. number of elements being copied in Array builtins.
static const int kMaxCopyElements = 100; static const int kMaxCopyElements = 100;
// Valid array indices range from +0 <= i < 2^32 - 1 (kMaxUInt32).
static constexpr uint32_t kMaxArrayLength = JSObject::kMaxElementCount;
static constexpr uint32_t kMaxArrayIndex = JSObject::kMaxElementIndex;
STATIC_ASSERT(kMaxArrayLength == kMaxUInt32);
STATIC_ASSERT(kMaxArrayIndex == kMaxUInt32 - 1);
// This constant is somewhat arbitrary. Any large enough value would work. // This constant is somewhat arbitrary. Any large enough value would work.
static const uint32_t kMaxFastArrayLength = 32 * 1024 * 1024; static constexpr uint32_t kMaxFastArrayLength = 32 * 1024 * 1024;
STATIC_ASSERT(kMaxFastArrayLength <= kMaxArrayLength);
// Min. stack size for detecting an Array.prototype.join() call cycle. // Min. stack size for detecting an Array.prototype.join() call cycle.
static const uint32_t kMinJoinStackSize = 2; static const uint32_t kMinJoinStackSize = 2;
...@@ -137,9 +144,6 @@ class JSArray : public JSObject { ...@@ -137,9 +144,6 @@ class JSArray : public JSObject {
AllocationMemento::kSize) >> AllocationMemento::kSize) >>
kDoubleSizeLog2; kDoubleSizeLog2;
// Valid array indices range from +0 <= i < 2^32 - 1 (kMaxUInt32).
static const uint32_t kMaxArrayIndex = kMaxUInt32 - 1;
OBJECT_CONSTRUCTORS(JSArray, JSObject); OBJECT_CONSTRUCTORS(JSArray, JSObject);
}; };
......
...@@ -745,7 +745,8 @@ class JSObject : public TorqueGeneratedJSObject<JSObject, JSReceiver> { ...@@ -745,7 +745,8 @@ class JSObject : public TorqueGeneratedJSObject<JSObject, JSReceiver> {
// Maximal number of elements (numbered 0 .. kMaxElementCount - 1). // Maximal number of elements (numbered 0 .. kMaxElementCount - 1).
// Also maximal value of JSArray's length property. // Also maximal value of JSArray's length property.
static const uint32_t kMaxElementCount = 0xffffffffu; static constexpr uint32_t kMaxElementCount = kMaxUInt32;
static constexpr uint32_t kMaxElementIndex = kMaxElementCount - 1;
// Constants for heuristics controlling conversion of fast elements // Constants for heuristics controlling conversion of fast elements
// to slow elements. // to slow elements.
......
...@@ -73,7 +73,7 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, ...@@ -73,7 +73,7 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
if (IsElement()) { if (IsElement()) {
// If we're not looking at a TypedArray, we will need the key represented // If we're not looking at a TypedArray, we will need the key represented
// as an internalized string. // as an internalized string.
if (index_ > JSArray::kMaxArrayIndex && if (index_ > JSObject::kMaxElementIndex &&
!lookup_start_object->IsJSTypedArray()) { !lookup_start_object->IsJSTypedArray()) {
if (name_.is_null()) { if (name_.is_null()) {
name_ = isolate->factory()->SizeToString(index_); name_ = isolate->factory()->SizeToString(index_);
...@@ -106,7 +106,9 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, ...@@ -106,7 +106,9 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
LookupIterator::Key::Key(Isolate* isolate, double index) { LookupIterator::Key::Key(Isolate* isolate, double index) {
DCHECK_EQ(index, static_cast<uint64_t>(index)); DCHECK_EQ(index, static_cast<uint64_t>(index));
#if V8_TARGET_ARCH_32_BIT #if V8_TARGET_ARCH_32_BIT
if (index <= JSArray::kMaxArrayIndex) { if (index <= JSObject::kMaxElementIndex) {
STATIC_ASSERT(JSObject::kMaxElementIndex <=
std::numeric_limits<size_t>::max());
index_ = static_cast<size_t>(index); index_ = static_cast<size_t>(index);
} else { } else {
index_ = LookupIterator::kInvalidIndex; index_ = LookupIterator::kInvalidIndex;
...@@ -165,7 +167,7 @@ Handle<Name> LookupIterator::GetName() { ...@@ -165,7 +167,7 @@ Handle<Name> LookupIterator::GetName() {
} }
bool LookupIterator::IsElement(JSReceiver object) const { bool LookupIterator::IsElement(JSReceiver object) const {
return index_ <= JSArray::kMaxArrayIndex || return index_ <= JSObject::kMaxElementIndex ||
(index_ != kInvalidIndex && object.map().has_typed_array_elements()); (index_ != kInvalidIndex && object.map().has_typed_array_elements());
} }
...@@ -270,7 +272,7 @@ Handle<T> LookupIterator::GetStoreTarget() const { ...@@ -270,7 +272,7 @@ Handle<T> LookupIterator::GetStoreTarget() const {
template <bool is_element> template <bool is_element>
InterceptorInfo LookupIterator::GetInterceptor(JSObject holder) const { InterceptorInfo LookupIterator::GetInterceptor(JSObject holder) const {
if (is_element && index_ <= JSArray::kMaxArrayIndex) { if (is_element && index_ <= JSObject::kMaxElementIndex) {
return holder.GetIndexedInterceptor(isolate_); return holder.GetIndexedInterceptor(isolate_);
} else { } else {
return holder.GetNamedInterceptor(isolate_); return holder.GetNamedInterceptor(isolate_);
......
...@@ -1101,7 +1101,7 @@ namespace { ...@@ -1101,7 +1101,7 @@ namespace {
template <bool is_element> template <bool is_element>
bool HasInterceptor(Map map, size_t index) { bool HasInterceptor(Map map, size_t index) {
if (is_element) { if (is_element) {
if (index > JSArray::kMaxArrayIndex) { if (index > JSObject::kMaxElementIndex) {
// There is currently no way to install interceptors on an object with // There is currently no way to install interceptors on an object with
// typed array elements. // typed array elements.
DCHECK(!map.has_typed_array_elements()); DCHECK(!map.has_typed_array_elements());
......
...@@ -324,7 +324,7 @@ RUNTIME_FUNCTION(Runtime_ObjectHasOwnProperty) { ...@@ -324,7 +324,7 @@ RUNTIME_FUNCTION(Runtime_ObjectHasOwnProperty) {
Map map = js_obj->map(); Map map = js_obj->map();
if (!map.IsJSGlobalProxyMap() && if (!map.IsJSGlobalProxyMap() &&
(key.is_element() && key.index() <= JSArray::kMaxArrayIndex (key.is_element() && key.index() <= JSObject::kMaxElementIndex
? !map.has_indexed_interceptor() ? !map.has_indexed_interceptor()
: !map.has_named_interceptor())) { : !map.has_named_interceptor())) {
return ReadOnlyRoots(isolate).false_value(); return ReadOnlyRoots(isolate).false_value();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment