Revert "[builtins] Fix Array.p.join length overflow and invalid string length handling"
This reverts commit ec969ea3b1
.
Reason for revert: test fails consistently on arm bots.
I can't repro the failure locally, but it does consume ~512MB of memory (for a single string, I think?), so my guess is that the bots don't have enough contiguous address space.
Original change's description:
> [builtins] Fix Array.p.join length overflow and invalid string length handling
>
> - Fixes and simplify allocating the temporary fixed array for ToString-ed elements.
> - When the array size is greater than representable by an intptr, it overflowed into a negative value causing a non-negative assert to fail.
> - Simplify fallback behavior by always allocating a conservatively sized temporary fixed array. Previously, if the array had dictionary elements, the temporary fixed array was sized based on %GetNumberDictionaryNumberOfElements() and then resized when entering the fallback.
>
> - Fixes related invalid string length handling. When the running total of the resulting string length overflowed or exceeded String::kMaxLength, a RangeError is thrown. Previously, this thrown RangeError bypassed JoinStackPop and left the receiver on the stack.
>
> Bug: chromium:897404
> Change-Id: I157b71ef04ab06125a5b1c3454e5ed3713bdb591
> Reviewed-on: https://chromium-review.googlesource.com/c/1293070
> Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#56907}
TBR=danno@chromium.org,peter.wm.wong@gmail.com,jgruber@chromium.org,tebbi@chromium.org
Change-Id: I8ca80bd75833aacc94ccb25ceb82bbc8880991db
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: chromium:897404
Reviewed-on: https://chromium-review.googlesource.com/c/1297471
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56915}
This commit is contained in:
parent
0d75b76c64
commit
7a975d1116
@ -10,9 +10,15 @@ module array {
|
|||||||
extern macro CallJSArrayArrayJoinConcatToSequentialString(
|
extern macro CallJSArrayArrayJoinConcatToSequentialString(
|
||||||
FixedArray, intptr, String, String): String;
|
FixedArray, intptr, String, String): String;
|
||||||
|
|
||||||
extern macro CallArrayJoin(
|
extern macro CallLoadJoinElement(implicit context: Context)(
|
||||||
Context, constexpr bool, JSReceiver, String, Number, Object, Object):
|
LoadJoinElementFn, JSReceiver, Number): Object
|
||||||
String
|
labels IfException(Object);
|
||||||
|
|
||||||
|
extern macro CallConvertToLocaleString(implicit context: Context)(
|
||||||
|
Object, Object, Object): String
|
||||||
|
labels IfException(Object);
|
||||||
|
|
||||||
|
extern macro CallToString(implicit context: Context)(Object): String
|
||||||
labels IfException(Object);
|
labels IfException(Object);
|
||||||
|
|
||||||
builtin LoadJoinElement<T: type>(
|
builtin LoadJoinElement<T: type>(
|
||||||
@ -142,11 +148,11 @@ module array {
|
|||||||
isOneByte: bool;
|
isOneByte: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro BufferInit(len: uintptr, sep: String): Buffer {
|
macro BufferInit(estimatedNonHoleyElements: uintptr, sep: String): Buffer {
|
||||||
const cappedBufferSize: intptr = len > kFixedArrayMaxLength ?
|
const bufferSize: intptr = Signed(estimatedNonHoleyElements + 1);
|
||||||
|
const cappedBufferSize: intptr = bufferSize > kFixedArrayMaxLength ?
|
||||||
FromConstexpr<intptr>(kFixedArrayMaxLength) :
|
FromConstexpr<intptr>(kFixedArrayMaxLength) :
|
||||||
Signed(len);
|
bufferSize;
|
||||||
assert(cappedBufferSize > 0);
|
|
||||||
const fixedArray: FixedArray = AllocateZeroedFixedArray(cappedBufferSize);
|
const fixedArray: FixedArray = AllocateZeroedFixedArray(cappedBufferSize);
|
||||||
const isOneByte: bool = HasOnlyOneByteChars(sep.instanceType);
|
const isOneByte: bool = HasOnlyOneByteChars(sep.instanceType);
|
||||||
return Buffer{fixedArray, 0, 0, isOneByte};
|
return Buffer{fixedArray, 0, 0, isOneByte};
|
||||||
@ -214,14 +220,17 @@ module array {
|
|||||||
macro ArrayJoinImpl(
|
macro ArrayJoinImpl(
|
||||||
context: Context, receiver: JSReceiver, sep: String, lengthNumber: Number,
|
context: Context, receiver: JSReceiver, sep: String, lengthNumber: Number,
|
||||||
useToLocaleString: constexpr bool, locales: Object, options: Object,
|
useToLocaleString: constexpr bool, locales: Object, options: Object,
|
||||||
initialLoadJoinElement: LoadJoinElementFn): String {
|
estimatedNonHoleyElements: uintptr,
|
||||||
|
initialLoadJoinElement: LoadJoinElementFn): String
|
||||||
|
labels IfException(Object) {
|
||||||
const initialMap: Map = receiver.map;
|
const initialMap: Map = receiver.map;
|
||||||
const len: uintptr = Convert<uintptr>(lengthNumber);
|
const len: uintptr = Convert<uintptr>(lengthNumber);
|
||||||
const separatorLength: intptr = sep.length;
|
const separatorLength: intptr = sep.length;
|
||||||
let nofSeparators: intptr = 0;
|
let nofSeparators: intptr = 0;
|
||||||
let loadJoinElements: LoadJoinElementFn = initialLoadJoinElement;
|
let loadJoinElements: LoadJoinElementFn = initialLoadJoinElement;
|
||||||
let buffer: Buffer = BufferInit(len, sep);
|
let buffer: Buffer = BufferInit(estimatedNonHoleyElements, sep);
|
||||||
|
|
||||||
|
if (estimatedNonHoleyElements != 0) {
|
||||||
// 6. Let k be 0.
|
// 6. Let k be 0.
|
||||||
let k: uintptr = 0;
|
let k: uintptr = 0;
|
||||||
|
|
||||||
@ -238,18 +247,26 @@ module array {
|
|||||||
CannotUseSameArrayAccessor(initialMap, lengthNumber, receiver))
|
CannotUseSameArrayAccessor(initialMap, lengthNumber, receiver))
|
||||||
deferred {
|
deferred {
|
||||||
loadJoinElements = LoadJoinElement<GenericElementsAccessor>;
|
loadJoinElements = LoadJoinElement<GenericElementsAccessor>;
|
||||||
|
|
||||||
|
// Join the current buffer into a single string and add it to a
|
||||||
|
// new buffer that the fall back will continue with.
|
||||||
|
const temp: String = BufferJoin(buffer, sep);
|
||||||
|
buffer = BufferInit((len - k + 1), sep);
|
||||||
|
buffer = BufferAdd(buffer, temp, 0, separatorLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// b. Let element be ? Get(O, ! ToString(k)).
|
// b. Let element be ? Get(O, ! ToString(k)).
|
||||||
const element: Object =
|
const element: Object = CallLoadJoinElement(
|
||||||
loadJoinElements(context, receiver, Convert<Number>(k++));
|
loadJoinElements, receiver, Convert<Number>(k++))
|
||||||
|
otherwise IfException;
|
||||||
|
|
||||||
// c. If element is undefined or null, let next be the empty String;
|
// c. If element is undefined or null, let next be the empty String;
|
||||||
// otherwise, let next be ? ToString(element).
|
// otherwise, let next be ? ToString(element).
|
||||||
let next: String;
|
let next: String;
|
||||||
if constexpr (useToLocaleString) {
|
if constexpr (useToLocaleString) {
|
||||||
next = ConvertToLocaleString(context, element, locales, options);
|
next = CallConvertToLocaleString(element, locales, options)
|
||||||
|
otherwise IfException;
|
||||||
if (next == kEmptyString) continue;
|
if (next == kEmptyString) continue;
|
||||||
} else {
|
} else {
|
||||||
typeswitch (element) {
|
typeswitch (element) {
|
||||||
@ -262,7 +279,7 @@ module array {
|
|||||||
}
|
}
|
||||||
case (obj: HeapObject): {
|
case (obj: HeapObject): {
|
||||||
if (IsNullOrUndefined(obj)) continue;
|
if (IsNullOrUndefined(obj)) continue;
|
||||||
next = ToString(context, obj);
|
next = CallToString(obj) otherwise IfException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,6 +288,9 @@ module array {
|
|||||||
buffer = BufferAdd(buffer, next, nofSeparators, separatorLength);
|
buffer = BufferAdd(buffer, next, nofSeparators, separatorLength);
|
||||||
nofSeparators = 0;
|
nofSeparators = 0;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
nofSeparators = Signed(len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Add any separators at the end.
|
// Add any separators at the end.
|
||||||
buffer = BufferAddSeparators(buffer, nofSeparators, separatorLength, true);
|
buffer = BufferAddSeparators(buffer, nofSeparators, separatorLength, true);
|
||||||
@ -281,7 +301,8 @@ module array {
|
|||||||
|
|
||||||
macro ArrayJoin(implicit context: Context)(
|
macro ArrayJoin(implicit context: Context)(
|
||||||
useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String,
|
useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String,
|
||||||
lenNumber: Number, locales: Object, options: Object): Object {
|
lenNumber: Number, locales: Object, options: Object): Object
|
||||||
|
labels IfException(Object) {
|
||||||
const map: Map = receiver.map;
|
const map: Map = receiver.map;
|
||||||
const kind: ElementsKind = map.elements_kind;
|
const kind: ElementsKind = map.elements_kind;
|
||||||
const len: uintptr = Convert<uintptr>(lenNumber);
|
const len: uintptr = Convert<uintptr>(lenNumber);
|
||||||
@ -289,6 +310,7 @@ module array {
|
|||||||
// Estimated number of elements that are not holes. This is conservatively
|
// Estimated number of elements that are not holes. This is conservatively
|
||||||
// defaulted to `len`. When the receiver has dictionary elements, a better
|
// defaulted to `len`. When the receiver has dictionary elements, a better
|
||||||
// estimate can be determined through GetNumberDictionaryNumberOfElements.
|
// estimate can be determined through GetNumberDictionaryNumberOfElements.
|
||||||
|
let estimatedNonHoleyElements: uintptr = len;
|
||||||
let loadJoinElements: LoadJoinElementFn;
|
let loadJoinElements: LoadJoinElementFn;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -301,26 +323,14 @@ module array {
|
|||||||
loadJoinElements = LoadJoinElement<FastSmiOrObjectElements>;
|
loadJoinElements = LoadJoinElement<FastSmiOrObjectElements>;
|
||||||
} else if (IsElementsKindLessThanOrEqual(kind, HOLEY_DOUBLE_ELEMENTS)) {
|
} else if (IsElementsKindLessThanOrEqual(kind, HOLEY_DOUBLE_ELEMENTS)) {
|
||||||
loadJoinElements = LoadJoinElement<FastDoubleElements>;
|
loadJoinElements = LoadJoinElement<FastDoubleElements>;
|
||||||
} else if (kind == DICTIONARY_ELEMENTS)
|
} else if (kind == DICTIONARY_ELEMENTS) {
|
||||||
deferred {
|
|
||||||
const dict: NumberDictionary =
|
const dict: NumberDictionary =
|
||||||
UnsafeCast<NumberDictionary>(array.elements);
|
UnsafeCast<NumberDictionary>(array.elements);
|
||||||
const nofElements: Smi = GetNumberDictionaryNumberOfElements(dict);
|
estimatedNonHoleyElements =
|
||||||
if (nofElements == 0) {
|
Unsigned(Convert<intptr>(GetNumberDictionaryNumberOfElements(dict)))
|
||||||
if (sep == kEmptyString) return kEmptyString;
|
<< 1;
|
||||||
try {
|
|
||||||
const nofSeparators: Smi =
|
|
||||||
Cast<Smi>(lenNumber - 1) otherwise IfNotSmi;
|
|
||||||
return StringRepeat(context, sep, nofSeparators);
|
|
||||||
}
|
|
||||||
label IfNotSmi {
|
|
||||||
ThrowRangeError(context, kInvalidStringLength);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadJoinElements = LoadJoinElement<DictionaryElements>;
|
loadJoinElements = LoadJoinElement<DictionaryElements>;
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto IfSlowPath;
|
goto IfSlowPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,19 +339,8 @@ module array {
|
|||||||
}
|
}
|
||||||
return ArrayJoinImpl(
|
return ArrayJoinImpl(
|
||||||
context, receiver, sep, lenNumber, useToLocaleString, locales, options,
|
context, receiver, sep, lenNumber, useToLocaleString, locales, options,
|
||||||
loadJoinElements);
|
estimatedNonHoleyElements, loadJoinElements)
|
||||||
}
|
otherwise IfException;
|
||||||
|
|
||||||
builtin ArrayJoinWithToLocaleString(
|
|
||||||
context: Context, receiver: JSReceiver, sep: String, len: Number,
|
|
||||||
locales: Object, options: Object): Object {
|
|
||||||
return ArrayJoin(true, receiver, sep, len, locales, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
builtin ArrayJoinWithoutToLocaleString(
|
|
||||||
context: Context, receiver: JSReceiver, sep: String, len: Number,
|
|
||||||
locales: Object, options: Object): Object {
|
|
||||||
return ArrayJoin(false, receiver, sep, len, locales, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Join Stack detects cyclical calls to Array Join builtins
|
// The Join Stack detects cyclical calls to Array Join builtins
|
||||||
@ -473,8 +472,8 @@ module array {
|
|||||||
|
|
||||||
JoinStackPushInline(o) otherwise IfReturnEmpty;
|
JoinStackPushInline(o) otherwise IfReturnEmpty;
|
||||||
|
|
||||||
const result: Object = CallArrayJoin(
|
const result: Object =
|
||||||
context, useToLocaleString, o, sep, len, locales, options)
|
ArrayJoin(useToLocaleString, o, sep, len, locales, options)
|
||||||
otherwise IfException;
|
otherwise IfException;
|
||||||
|
|
||||||
JoinStackPopInline(o);
|
JoinStackPopInline(o);
|
||||||
|
@ -1168,4 +1168,3 @@ extern macro TryIntPtrAdd(intptr, intptr): intptr
|
|||||||
labels IfOverflow;
|
labels IfOverflow;
|
||||||
|
|
||||||
extern builtin ObjectToString(Context, Object): Object;
|
extern builtin ObjectToString(Context, Object): Object;
|
||||||
extern builtin StringRepeat(Context, String, Number): String;
|
|
||||||
|
@ -91,16 +91,43 @@ class ArrayBuiltinsAssembler : public BaseBuiltinsFromDSLAssembler {
|
|||||||
|
|
||||||
// Temporary Torque support for Array.prototype.join().
|
// Temporary Torque support for Array.prototype.join().
|
||||||
// TODO(pwong): Remove this when Torque supports exception handlers.
|
// TODO(pwong): Remove this when Torque supports exception handlers.
|
||||||
TNode<String> CallArrayJoin(TNode<Context> context, bool use_to_locale_string,
|
TNode<Object> CallLoadJoinElement(TNode<Context> context,
|
||||||
TNode<JSReceiver> receiver, TNode<String> sep,
|
TNode<Code> loadJoinElement,
|
||||||
TNode<Number> len, TNode<Object> locales,
|
TNode<JSReceiver> receiver, TNode<Number> k,
|
||||||
TNode<Object> options, Label* if_exception,
|
Label* if_exception,
|
||||||
TVariable<Object>* var_exception) {
|
TVariable<Object>* var_exception) {
|
||||||
Builtins::Name builtin = use_to_locale_string
|
// Calling a specialization of LoadJoinElement (see array-join.tq), requires
|
||||||
? Builtins::kArrayJoinWithToLocaleString
|
// a descriptor. We arbitrarily use one of specialization's descriptor, as
|
||||||
: Builtins::kArrayJoinWithoutToLocaleString;
|
// all specializations share the same interface.
|
||||||
TNode<Object> result =
|
TNode<Object> result = CallStub(
|
||||||
CallBuiltin(builtin, context, receiver, sep, len, locales, options);
|
Builtins::CallableFor(isolate(),
|
||||||
|
Builtins::kLoadJoinElement20ATDictionaryElements)
|
||||||
|
.descriptor(),
|
||||||
|
loadJoinElement, context, receiver, k);
|
||||||
|
GotoIfException(result, if_exception, var_exception);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary Torque support for Array.prototype.join().
|
||||||
|
// TODO(pwong): Remove this when Torque supports exception handlers.
|
||||||
|
TNode<String> CallConvertToLocaleString(TNode<Context> context,
|
||||||
|
TNode<Object> element,
|
||||||
|
TNode<Object> locales,
|
||||||
|
TNode<Object> options,
|
||||||
|
Label* if_exception,
|
||||||
|
TVariable<Object>* var_exception) {
|
||||||
|
TNode<Object> result = CallBuiltin(Builtins::kConvertToLocaleString,
|
||||||
|
context, element, locales, options);
|
||||||
|
GotoIfException(result, if_exception, var_exception);
|
||||||
|
return CAST(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary Torque support for Array.prototype.join().
|
||||||
|
// TODO(pwong): Remove this when Torque supports exception handlers.
|
||||||
|
TNode<String> CallToString(TNode<Context> context, TNode<Object> obj,
|
||||||
|
Label* if_exception,
|
||||||
|
TVariable<Object>* var_exception) {
|
||||||
|
TNode<Object> result = CallBuiltin(Builtins::kToString, context, obj);
|
||||||
GotoIfException(result, if_exception, var_exception);
|
GotoIfException(result, if_exception, var_exception);
|
||||||
return CAST(result);
|
return CAST(result);
|
||||||
}
|
}
|
||||||
|
@ -1257,6 +1257,7 @@ TF_BUILTIN(StringRepeat, StringBuiltinsAssembler) {
|
|||||||
CSA_ASSERT(this, IsString(string));
|
CSA_ASSERT(this, IsString(string));
|
||||||
CSA_ASSERT(this, Word32BinaryNot(IsEmptyString(string)));
|
CSA_ASSERT(this, Word32BinaryNot(IsEmptyString(string)));
|
||||||
CSA_ASSERT(this, TaggedIsPositiveSmi(count));
|
CSA_ASSERT(this, TaggedIsPositiveSmi(count));
|
||||||
|
CSA_ASSERT(this, SmiLessThanOrEqual(count, SmiConstant(String::kMaxLength)));
|
||||||
|
|
||||||
// The string is repeated with the following algorithm:
|
// The string is repeated with the following algorithm:
|
||||||
// let n = count;
|
// let n = count;
|
||||||
|
@ -254,11 +254,7 @@ void CSAGenerator::EmitInstruction(
|
|||||||
out_ << ", &" << var_names[i][j];
|
out_ << ", &" << var_names[i][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (return_type->IsStructType()) {
|
|
||||||
out_ << ").Flatten();\n";
|
|
||||||
} else {
|
|
||||||
out_ << ");\n";
|
out_ << ");\n";
|
||||||
}
|
|
||||||
if (instruction.return_continuation) {
|
if (instruction.return_continuation) {
|
||||||
out_ << " Goto(&" << BlockName(*instruction.return_continuation);
|
out_ << " Goto(&" << BlockName(*instruction.return_continuation);
|
||||||
for (const std::string& value : *stack) {
|
for (const std::string& value : *stack) {
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
// Flags: --allow-natives-syntax
|
|
||||||
|
|
||||||
(function DictionaryStringRepeatFastPath() {
|
|
||||||
const a = new Array(%StringMaxLength());
|
|
||||||
assertTrue(%HasDictionaryElements(a));
|
|
||||||
const sep = '12';
|
|
||||||
assertThrows(() => a.join(sep), RangeError);
|
|
||||||
|
|
||||||
// Verifies cycle detection still works properly after thrown error.
|
|
||||||
assertThrows(() => a.join(sep), RangeError);
|
|
||||||
|
|
||||||
a.length = 3;
|
|
||||||
a[0] = 'a';
|
|
||||||
a[1] = 'b';
|
|
||||||
a[2] = 'c';
|
|
||||||
assertSame('a,b,c', a.join());
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function SeparatorOverflow() {
|
|
||||||
const a = ['a',,,,,'b'];
|
|
||||||
|
|
||||||
const sep = ','.repeat(%StringMaxLength());
|
|
||||||
assertThrows(() => a.join(sep), RangeError);
|
|
||||||
|
|
||||||
// Verifies cycle detection still works properly after thrown error.
|
|
||||||
assertThrows(() => a.join(sep), RangeError);
|
|
||||||
assertSame('a,,,,,b', a.join());
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function ElementOverflow() {
|
|
||||||
const el = ','.repeat(%StringMaxLength());
|
|
||||||
const a = [el, el, el, el, el];
|
|
||||||
|
|
||||||
assertThrows(() => a.join(), RangeError);
|
|
||||||
|
|
||||||
// Verifies cycle detection still works properly after thrown error.
|
|
||||||
assertThrows(() => a.join(), RangeError);
|
|
||||||
a[0] = 'a';
|
|
||||||
a[1] = 'b';
|
|
||||||
a[2] = 'c';
|
|
||||||
a[3] = 'd';
|
|
||||||
a[4] = 'e';
|
|
||||||
assertSame('a,b,c,d,e', a.join());
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function ElementSeparatorOverflow() {
|
|
||||||
const el = ','.repeat(%StringMaxLength());
|
|
||||||
const a = [el, el, el, el];
|
|
||||||
|
|
||||||
assertThrows(() => a.join(el), RangeError);
|
|
||||||
|
|
||||||
// Verifies cycle detection still works properly after thrown error.
|
|
||||||
assertThrows(() => a.join(el), RangeError);
|
|
||||||
a[0] = 'a';
|
|
||||||
a[1] = 'b';
|
|
||||||
a[2] = 'c';
|
|
||||||
a[3] = 'd';
|
|
||||||
assertSame('a,b,c,d', a.join());
|
|
||||||
})();
|
|
@ -570,10 +570,6 @@ test(function() {
|
|||||||
"a".repeat(1 << 30);
|
"a".repeat(1 << 30);
|
||||||
}, "Invalid string length", RangeError);
|
}, "Invalid string length", RangeError);
|
||||||
|
|
||||||
test(function() {
|
|
||||||
new Array(1 << 30).join();
|
|
||||||
}, "Invalid string length", RangeError);
|
|
||||||
|
|
||||||
// kNormalizationForm
|
// kNormalizationForm
|
||||||
test(function() {
|
test(function() {
|
||||||
"".normalize("ABC");
|
"".normalize("ABC");
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
function TestError() {}
|
|
||||||
|
|
||||||
// Force slow, generic assess path that will always allocate a temporary fixed
|
|
||||||
// array.
|
|
||||||
String.prototype.__defineGetter__(0, function() { });
|
|
||||||
|
|
||||||
const a = new Array(2**32 - 1);
|
|
||||||
|
|
||||||
// Force early exit to avoid an unreasonably long test.
|
|
||||||
a[0] = {
|
|
||||||
toString() { throw new TestError(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Verify join throws test error and does not fail due to asserts (Negative
|
|
||||||
// length fixed array allocation).
|
|
||||||
assertThrows(() => a.join(), TestError);
|
|
Loading…
Reference in New Issue
Block a user