[vm, compiler] Consistently produce OutOfMemoryErrors for large variable-length object allocations.
Bug: https://github.com/dart-lang/sdk/issues/38575 Change-Id: I3f93488511519ac1dca04f91465efad3d2a0c66d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118886 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Aart Bik <ajcbik@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
5f32afdd4c
commit
8a5e2a688b
+11
-2
@@ -287,8 +287,17 @@ DEFINE_NATIVE_ENTRY(OneByteString_splitWithCharCode, 0, 2) {
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(OneByteString_allocate, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Smi, length_obj, arguments->NativeArgAt(0));
|
||||
return OneByteString::New(length_obj.Value(), Heap::kNew);
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, length_obj, arguments->NativeArgAt(0));
|
||||
const int64_t length = length_obj.AsInt64Value();
|
||||
if ((length < 0) || (length > OneByteString::kMaxElements)) {
|
||||
// Assume that negative lengths are the result of wrapping in code in
|
||||
// string_patch.dart.
|
||||
const Instance& exception =
|
||||
Instance::Handle(thread->isolate()->object_store()->out_of_memory());
|
||||
Exceptions::Throw(thread, exception);
|
||||
UNREACHABLE();
|
||||
}
|
||||
return OneByteString::New(static_cast<intptr_t>(length), Heap::kNew);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(OneByteString_allocateFromOneByteList, 0, 3) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/native_entry.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/object_store.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
@@ -189,12 +190,18 @@ DEFINE_NATIVE_ENTRY(TypedData_setRange, 0, 7) {
|
||||
// Argument 0 is type arguments and is ignored.
|
||||
#define TYPED_DATA_NEW(name) \
|
||||
DEFINE_NATIVE_ENTRY(TypedData_##name##_new, 0, 2) { \
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1)); \
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, length, arguments->NativeArgAt(1)); \
|
||||
const intptr_t cid = kTypedData##name##Cid; \
|
||||
const intptr_t len = length.Value(); \
|
||||
const intptr_t max = TypedData::MaxElements(cid); \
|
||||
LengthCheck(len, max); \
|
||||
return TypedData::New(cid, len); \
|
||||
const int64_t len = length.AsInt64Value(); \
|
||||
if (len < 0) { \
|
||||
Exceptions::ThrowRangeError("length", length, 0, max); \
|
||||
} else if (len > max) { \
|
||||
const Instance& exception = Instance::Handle( \
|
||||
zone, thread->isolate()->object_store()->out_of_memory()); \
|
||||
Exceptions::Throw(thread, exception); \
|
||||
} \
|
||||
return TypedData::New(cid, static_cast<intptr_t>(len)); \
|
||||
}
|
||||
|
||||
#define TYPED_DATA_NEW_NATIVE(name) TYPED_DATA_NEW(name)
|
||||
|
||||
@@ -1936,19 +1936,22 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler,
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
// Allocates one-byte string of length 'end - start'. The content is not
|
||||
// initialized.
|
||||
// 'length-reg' (R2) contains tagged length.
|
||||
// Allocates a _OneByteString. The content is not initialized.
|
||||
// 'length-reg' (R2) contains the desired length as a _Smi or _Mint.
|
||||
// Returns new string as tagged pointer in R0.
|
||||
static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
static void TryAllocateOneByteString(Assembler* assembler,
|
||||
Label* ok,
|
||||
Label* failure) {
|
||||
const Register length_reg = R2;
|
||||
Label fail;
|
||||
// _Mint length: call to runtime to produce error.
|
||||
__ BranchIfNotSmi(length_reg, failure);
|
||||
// Negative length: call to runtime to produce error.
|
||||
__ cmp(length_reg, Operand(0));
|
||||
__ b(failure, LT);
|
||||
|
||||
NOT_IN_PRODUCT(__ LoadAllocationStatsAddress(R0, kOneByteStringCid));
|
||||
NOT_IN_PRODUCT(__ MaybeTraceAllocation(R0, failure));
|
||||
__ mov(R8, Operand(length_reg)); // Save the length register.
|
||||
// TODO(koda): Protect against negative length and overflow here.
|
||||
__ SmiUntag(length_reg);
|
||||
const intptr_t fixed_size_plus_alignment_padding =
|
||||
target::String::InstanceSize() +
|
||||
@@ -1962,7 +1965,7 @@ static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
|
||||
// length_reg: allocation size.
|
||||
__ adds(R1, R0, Operand(length_reg));
|
||||
__ b(&fail, CS); // Fail on unsigned overflow.
|
||||
__ b(failure, CS); // Fail on unsigned overflow.
|
||||
|
||||
// Check if the allocation fits into the remaining space.
|
||||
// R0: potential new object start.
|
||||
@@ -1970,7 +1973,7 @@ static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
// R2: allocation size.
|
||||
__ ldr(NOTFP, Address(THR, target::Thread::end_offset()));
|
||||
__ cmp(R1, Operand(NOTFP));
|
||||
__ b(&fail, CS);
|
||||
__ b(failure, CS);
|
||||
|
||||
// Successfully allocated the object(s), now update top to point to
|
||||
// next object start and initialize the object.
|
||||
@@ -2010,9 +2013,6 @@ static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
|
||||
NOT_IN_PRODUCT(__ IncrementAllocationStatsWithSize(R4, R2));
|
||||
__ b(ok);
|
||||
|
||||
__ Bind(&fail);
|
||||
__ b(failure);
|
||||
}
|
||||
|
||||
// Arg0: OneByteString (receiver).
|
||||
@@ -2033,7 +2033,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler,
|
||||
__ b(normal_ir_body, NE); // 'start', 'end' not Smi.
|
||||
|
||||
__ sub(R2, R2, Operand(TMP));
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body);
|
||||
__ Bind(&ok);
|
||||
// R0: new string as tagged pointer.
|
||||
// Copy string.
|
||||
@@ -2092,7 +2092,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldr(R2, Address(SP, 0 * target::kWordSize)); // Length.
|
||||
Label ok;
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body);
|
||||
|
||||
__ Bind(&ok);
|
||||
__ Ret();
|
||||
|
||||
@@ -1999,15 +1999,18 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler,
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Allocates one-byte string of length 'end - start'. The content is not
|
||||
// initialized.
|
||||
// 'length-reg' (R2) contains tagged length.
|
||||
// Allocates a _OneByteString. The content is not initialized.
|
||||
// 'length-reg' (R2) contains the desired length as a _Smi or _Mint.
|
||||
// Returns new string as tagged pointer in R0.
|
||||
static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
static void TryAllocateOneByteString(Assembler* assembler,
|
||||
Label* ok,
|
||||
Label* failure) {
|
||||
const Register length_reg = R2;
|
||||
Label fail;
|
||||
// _Mint length: call to runtime to produce error.
|
||||
__ BranchIfNotSmi(length_reg, failure);
|
||||
// negative length: call to runtime to produce error.
|
||||
__ tbnz(failure, length_reg, compiler::target::kBitsPerWord - 1);
|
||||
|
||||
NOT_IN_PRODUCT(__ MaybeTraceAllocation(kOneByteStringCid, R0, failure));
|
||||
__ mov(R6, length_reg); // Save the length register.
|
||||
// TODO(koda): Protect against negative length and overflow here.
|
||||
@@ -2030,7 +2033,7 @@ static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
|
||||
// length_reg: allocation size.
|
||||
__ adds(R1, R0, Operand(length_reg));
|
||||
__ b(&fail, CS); // Fail on unsigned overflow.
|
||||
__ b(failure, CS); // Fail on unsigned overflow.
|
||||
|
||||
// Check if the allocation fits into the remaining space.
|
||||
// R0: potential new object start.
|
||||
@@ -2038,7 +2041,7 @@ static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
// R2: allocation size.
|
||||
__ ldr(R7, Address(THR, target::Thread::end_offset()));
|
||||
__ cmp(R1, Operand(R7));
|
||||
__ b(&fail, CS);
|
||||
__ b(failure, CS);
|
||||
|
||||
// Successfully allocated the object(s), now update top to point to
|
||||
// next object start and initialize the object.
|
||||
@@ -2072,9 +2075,6 @@ static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
__ StoreIntoObjectNoBarrier(
|
||||
R0, FieldAddress(R0, target::String::length_offset()), R6);
|
||||
__ b(ok);
|
||||
|
||||
__ Bind(&fail);
|
||||
__ b(failure);
|
||||
}
|
||||
|
||||
// Arg0: OneByteString (receiver).
|
||||
@@ -2094,7 +2094,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler,
|
||||
__ BranchIfNotSmi(R3, normal_ir_body); // 'start', 'end' not Smi.
|
||||
|
||||
__ sub(R2, R2, Operand(TMP));
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body);
|
||||
__ Bind(&ok);
|
||||
// R0: new string as tagged pointer.
|
||||
// Copy string.
|
||||
@@ -2155,7 +2155,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler,
|
||||
Label ok;
|
||||
|
||||
__ ldr(R2, Address(SP, 0 * target::kWordSize)); // Length.
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body);
|
||||
|
||||
__ Bind(&ok);
|
||||
__ ret();
|
||||
|
||||
@@ -1951,13 +1951,19 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler,
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Allocates one-byte string of length 'end - start'. The content is not
|
||||
// initialized. 'length-reg' contains tagged length.
|
||||
// Allocates a _OneByteString. The content is not initialized.
|
||||
// 'length-reg' contains the desired length as a _Smi or _Mint.
|
||||
// Returns new string as tagged pointer in EAX.
|
||||
static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
static void TryAllocateOneByteString(Assembler* assembler,
|
||||
Label* ok,
|
||||
Label* failure,
|
||||
Register length_reg) {
|
||||
// _Mint length: call to runtime to produce error.
|
||||
__ BranchIfNotSmi(length_reg, failure);
|
||||
// negative length: call to runtime to produce error.
|
||||
__ cmpl(length_reg, Immediate(0));
|
||||
__ j(LESS, failure);
|
||||
|
||||
NOT_IN_PRODUCT(
|
||||
__ MaybeTraceAllocation(kOneByteStringCid, EAX, failure, false));
|
||||
if (length_reg != EDI) {
|
||||
@@ -2048,7 +2054,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler,
|
||||
__ j(NOT_ZERO, normal_ir_body); // 'start', 'end' not Smi.
|
||||
|
||||
__ subl(EDI, Address(ESP, +kStartIndexOffset));
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body, EDI);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body, EDI);
|
||||
__ Bind(&ok);
|
||||
// EAX: new string as tagged pointer.
|
||||
// Copy string.
|
||||
@@ -2098,7 +2104,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ movl(EDI, Address(ESP, +1 * target::kWordSize)); // Length.
|
||||
Label ok;
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body, EDI);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body, EDI);
|
||||
// EDI: Start address to copy from (untagged).
|
||||
|
||||
__ Bind(&ok);
|
||||
|
||||
@@ -1977,13 +1977,19 @@ void AsmIntrinsifier::OneByteString_getHashCode(Assembler* assembler,
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Allocates one-byte string of length 'end - start'. The content is not
|
||||
// initialized. 'length-reg' contains tagged length.
|
||||
// Allocates a _OneByteString. The content is not initialized.
|
||||
// 'length-reg' contains the desired length as a _Smi or _Mint.
|
||||
// Returns new string as tagged pointer in RAX.
|
||||
static void TryAllocateOnebyteString(Assembler* assembler,
|
||||
static void TryAllocateOneByteString(Assembler* assembler,
|
||||
Label* ok,
|
||||
Label* failure,
|
||||
Register length_reg) {
|
||||
// _Mint length: call to runtime to produce error.
|
||||
__ BranchIfNotSmi(length_reg, failure);
|
||||
// negative length: call to runtime to produce error.
|
||||
__ cmpq(length_reg, Immediate(0));
|
||||
__ j(LESS, failure);
|
||||
|
||||
NOT_IN_PRODUCT(__ MaybeTraceAllocation(kOneByteStringCid, failure, false));
|
||||
if (length_reg != RDI) {
|
||||
__ movq(RDI, length_reg);
|
||||
@@ -2076,7 +2082,7 @@ void AsmIntrinsifier::OneByteString_substringUnchecked(Assembler* assembler,
|
||||
__ j(NOT_ZERO, normal_ir_body); // 'start', 'end' not Smi.
|
||||
|
||||
__ subq(RDI, Address(RSP, +kStartIndexOffset));
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body, RDI);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body, RDI);
|
||||
__ Bind(&ok);
|
||||
// RAX: new string as tagged pointer.
|
||||
// Copy string.
|
||||
@@ -2126,7 +2132,7 @@ void AsmIntrinsifier::OneByteString_allocate(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ movq(RDI, Address(RSP, +1 * target::kWordSize)); // Length.v=
|
||||
Label ok;
|
||||
TryAllocateOnebyteString(assembler, &ok, normal_ir_body, RDI);
|
||||
TryAllocateOneByteString(assembler, &ok, normal_ir_body, RDI);
|
||||
// RDI: Start address to copy from (untagged).
|
||||
|
||||
__ Bind(&ok);
|
||||
|
||||
@@ -1005,12 +1005,6 @@ void Exceptions::ThrowUnsupportedError(const char* msg) {
|
||||
Exceptions::ThrowByType(Exceptions::kUnsupported, args);
|
||||
}
|
||||
|
||||
void Exceptions::ThrowRangeErrorMsg(const char* msg) {
|
||||
const Array& args = Array::Handle(Array::New(1));
|
||||
args.SetAt(0, String::Handle(String::New(msg)));
|
||||
Exceptions::ThrowByType(Exceptions::kRangeMsg, args);
|
||||
}
|
||||
|
||||
void Exceptions::ThrowCompileTimeError(const LanguageError& error) {
|
||||
const Array& args = Array::Handle(Array::New(1));
|
||||
args.SetAt(0, String::Handle(error.FormatMessage()));
|
||||
|
||||
@@ -83,7 +83,6 @@ class Exceptions : AllStatic {
|
||||
const Integer& argument_value,
|
||||
intptr_t expected_from,
|
||||
intptr_t expected_to);
|
||||
DART_NORETURN static void ThrowRangeErrorMsg(const char* msg);
|
||||
DART_NORETURN static void ThrowUnsupportedError(const char* msg);
|
||||
DART_NORETURN static void ThrowCompileTimeError(const LanguageError& error);
|
||||
|
||||
|
||||
@@ -262,6 +262,9 @@ DART_FORCE_INLINE static bool TryAllocate(Thread* thread,
|
||||
intptr_t class_id,
|
||||
intptr_t instance_size,
|
||||
RawObject** result) {
|
||||
ASSERT(instance_size > 0);
|
||||
ASSERT(Utils::IsAligned(instance_size, kObjectAlignment));
|
||||
|
||||
const uword start = thread->top();
|
||||
#ifndef PRODUCT
|
||||
auto table = thread->isolate()->shared_class_table();
|
||||
@@ -269,7 +272,8 @@ DART_FORCE_INLINE static bool TryAllocate(Thread* thread,
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (LIKELY((start + instance_size) < thread->end())) {
|
||||
const intptr_t remaining = thread->end() - start;
|
||||
if (LIKELY(remaining >= instance_size)) {
|
||||
thread->set_top(start + instance_size);
|
||||
#ifndef PRODUCT
|
||||
table->UpdateAllocatedNew(class_id, instance_size);
|
||||
|
||||
@@ -1835,10 +1835,22 @@ TEST_CASE(ArrayLengthNegativeOne) {
|
||||
TEST_CASE(ArrayLengthSmiMin) {
|
||||
TestIllegalArrayLength(kSmiMin);
|
||||
}
|
||||
|
||||
TEST_CASE(ArrayLengthOneTooMany) {
|
||||
const intptr_t kOneTooMany = Array::kMaxElements + 1;
|
||||
ASSERT(kOneTooMany >= 0);
|
||||
TestIllegalArrayLength(kOneTooMany);
|
||||
|
||||
char buffer[1024];
|
||||
Utils::SNPrint(buffer, sizeof(buffer),
|
||||
"main() {\n"
|
||||
" return new List(%" Pd
|
||||
");\n"
|
||||
"}\n",
|
||||
kOneTooMany);
|
||||
Dart_Handle lib = TestCase::LoadTestScript(buffer, NULL);
|
||||
EXPECT_VALID(lib);
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
EXPECT_ERROR(result, "Out of Memory");
|
||||
}
|
||||
|
||||
TEST_CASE(ArrayLengthMaxElements) {
|
||||
@@ -1876,7 +1888,7 @@ static void TestIllegalTypedDataLength(const char* class_name,
|
||||
EXPECT_VALID(lib);
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
Utils::SNPrint(buffer, sizeof(buffer), "%" Pd, length);
|
||||
EXPECT_ERROR(result, "Invalid argument(s)");
|
||||
EXPECT_ERROR(result, "RangeError (length): Invalid value");
|
||||
EXPECT_ERROR(result, buffer);
|
||||
}
|
||||
|
||||
@@ -1890,7 +1902,19 @@ TEST_CASE(Int8ListLengthOneTooMany) {
|
||||
const intptr_t kOneTooMany =
|
||||
TypedData::MaxElements(kTypedDataInt8ArrayCid) + 1;
|
||||
ASSERT(kOneTooMany >= 0);
|
||||
TestIllegalTypedDataLength("Int8List", kOneTooMany);
|
||||
|
||||
char buffer[1024];
|
||||
Utils::SNPrint(buffer, sizeof(buffer),
|
||||
"import 'dart:typed_data';\n"
|
||||
"main() {\n"
|
||||
" return new Int8List(%" Pd
|
||||
");\n"
|
||||
"}\n",
|
||||
kOneTooMany);
|
||||
Dart_Handle lib = TestCase::LoadTestScript(buffer, NULL);
|
||||
EXPECT_VALID(lib);
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
EXPECT_ERROR(result, "Out of Memory");
|
||||
}
|
||||
|
||||
TEST_CASE(Int8ListLengthMaxElements) {
|
||||
|
||||
+22
-22
@@ -248,29 +248,29 @@ DEFINE_RUNTIME_ENTRY(AllocateArray, 2) {
|
||||
args.SetAt(2, String::Handle(zone, String::New("is not an integer")));
|
||||
Exceptions::ThrowByType(Exceptions::kArgumentValue, args);
|
||||
}
|
||||
if (length.IsSmi()) {
|
||||
const intptr_t len = Smi::Cast(length).Value();
|
||||
if (Array::IsValidLength(len)) {
|
||||
const Array& array = Array::Handle(zone, Array::New(len, Heap::kNew));
|
||||
arguments.SetReturn(array);
|
||||
TypeArguments& element_type =
|
||||
TypeArguments::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
// An Array is raw or takes one type argument. However, its type argument
|
||||
// vector may be longer than 1 due to a type optimization reusing the type
|
||||
// argument vector of the instantiator.
|
||||
ASSERT(element_type.IsNull() ||
|
||||
(element_type.Length() >= 1 && element_type.IsInstantiated()));
|
||||
array.SetTypeArguments(element_type); // May be null.
|
||||
return;
|
||||
}
|
||||
const int64_t len = Integer::Cast(length).AsInt64Value();
|
||||
if (len < 0) {
|
||||
// Throw: new RangeError.range(length, 0, Array::kMaxElements, "length");
|
||||
Exceptions::ThrowRangeError("length", Integer::Cast(length), 0,
|
||||
Array::kMaxElements);
|
||||
}
|
||||
// Throw: new RangeError.range(length, 0, Array::kMaxElements, "length");
|
||||
const Array& args = Array::Handle(zone, Array::New(4));
|
||||
args.SetAt(0, length);
|
||||
args.SetAt(1, Integer::Handle(zone, Integer::New(0)));
|
||||
args.SetAt(2, Integer::Handle(zone, Integer::New(Array::kMaxElements)));
|
||||
args.SetAt(3, Symbols::Length());
|
||||
Exceptions::ThrowByType(Exceptions::kRange, args);
|
||||
if (len > Array::kMaxElements) {
|
||||
const Instance& exception = Instance::Handle(
|
||||
zone, thread->isolate()->object_store()->out_of_memory());
|
||||
Exceptions::Throw(thread, exception);
|
||||
}
|
||||
|
||||
const Array& array =
|
||||
Array::Handle(zone, Array::New(static_cast<intptr_t>(len), Heap::kNew));
|
||||
arguments.SetReturn(array);
|
||||
TypeArguments& element_type =
|
||||
TypeArguments::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
// An Array is raw or takes one type argument. However, its type argument
|
||||
// vector may be longer than 1 due to a type optimization reusing the type
|
||||
// argument vector of the instantiator.
|
||||
ASSERT(element_type.IsNull() ||
|
||||
(element_type.Length() >= 1 && element_type.IsInstantiated()));
|
||||
array.SetTypeArguments(element_type); // May be null.
|
||||
}
|
||||
|
||||
// Helper returning the token position of the Dart caller.
|
||||
|
||||
@@ -91,7 +91,8 @@ void testConstructor() {
|
||||
testGrowable(new List<int>.filled(5, null, growable: true));
|
||||
Expect.throwsArgumentError(() => new List<int>(-1), "-1");
|
||||
// There must be limits. Fix this test if we ever allow 2^63 elements.
|
||||
Expect.throwsArgumentError(() => new List<int>(0x7ffffffffffff000), "bignum");
|
||||
Expect.throws(() => new List<int>(0x7ffffffffffff000),
|
||||
(e) => e is OutOfMemoryError || e is ArgumentError, "bignum");
|
||||
Expect.throwsArgumentError(() => new List<int>(null), "null");
|
||||
testThrowsOrTypeError(
|
||||
() => new List([] as Object), // Cast to avoid warning.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
const interestingLengths = <int>[
|
||||
0x3FFFFFFF00000000,
|
||||
0x3FFFFFFFFFFFF000,
|
||||
0x3FFFFFFFFFFFFF00,
|
||||
0x3FFFFFFFFFFFFFF0,
|
||||
0x3FFFFFFFFFFFFFFE,
|
||||
0x3FFFFFFFFFFFFFFF,
|
||||
0x7FFFFFFF00000000,
|
||||
0x7FFFFFFFFFFFF000,
|
||||
0x7FFFFFFFFFFFFF00,
|
||||
0x7FFFFFFFFFFFFFF0,
|
||||
0x7FFFFFFFFFFFFFFE,
|
||||
0x7FFFFFFFFFFFFFFF,
|
||||
];
|
||||
|
||||
main() {
|
||||
for (int interestingLength in interestingLengths) {
|
||||
for (int elementLength in <int>[1, 2, 3, 4, 5, 6, 7, 8, 9]) {
|
||||
print(interestingLength ~/ elementLength);
|
||||
|
||||
Expect.throws(() {
|
||||
var array = new List(interestingLength ~/ elementLength);
|
||||
print(array.first);
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
const interestingLengths = <int>[
|
||||
0x3FFFFFFF00000000,
|
||||
0x3FFFFFFFFFFFFFF0,
|
||||
0x3FFFFFFFFFFFFFFE,
|
||||
0x3FFFFFFFFFFFFFFF,
|
||||
0x7FFFFFFF00000000,
|
||||
0x7FFFFFFFFFFFFFF0,
|
||||
0x7FFFFFFFFFFFFFFE,
|
||||
0x7FFFFFFFFFFFFFFF,
|
||||
];
|
||||
|
||||
main() {
|
||||
for (int interestingLength in interestingLengths) {
|
||||
print(interestingLength);
|
||||
|
||||
Expect.throws(() {
|
||||
var bytearray = new Uint8List(interestingLength);
|
||||
print(bytearray.first);
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
|
||||
Expect.throws(() {
|
||||
var bytearray = new Uint8ClampedList(interestingLength);
|
||||
print(bytearray.first);
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
|
||||
Expect.throws(() {
|
||||
var bytearray = new Int8List(interestingLength);
|
||||
print(bytearray.first);
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
|
||||
Expect.throws(() {
|
||||
var bytearray = new ByteData(interestingLength);
|
||||
print(bytearray.getUint8(0));
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
const interestingLengths = <int>[
|
||||
0x3FFFFFFF00000000,
|
||||
0x3FFFFFFFFFFFFFF0,
|
||||
0x3FFFFFFFFFFFFFFE,
|
||||
0x3FFFFFFFFFFFFFFF,
|
||||
0x7FFFFFFF00000000,
|
||||
0x7FFFFFFFFFFFFFF0,
|
||||
0x7FFFFFFFFFFFFFFE,
|
||||
0x7FFFFFFFFFFFFFFF,
|
||||
];
|
||||
|
||||
main() {
|
||||
for (int interestingLength in interestingLengths) {
|
||||
print(interestingLength);
|
||||
|
||||
Expect.throws(() {
|
||||
var oneByteString = "v";
|
||||
oneByteString *= interestingLength;
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
|
||||
Expect.throws(() {
|
||||
var oneByteString = "v";
|
||||
oneByteString = oneByteString.padLeft(interestingLength);
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
|
||||
Expect.throws(() {
|
||||
var oneByteString = "v";
|
||||
oneByteString = oneByteString.padRight(interestingLength);
|
||||
}, (e) => e is OutOfMemoryError);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user