Reland "Throw exception on invalid string length instead of OOM."
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 20 Mar 2014 12:27:36 +0000 (12:27 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 20 Mar 2014 12:27:36 +0000 (12:27 +0000)
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

19 files changed:
src/api.cc
src/factory.cc
src/func-name-inferrer.cc
src/isolate.cc
src/isolate.h
src/json-stringifier.h
src/parser.cc
src/runtime.cc
src/uri.h
test/cctest/test-strings.cc
test/mjsunit/mjsunit.status
test/mjsunit/string-oom-array-join.js [new file with mode: 0644]
test/mjsunit/string-oom-concat.js [new file with mode: 0644]
test/mjsunit/string-oom-replace-global-regexp-with-string.js [new file with mode: 0644]
test/mjsunit/string-oom-replace-regexp-global-with-function.js [new file with mode: 0644]
test/mjsunit/string-oom-slow-escape.js [new file with mode: 0644]
test/mjsunit/string-oom-slow-json-stringify.js [new file with mode: 0644]
test/mjsunit/string-oom-slow-replace-one-with-string.js [new file with mode: 0644]
test/mjsunit/string-oom-slow-to-uppercase.js [new file with mode: 0644]

index b94a9d5..402598f 100644 (file)
@@ -5476,6 +5476,8 @@ Local<String> v8::String::Concat(Handle<String> left, Handle<String> right) {
   i::Handle<i::String> right_string = Utils::OpenHandle(*right);
   i::Handle<i::String> 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<String> CpuProfileNode::GetFunctionName() const {
     return ToApiHandle<String>(
         isolate->factory()->InternalizeUtf8String(entry->name()));
   } else {
-    return ToApiHandle<String>(isolate->factory()->NewConsString(
+    i::Handle<i::String> 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<String>(cons);
   }
 }
 
index 55529e5..bc890a2 100644 (file)
@@ -377,9 +377,7 @@ Handle<String> Factory::NewConsString(Handle<String> 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<String>::null();
   }
 
index 5409a4e..441113b 100644 (file)
@@ -83,11 +83,14 @@ Handle<String> FuncNameInferrer::MakeNameFromStackHelper(int pos,
     return MakeNameFromStackHelper(pos + 1, prev);
   } else {
     if (prev->length() > 0) {
+      Handle<String> name = names_stack_.at(pos).name;
+      if (prev->length() + name->length() + 1 > String::kMaxLength) return prev;
       Factory* factory = isolate()->factory();
-      Handle<String> curr = factory->NewConsString(
-          factory->dot_string(), names_stack_.at(pos).name);
-      return MakeNameFromStackHelper(pos + 1,
-                                     factory->NewConsString(prev, curr));
+      Handle<String> 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);
     }
index 50b402b..fed4b4a 100644 (file)
@@ -951,6 +951,12 @@ Failure* Isolate::ThrowIllegalOperation() {
 }
 
 
+Failure* Isolate::ThrowInvalidStringLength() {
+  return Throw(*factory()->NewRangeError(
+      "invalid_string_length", HandleVector<Object>(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.
index d1f7003..0a24404 100644 (file)
@@ -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();
index 175b9a1..a75b3de 100644 (file)
@@ -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<JSValue>::cast(
                            factory_->ToObject(factory_->empty_string()));
@@ -269,9 +275,12 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> 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<Object>(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<String> 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;
index 0d1ba00..9fe6a3b 100644 (file)
@@ -2922,6 +2922,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
         Factory* heap_factory = isolate()->factory();
         Handle<String> tempstr =
             heap_factory->NewConsString(heap_factory->dot_for_string(), name);
+        RETURN_IF_EMPTY_HANDLE_VALUE(isolate(), tempstr, 0);
         Handle<String> tempname = heap_factory->InternalizeString(tempstr);
         Variable* temp = scope_->DeclarationScope()->NewTemporary(tempname);
         VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
index f3cf01b..b928cd0 100644 (file)
@@ -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<String> ToString() {
+    if (overflowed_) {
+      heap_->isolate()->ThrowInvalidStringLength();
+      return Handle<String>();
+    }
+
     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<String> 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<String> result = builder.ToString();
+  RETURN_IF_EMPTY_HANDLE(isolate, result);
+  return *result;
 }
 
 
@@ -4187,8 +4196,8 @@ Handle<String> 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<String> new_second =
         StringReplaceOneCharWithString(isolate,
@@ -4197,8 +4206,8 @@ Handle<String> 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<String> StringReplaceOneCharWithString(Isolate* isolate,
     *found = true;
     Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
     Handle<String> cons1 = isolate->factory()->NewConsString(first, replace);
+    RETURN_IF_EMPTY_HANDLE_VALUE(isolate, cons1, Handle<String>());
     Handle<String> 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<String> result = string->IsOneByteRepresentationUnderneath()
       ? URIEscape::Escape<uint8_t>(isolate, source)
       : URIEscape::Escape<uc16>(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<String> 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<FixedArray> 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<SeqTwoByteString> 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 <typename Char>
@@ -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<Object>(NULL, 0)));
+    return isolate->ThrowInvalidStringLength();
   }
 
   if (is_ascii) {
index ee1baeb..81ec0c5 100644 (file)
--- a/src/uri.h
+++ b/src/uri.h
@@ -264,7 +264,8 @@ Handle<String> URIEscape::Escape(Isolate* isolate, Handle<String> 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<String>::null();
       }
     }
index 129e6cf..8e63cb0 100644 (file)
@@ -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<v8::Script> script = v8::Script::Compile(
-      v8::String::NewFromUtf8(CcTest::isolate(), join_causing_out_of_memory));
-  v8::Local<v8::Value> result = script->Run();
-
-  // Check for out of memory state.
-  CHECK(result.IsEmpty());
-  CHECK(context->HasOutOfMemoryException());
+      "a.join("");").IsEmpty());
+  CHECK(try_catch.HasCaught());
 }
 
 
index bf91796..3263410 100644 (file)
@@ -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],
   '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.
   '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 (file)
index 0000000..73758ce
--- /dev/null
@@ -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 (file)
index 0000000..9529c89
--- /dev/null
@@ -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 (file)
index 0000000..a527ae6
--- /dev/null
@@ -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 (file)
index 0000000..5555a5f
--- /dev/null
@@ -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 (file)
index 0000000..34088c0
--- /dev/null
@@ -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 (file)
index 0000000..2f99655
--- /dev/null
@@ -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 (file)
index 0000000..bfcbe0f
--- /dev/null
@@ -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 (file)
index 0000000..79f975b
--- /dev/null
@@ -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);