From 000be4d033e97da05de3da1a88bc2d720f8d7e10 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Thu, 20 Mar 2014 12:27:36 +0000 Subject: [PATCH] Reland "Throw exception on invalid string length instead of OOM." R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/199583007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20119 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/api.cc | 9 ++- src/factory.cc | 4 +- src/func-name-inferrer.cc | 11 ++- src/isolate.cc | 6 ++ src/isolate.h | 1 + src/json-stringifier.h | 35 +++++++-- src/parser.cc | 1 + src/runtime.cc | 91 +++++++++++----------- src/uri.h | 3 +- test/cctest/test-strings.cc | 19 ++--- test/mjsunit/mjsunit.status | 5 +- test/mjsunit/string-oom-array-join.js | 14 ++++ test/mjsunit/string-oom-concat.js | 12 +++ ...string-oom-replace-global-regexp-with-string.js | 14 ++++ ...ring-oom-replace-regexp-global-with-function.js | 14 ++++ test/mjsunit/string-oom-slow-escape.js | 14 ++++ test/mjsunit/string-oom-slow-json-stringify.js | 13 ++++ .../string-oom-slow-replace-one-with-string.js | 12 +++ test/mjsunit/string-oom-slow-to-uppercase.js | 12 +++ 19 files changed, 212 insertions(+), 78 deletions(-) create mode 100644 test/mjsunit/string-oom-array-join.js create mode 100644 test/mjsunit/string-oom-concat.js create mode 100644 test/mjsunit/string-oom-replace-global-regexp-with-string.js create mode 100644 test/mjsunit/string-oom-replace-regexp-global-with-function.js create mode 100644 test/mjsunit/string-oom-slow-escape.js create mode 100644 test/mjsunit/string-oom-slow-json-stringify.js create mode 100644 test/mjsunit/string-oom-slow-replace-one-with-string.js create mode 100644 test/mjsunit/string-oom-slow-to-uppercase.js diff --git a/src/api.cc b/src/api.cc index b94a9d5..402598f 100644 --- a/src/api.cc +++ b/src/api.cc @@ -5476,6 +5476,8 @@ Local v8::String::Concat(Handle left, Handle right) { i::Handle right_string = Utils::OpenHandle(*right); i::Handle result = isolate->factory()->NewConsString(left_string, right_string); + // We do not expect this to throw an exception. Change this if it does. + CHECK_NOT_EMPTY_HANDLE(isolate, result); return Utils::ToLocal(result); } @@ -6967,9 +6969,12 @@ Handle CpuProfileNode::GetFunctionName() const { return ToApiHandle( isolate->factory()->InternalizeUtf8String(entry->name())); } else { - return ToApiHandle(isolate->factory()->NewConsString( + i::Handle cons = isolate->factory()->NewConsString( isolate->factory()->InternalizeUtf8String(entry->name_prefix()), - isolate->factory()->InternalizeUtf8String(entry->name()))); + isolate->factory()->InternalizeUtf8String(entry->name())); + // We do not expect this to throw an exception. Change this if it does. + CHECK_NOT_EMPTY_HANDLE(isolate, cons); + return ToApiHandle(cons); } } diff --git a/src/factory.cc b/src/factory.cc index 55529e5..bc890a2 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -377,9 +377,7 @@ Handle Factory::NewConsString(Handle left, // Make sure that an out of memory exception is thrown if the length // of the new cons string is too large. if (length > String::kMaxLength || length < 0) { - isolate()->context()->mark_out_of_memory(); - V8::FatalProcessOutOfMemory("String concatenation result too large."); - UNREACHABLE(); + isolate()->ThrowInvalidStringLength(); return Handle::null(); } diff --git a/src/func-name-inferrer.cc b/src/func-name-inferrer.cc index 5409a4e..441113b 100644 --- a/src/func-name-inferrer.cc +++ b/src/func-name-inferrer.cc @@ -83,11 +83,14 @@ Handle FuncNameInferrer::MakeNameFromStackHelper(int pos, return MakeNameFromStackHelper(pos + 1, prev); } else { if (prev->length() > 0) { + Handle name = names_stack_.at(pos).name; + if (prev->length() + name->length() + 1 > String::kMaxLength) return prev; Factory* factory = isolate()->factory(); - Handle curr = factory->NewConsString( - factory->dot_string(), names_stack_.at(pos).name); - return MakeNameFromStackHelper(pos + 1, - factory->NewConsString(prev, curr)); + Handle curr = factory->NewConsString(factory->dot_string(), name); + CHECK_NOT_EMPTY_HANDLE(isolate(), curr); + curr = factory->NewConsString(prev, curr); + CHECK_NOT_EMPTY_HANDLE(isolate(), curr); + return MakeNameFromStackHelper(pos + 1, curr); } else { return MakeNameFromStackHelper(pos + 1, names_stack_.at(pos).name); } diff --git a/src/isolate.cc b/src/isolate.cc index 50b402b..fed4b4a 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -951,6 +951,12 @@ Failure* Isolate::ThrowIllegalOperation() { } +Failure* Isolate::ThrowInvalidStringLength() { + return Throw(*factory()->NewRangeError( + "invalid_string_length", HandleVector(NULL, 0))); +} + + void Isolate::ScheduleThrow(Object* exception) { // When scheduling a throw we first throw the exception to get the // error reporting if it is uncaught before rescheduling it. diff --git a/src/isolate.h b/src/isolate.h index d1f7003..0a24404 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -789,6 +789,7 @@ class Isolate { // Return pending location if any or unfilled structure. MessageLocation GetMessageLocation(); Failure* ThrowIllegalOperation(); + Failure* ThrowInvalidStringLength(); // Promote a scheduled exception to pending. Asserts has_scheduled_exception. Failure* PromoteScheduledException(); diff --git a/src/json-stringifier.h b/src/json-stringifier.h index 175b9a1..a75b3de 100644 --- a/src/json-stringifier.h +++ b/src/json-stringifier.h @@ -51,6 +51,8 @@ class BasicJsonStringifier BASE_EMBEDDED { enum Result { UNCHANGED, SUCCESS, EXCEPTION, CIRCULAR, STACK_OVERFLOW }; + void Accumulate(); + void Extend(); void ChangeEncoding(); @@ -178,6 +180,7 @@ class BasicJsonStringifier BASE_EMBEDDED { int current_index_; int part_length_; bool is_ascii_; + bool overflowed_; static const int kJsonEscapeTableEntrySize = 8; static const char* const JsonEscapeTable; @@ -254,7 +257,10 @@ const char* const BasicJsonStringifier::JsonEscapeTable = BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate) - : isolate_(isolate), current_index_(0), is_ascii_(true) { + : isolate_(isolate), + current_index_(0), + is_ascii_(true), + overflowed_(false) { factory_ = isolate_->factory(); accumulator_store_ = Handle::cast( factory_->ToObject(factory_->empty_string())); @@ -269,9 +275,12 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle object) { switch (SerializeObject(object)) { case UNCHANGED: return isolate_->heap()->undefined_value(); - case SUCCESS: + case SUCCESS: { ShrinkCurrentPart(); - return *factory_->NewConsString(accumulator(), current_part_); + Accumulate(); + if (overflowed_) return isolate_->ThrowInvalidStringLength(); + return *accumulator(); + } case CIRCULAR: return isolate_->Throw(*factory_->NewTypeError( "circular_structure", HandleVector(NULL, 0))); @@ -486,7 +495,9 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeGeneric( part_length_ = kInitialPartLength; // Allocate conservatively. Extend(); // Attach current part and allocate new part. // Attach result string to the accumulator. - set_accumulator(factory_->NewConsString(accumulator(), result_string)); + Handle cons = factory_->NewConsString(accumulator(), result_string); + RETURN_IF_EMPTY_HANDLE_VALUE(isolate_, cons, EXCEPTION); + set_accumulator(cons); return SUCCESS; } @@ -708,8 +719,20 @@ void BasicJsonStringifier::ShrinkCurrentPart() { } +void BasicJsonStringifier::Accumulate() { + if (accumulator()->length() + current_part_->length() > String::kMaxLength) { + // Screw it. Simply set the flag and carry on. Throw exception at the end. + // We most likely will trigger a real OOM before even reaching this point. + set_accumulator(factory_->empty_string()); + overflowed_ = true; + } else { + set_accumulator(factory_->NewConsString(accumulator(), current_part_)); + } +} + + void BasicJsonStringifier::Extend() { - set_accumulator(factory_->NewConsString(accumulator(), current_part_)); + Accumulate(); if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) { part_length_ *= kPartLengthGrowthFactor; } @@ -724,7 +747,7 @@ void BasicJsonStringifier::Extend() { void BasicJsonStringifier::ChangeEncoding() { ShrinkCurrentPart(); - set_accumulator(factory_->NewConsString(accumulator(), current_part_)); + Accumulate(); current_part_ = factory_->NewRawTwoByteString(part_length_); current_index_ = 0; is_ascii_ = false; diff --git a/src/parser.cc b/src/parser.cc index 0d1ba00..9fe6a3b 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -2922,6 +2922,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Factory* heap_factory = isolate()->factory(); Handle tempstr = heap_factory->NewConsString(heap_factory->dot_for_string(), name); + RETURN_IF_EMPTY_HANDLE_VALUE(isolate(), tempstr, 0); Handle tempname = heap_factory->InternalizeString(tempstr); Variable* temp = scope_->DeclarationScope()->NewTemporary(tempname); VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); diff --git a/src/runtime.cc b/src/runtime.cc index f3cf01b..b928cd0 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -3337,7 +3337,8 @@ class ReplacementStringBuilder { array_builder_(heap->isolate(), estimated_part_count), subject_(subject), character_count_(0), - is_ascii_(subject->IsOneByteRepresentation()) { + is_ascii_(subject->IsOneByteRepresentation()), + overflowed_(false) { // Require a non-zero initial size. Ensures that doubling the size to // extend the array will work. ASSERT(estimated_part_count > 0); @@ -3385,6 +3386,11 @@ class ReplacementStringBuilder { Handle ToString() { + if (overflowed_) { + heap_->isolate()->ThrowInvalidStringLength(); + return Handle(); + } + if (array_builder_.length() == 0) { return heap_->isolate()->factory()->empty_string(); } @@ -3416,7 +3422,7 @@ class ReplacementStringBuilder { void IncrementCharacterCount(int by) { if (character_count_ > String::kMaxLength - by) { - V8::FatalProcessOutOfMemory("String.replace result too large."); + overflowed_ = true; } character_count_ += by; } @@ -3443,6 +3449,7 @@ class ReplacementStringBuilder { Handle subject_; int character_count_; bool is_ascii_; + bool overflowed_; }; @@ -4041,7 +4048,9 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithString( capture_count, global_cache.LastSuccessfulMatch()); - return *(builder.ToString()); + Handle result = builder.ToString(); + RETURN_IF_EMPTY_HANDLE(isolate, result); + return *result; } @@ -4187,8 +4196,8 @@ Handle StringReplaceOneCharWithString(Isolate* isolate, replace, found, recursion_limit - 1); - if (*found) return isolate->factory()->NewConsString(new_first, second); if (new_first.is_null()) return new_first; + if (*found) return isolate->factory()->NewConsString(new_first, second); Handle new_second = StringReplaceOneCharWithString(isolate, @@ -4197,8 +4206,8 @@ Handle StringReplaceOneCharWithString(Isolate* isolate, replace, found, recursion_limit - 1); - if (*found) return isolate->factory()->NewConsString(first, new_second); if (new_second.is_null()) return new_second; + if (*found) return isolate->factory()->NewConsString(first, new_second); return subject; } else { @@ -4207,6 +4216,7 @@ Handle StringReplaceOneCharWithString(Isolate* isolate, *found = true; Handle first = isolate->factory()->NewSubString(subject, 0, index); Handle cons1 = isolate->factory()->NewConsString(first, replace); + RETURN_IF_EMPTY_HANDLE_VALUE(isolate, cons1, Handle()); Handle second = isolate->factory()->NewSubString(subject, index + 1, subject->length()); return isolate->factory()->NewConsString(cons1, second); @@ -4232,6 +4242,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceOneCharWithString) { &found, kRecursionLimit); if (!result.is_null()) return *result; + if (isolate->has_pending_exception()) return Failure::Exception(); return *StringReplaceOneCharWithString(isolate, FlattenGetString(subject), search, @@ -6232,7 +6243,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_URIEscape) { Handle result = string->IsOneByteRepresentationUnderneath() ? URIEscape::Escape(isolate, source) : URIEscape::Escape(isolate, source); - if (result.is_null()) return Failure::OutOfMemoryException(0x12); + RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } @@ -6366,9 +6377,9 @@ MUST_USE_RESULT static MaybeObject* ConvertCaseHelper( int char_length = mapping->get(current, 0, chars); if (char_length == 0) char_length = 1; current_length += char_length; - if (current_length > Smi::kMaxValue) { - isolate->context()->mark_out_of_memory(); - return Failure::OutOfMemoryException(0x13); + if (current_length > String::kMaxLength) { + AllowHeapAllocation allocate_error_and_return; + return isolate->ThrowInvalidStringLength(); } } // Try again with the real length. Return signed if we need @@ -7023,7 +7034,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringAdd) { CONVERT_ARG_HANDLE_CHECKED(String, str1, 0); CONVERT_ARG_HANDLE_CHECKED(String, str2, 1); isolate->counters()->string_add_runtime()->Increment(); - return *isolate->factory()->NewConsString(str1, str2); + Handle result = isolate->factory()->NewConsString(str1, str2); + RETURN_IF_EMPTY_HANDLE(isolate, result); + return *result; } @@ -7070,10 +7083,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { HandleScope scope(isolate); ASSERT(args.length() == 3); CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); - if (!args[1]->IsSmi()) { - isolate->context()->mark_out_of_memory(); - return Failure::OutOfMemoryException(0x14); - } + if (!args[1]->IsSmi()) return isolate->ThrowInvalidStringLength(); int array_length = args.smi_at(1); CONVERT_ARG_HANDLE_CHECKED(String, special, 2); @@ -7147,8 +7157,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { return isolate->Throw(isolate->heap()->illegal_argument_string()); } if (increment > String::kMaxLength - position) { - isolate->context()->mark_out_of_memory(); - return Failure::OutOfMemoryException(0x15); + return isolate->ThrowInvalidStringLength(); } position += increment; } @@ -7183,20 +7192,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { - SealHandleScope shs(isolate); + HandleScope scope(isolate); ASSERT(args.length() == 3); - CONVERT_ARG_CHECKED(JSArray, array, 0); - if (!args[1]->IsSmi()) { - isolate->context()->mark_out_of_memory(); - return Failure::OutOfMemoryException(0x16); - } + CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); + if (!args[1]->IsSmi()) return isolate->ThrowInvalidStringLength(); int array_length = args.smi_at(1); - CONVERT_ARG_CHECKED(String, separator, 2); + CONVERT_ARG_HANDLE_CHECKED(String, separator, 2); + RUNTIME_ASSERT(array->HasFastObjectElements()); - if (!array->HasFastObjectElements()) { - return isolate->Throw(isolate->heap()->illegal_argument_string()); - } - FixedArray* fixed_array = FixedArray::cast(array->elements()); + Handle fixed_array(FixedArray::cast(array->elements())); if (fixed_array->length() < array_length) { array_length = fixed_array->length(); } @@ -7205,38 +7209,32 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { return isolate->heap()->empty_string(); } else if (array_length == 1) { Object* first = fixed_array->get(0); - if (first->IsString()) return first; + RUNTIME_ASSERT(first->IsString()); + return first; } int separator_length = separator->length(); int max_nof_separators = (String::kMaxLength + separator_length - 1) / separator_length; if (max_nof_separators < (array_length - 1)) { - isolate->context()->mark_out_of_memory(); - return Failure::OutOfMemoryException(0x17); + return isolate->ThrowInvalidStringLength(); } int length = (array_length - 1) * separator_length; for (int i = 0; i < array_length; i++) { Object* element_obj = fixed_array->get(i); - if (!element_obj->IsString()) { - // TODO(1161): handle this case. - return isolate->Throw(isolate->heap()->illegal_argument_string()); - } + RUNTIME_ASSERT(element_obj->IsString()); String* element = String::cast(element_obj); int increment = element->length(); if (increment > String::kMaxLength - length) { - isolate->context()->mark_out_of_memory(); - return Failure::OutOfMemoryException(0x18); + return isolate->ThrowInvalidStringLength(); } length += increment; } - Object* object; - { MaybeObject* maybe_object = - isolate->heap()->AllocateRawTwoByteString(length); - if (!maybe_object->ToObject(&object)) return maybe_object; - } - SeqTwoByteString* answer = SeqTwoByteString::cast(object); + Handle answer = + isolate->factory()->NewRawTwoByteString(length); + + DisallowHeapAllocation no_gc; uc16* sink = answer->GetChars(); #ifdef DEBUG @@ -7244,13 +7242,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { #endif String* first = String::cast(fixed_array->get(0)); + String* seperator_raw = *separator; int first_length = first->length(); String::WriteToFlat(first, sink, 0, first_length); sink += first_length; for (int i = 1; i < array_length; i++) { ASSERT(sink + separator_length <= end); - String::WriteToFlat(separator, sink, 0, separator_length); + String::WriteToFlat(seperator_raw, sink, 0, separator_length); sink += separator_length; String* element = String::cast(fixed_array->get(i)); @@ -7263,7 +7262,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { // Use %_FastAsciiArrayJoin instead. ASSERT(!answer->IsOneByteRepresentation()); - return answer; + return *answer; } template @@ -7364,9 +7363,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { // Throw an exception if the resulting string is too large. See // https://code.google.com/p/chromium/issues/detail?id=336820 // for details. - return isolate->Throw(*isolate->factory()-> - NewRangeError("invalid_string_length", - HandleVector(NULL, 0))); + return isolate->ThrowInvalidStringLength(); } if (is_ascii) { diff --git a/src/uri.h b/src/uri.h index ee1baeb..81ec0c5 100644 --- a/src/uri.h +++ b/src/uri.h @@ -264,7 +264,8 @@ Handle URIEscape::Escape(Isolate* isolate, Handle string) { // We don't allow strings that are longer than a maximal length. ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow. if (escaped_length > String::kMaxLength) { - isolate->context()->mark_out_of_memory(); + AllowHeapAllocation allocate_error_and_return; + isolate->ThrowInvalidStringLength(); return Handle::null(); } } diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc index 129e6cf..8e63cb0 100644 --- a/test/cctest/test-strings.cc +++ b/test/cctest/test-strings.cc @@ -1209,24 +1209,17 @@ TEST(AsciiArrayJoin) { // starting with 'bad', followed by 2^14 times the string s. That means the // total length of the concatenated strings is 2^31 + 3. So on 32bit systems // summing the lengths of the strings (as Smis) overflows and wraps. - static const char* join_causing_out_of_memory = + LocalContext context; + v8::HandleScope scope(CcTest::isolate()); + v8::TryCatch try_catch; + CHECK(CompileRun( "var two_14 = Math.pow(2, 14);" "var two_17 = Math.pow(2, 17);" "var s = Array(two_17 + 1).join('c');" "var a = ['bad'];" "for (var i = 1; i <= two_14; i++) a.push(s);" - "a.join("");"; - - v8::HandleScope scope(CcTest::isolate()); - LocalContext context; - v8::V8::IgnoreOutOfMemoryException(); - v8::Local script = v8::Script::Compile( - v8::String::NewFromUtf8(CcTest::isolate(), join_causing_out_of_memory)); - v8::Local result = script->Run(); - - // Check for out of memory state. - CHECK(result.IsEmpty()); - CHECK(context->HasOutOfMemoryException()); + "a.join("");").IsEmpty()); + CHECK(try_catch.HasCaught()); } diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index bf91796..3263410 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -44,6 +44,7 @@ # When that bug is fixed, revert the expectation to: # Skip long running test in debug and allow it to timeout in release mode. # regress/regress-524: [PASS, TIMEOUT, ['mode == debug', SKIP]], + 'string-oom-slow-*': [PASS, SLOW, ['mode == debug', SKIP]], # This test non-deterministically runs out of memory on Windows ia32. 'regress/regress-crbug-160010': [SKIP], @@ -243,7 +244,7 @@ 'unicode-test': [PASS, ['mode == debug', PASS, FAIL]], # Times out often in release mode on ARM. - 'compiler/regress-stacktrace-methods': [PASS, PASS, ['mode == release', TIMEOUT]], + 'compiler/regress-stacktrace-methods': [PASS, ['mode == release', TIMEOUT]], 'array-splice': [PASS, TIMEOUT], # Long running test. @@ -294,7 +295,7 @@ 'array-constructor': [PASS, ['mode == debug', SKIP]], # Times out often in release mode on MIPS. - 'compiler/regress-stacktrace-methods': [PASS, PASS, ['mode == release', TIMEOUT]], + 'compiler/regress-stacktrace-methods': [PASS, ['mode == release', TIMEOUT]], 'array-splice': [PASS, TIMEOUT], # Long running test. diff --git a/test/mjsunit/string-oom-array-join.js b/test/mjsunit/string-oom-array-join.js new file mode 100644 index 0000000..73758ce --- /dev/null +++ b/test/mjsunit/string-oom-array-join.js @@ -0,0 +1,14 @@ +// Copyright 2014 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. + +var a = "a"; +for (var i = 0; i < 23; i++) a += a; +var b = []; +for (var i = 0; i < (1<<5); i++) b.push(a); + +function join() { + b.join(); +} + +assertThrows(join, RangeError); diff --git a/test/mjsunit/string-oom-concat.js b/test/mjsunit/string-oom-concat.js new file mode 100644 index 0000000..9529c89 --- /dev/null +++ b/test/mjsunit/string-oom-concat.js @@ -0,0 +1,12 @@ +// Copyright 2014 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 concat() { + var a = " "; + for (var i = 0; i < 100; i++) { + a += a; + } +} + +assertThrows(concat, RangeError); diff --git a/test/mjsunit/string-oom-replace-global-regexp-with-string.js b/test/mjsunit/string-oom-replace-global-regexp-with-string.js new file mode 100644 index 0000000..a527ae6 --- /dev/null +++ b/test/mjsunit/string-oom-replace-global-regexp-with-string.js @@ -0,0 +1,14 @@ +// Copyright 2014 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. + +var a = 'a'; +for (var i = 0; i < 5; i++) a += a; +var b = 'b'; +for (var i = 0; i < 23; i++) b += b; + +function replace() { + a.replace(/./g, b); +} + +assertThrows(replace, RangeError); diff --git a/test/mjsunit/string-oom-replace-regexp-global-with-function.js b/test/mjsunit/string-oom-replace-regexp-global-with-function.js new file mode 100644 index 0000000..5555a5f --- /dev/null +++ b/test/mjsunit/string-oom-replace-regexp-global-with-function.js @@ -0,0 +1,14 @@ +// Copyright 2014 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. + +var a = "a"; +for (var i = 0; i < 5; i++) a += a; +var b = "b"; +for (var i = 0; i < 23; i++) b += b; + +function replace() { + a.replace(/a/g, function() { return b }); +} + +assertThrows(replace, RangeError); diff --git a/test/mjsunit/string-oom-slow-escape.js b/test/mjsunit/string-oom-slow-escape.js new file mode 100644 index 0000000..34088c0 --- /dev/null +++ b/test/mjsunit/string-oom-slow-escape.js @@ -0,0 +1,14 @@ +// Copyright 2014 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. + + +var a = '<'; +for (var i = 0; i < 26; i++) a += a; +a = a + a + a; + +function escape_a() { + escape(a); +} + +assertThrows(escape_a, RangeError); diff --git a/test/mjsunit/string-oom-slow-json-stringify.js b/test/mjsunit/string-oom-slow-json-stringify.js new file mode 100644 index 0000000..2f99655 --- /dev/null +++ b/test/mjsunit/string-oom-slow-json-stringify.js @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +var a = 'a'; +for (var i = 0; i < 20; i++) a += a; +for (var i = 0; i < 8; i++) a = [a, a]; + +function stringify() { + JSON.stringify(a); +} + +assertThrows(stringify, RangeError); diff --git a/test/mjsunit/string-oom-slow-replace-one-with-string.js b/test/mjsunit/string-oom-slow-replace-one-with-string.js new file mode 100644 index 0000000..bfcbe0f --- /dev/null +++ b/test/mjsunit/string-oom-slow-replace-one-with-string.js @@ -0,0 +1,12 @@ +// Copyright 2014 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. + +var a = 'a'; +for (var i = 0; i < 27; i++) a += a; + +function replace() { + a.replace('a', a); +} + +assertThrows(replace, RangeError); diff --git a/test/mjsunit/string-oom-slow-to-uppercase.js b/test/mjsunit/string-oom-slow-to-uppercase.js new file mode 100644 index 0000000..79f975b --- /dev/null +++ b/test/mjsunit/string-oom-slow-to-uppercase.js @@ -0,0 +1,12 @@ +// Copyright 2014 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. + +var s = "\u00df"; // ß +for (var i = 0; i < 27; i++) s += s; + +function upper() { + s.toUpperCase(); +} + +assertThrows(upper, RangeError); -- 2.7.4