No longer OOM on invalid string length.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 24 Mar 2014 15:01:17 +0000 (15:01 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 24 Mar 2014 15:01:17 +0000 (15:01 +0000)
R=ishell@chromium.org
BUG=v8:3060
LOG=Y

Review URL: https://codereview.chromium.org/207613005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20202 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

20 files changed:
src/api.cc
src/bootstrapper.cc
src/bootstrapper.h
src/contexts.cc
src/debug.cc
src/factory.cc
src/heap-inl.h
src/heap.cc
src/hydrogen-instructions.cc
src/json-parser.h
src/json-stringifier.h
src/jsregexp.cc
src/liveedit.cc
src/parser.cc
src/runtime.cc
src/uri.h
test/cctest/cctest.status
test/cctest/test-strings.cc
test/mjsunit/string-oom-replace-global-regexp-with-string.js
tools/lexer-shell.cc

index c89b619..31e8193 100644 (file)
@@ -5403,6 +5403,8 @@ inline Local<String> NewString(Isolate* v8_isolate,
   if (length == -1) length = StringLength(data);
   i::Handle<i::String> result = NewString(
       isolate->factory(), type, i::Vector<const Char>(data, length));
+  // We do not expect this to fail. Change this if it does.
+  CHECK(!result.is_null());
   if (type == String::kUndetectableString) {
     result->MarkAsUndetectable();
   }
@@ -5460,8 +5462,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);
+  // We do not expect this to fail. Change this if it does.
+  CHECK(!result.is_null());
   return Utils::ToLocal(result);
 }
 
@@ -5469,14 +5471,22 @@ Local<String> v8::String::Concat(Handle<String> left, Handle<String> right) {
 static i::Handle<i::String> NewExternalStringHandle(
     i::Isolate* isolate,
     v8::String::ExternalStringResource* resource) {
-  return isolate->factory()->NewExternalStringFromTwoByte(resource);
+  i::Handle<i::String> result =
+      isolate->factory()->NewExternalStringFromTwoByte(resource);
+  // We do not expect this to fail. Change this if it does.
+  CHECK(!result.is_null());
+  return result;
 }
 
 
 static i::Handle<i::String> NewExternalAsciiStringHandle(
     i::Isolate* isolate,
     v8::String::ExternalAsciiStringResource* resource) {
-  return isolate->factory()->NewExternalStringFromAscii(resource);
+  i::Handle<i::String> result =
+      isolate->factory()->NewExternalStringFromAscii(resource);
+  // We do not expect this to fail. Change this if it does.
+  CHECK(!result.is_null());
+  return result;
 }
 
 
@@ -6130,6 +6140,8 @@ Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
     if (length == -1) length = i::StrLength(data);
     i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
         i::Vector<const char>(data, length));
+    // We do not expect this to fail. Change this if it does.
+    CHECK(!name.is_null());
     result->set_name(*name);
   }
   return Utils::ToLocal(result);
@@ -6951,8 +6963,8 @@ Handle<String> CpuProfileNode::GetFunctionName() const {
     i::Handle<i::String> cons = isolate->factory()->NewConsString(
         isolate->factory()->InternalizeUtf8String(entry->name_prefix()),
         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);
+    // We do not expect this to fail. Change this if it does.
+    CHECK(!cons.is_null());
     return ToApiHandle<String>(cons);
   }
 }
index 7942c7f..b2a52cd 100644 (file)
@@ -88,6 +88,8 @@ Handle<String> Bootstrapper::NativesSourceLookup(int index) {
                                           source.length());
     Handle<String> source_code =
         isolate_->factory()->NewExternalStringFromAscii(resource);
+    // We do not expect this to throw an exception. Change this if it does.
+    CHECK_NOT_EMPTY_HANDLE(isolate_, source_code);
     heap->natives_source_cache()->set(index, *source_code);
   }
   Handle<Object> cached_source(heap->natives_source_cache()->get(index),
@@ -1463,6 +1465,7 @@ bool Genesis::CompileExperimentalBuiltin(Isolate* isolate, int index) {
   Handle<String> source_code =
       factory->NewStringFromAscii(
           ExperimentalNatives::GetRawScriptSource(index));
+  RETURN_IF_EMPTY_HANDLE_VALUE(isolate, source_code, false);
   return CompileNative(isolate, name, source_code);
 }
 
@@ -1512,6 +1515,7 @@ bool Genesis::CompileScriptCached(Isolate* isolate,
   if (cache == NULL || !cache->Lookup(name, &function_info)) {
     ASSERT(source->IsOneByteRepresentation());
     Handle<String> script_name = factory->NewStringFromUtf8(name);
+    ASSERT(!script_name.is_null());
     function_info = Compiler::CompileScript(
         source,
         script_name,
@@ -2080,8 +2084,10 @@ static Handle<JSObject> ResolveBuiltinIdHolder(
   ASSERT_EQ(".prototype", period_pos);
   Vector<const char> property(holder_expr,
                               static_cast<int>(period_pos - holder_expr));
+  Handle<String> property_string = factory->InternalizeUtf8String(property);
+  ASSERT(!property_string.is_null());
   Handle<JSFunction> function = Handle<JSFunction>::cast(
-      GetProperty(isolate, global, factory->InternalizeUtf8String(property)));
+      GetProperty(isolate, global, property_string));
   return Handle<JSObject>(JSObject::cast(function->prototype()));
 }
 
@@ -2349,6 +2355,8 @@ bool Genesis::InstallExtension(Isolate* isolate,
   }
   Handle<String> source_code =
       isolate->factory()->NewExternalStringFromAscii(extension->source());
+  // We do not expect this to throw an exception. Change this if it does.
+  CHECK_NOT_EMPTY_HANDLE(isolate, source_code);
   bool result = CompileScriptCached(isolate,
                                     CStrVector(extension->name()),
                                     source_code,
index 14dd1bd..e683a45 100644 (file)
@@ -73,6 +73,7 @@ class SourceCodeCache BASE_EMBEDDED {
     cache_->CopyTo(0, *new_array, 0, cache_->length());
     cache_ = *new_array;
     Handle<String> str = factory->NewStringFromAscii(name, TENURED);
+    ASSERT(!str.is_null());
     cache_->set(length, *str);
     cache_->set(length + 1, *shared);
     Script::cast(shared->script())->set_type(Smi::FromInt(type_));
index ada6e04..33d47e9 100644 (file)
@@ -368,7 +368,7 @@ Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
   Handle<Object> result(error_message_for_code_gen_from_strings(),
                         GetIsolate());
   if (!result->IsUndefined()) return result;
-  return GetIsolate()->factory()->NewStringFromAscii(i::CStrVector(
+  return GetIsolate()->factory()->NewStringFromOneByte(STATIC_ASCII_VECTOR(
       "Code generation from strings disallowed for this context"));
 }
 
index c8a99d5..d7667f1 100644 (file)
@@ -754,6 +754,7 @@ bool Debug::CompileDebuggerScript(Isolate* isolate, int index) {
       isolate->bootstrapper()->NativesSourceLookup(index);
   Vector<const char> name = Natives::GetScriptName(index);
   Handle<String> script_name = factory->NewStringFromAscii(name);
+  ASSERT(!script_name.is_null());
   Handle<Context> context = isolate->native_context();
 
   // Compile the script.
@@ -2599,6 +2600,7 @@ Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name,
   // Create the execution state object.
   Handle<String> constructor_str =
       isolate_->factory()->InternalizeUtf8String(constructor_name);
+  ASSERT(!constructor_str.is_null());
   Handle<Object> constructor(
       isolate_->global_object()->GetPropertyNoExceptionThrown(*constructor_str),
       isolate_);
index 5e66202..5c4ac9c 100644 (file)
@@ -289,7 +289,7 @@ Handle<String> Factory::NewStringFromTwoByte(Vector<const uc16> string,
 
 
 Handle<SeqOneByteString> Factory::NewRawOneByteString(int length,
-                                                  PretenureFlag pretenure) {
+                                                      PretenureFlag pretenure) {
   CALL_HEAP_FUNCTION(
       isolate(),
       isolate()->heap()->AllocateRawOneByteString(length, pretenure),
@@ -411,6 +411,7 @@ Handle<String> Factory::NewConsString(Handle<String> left,
     ASSERT(left->IsFlat());
     ASSERT(right->IsFlat());
 
+    STATIC_ASSERT(ConsString::kMinLength <= String::kMaxLength);
     if (is_one_byte) {
       Handle<SeqOneByteString> result = NewRawOneByteString(length);
       DisallowHeapAllocation no_gc;
@@ -496,12 +497,14 @@ Handle<String> Factory::NewProperSubString(Handle<String> str,
   if (!FLAG_string_slices || length < SlicedString::kMinLength) {
     if (str->IsOneByteRepresentation()) {
       Handle<SeqOneByteString> result = NewRawOneByteString(length);
+      ASSERT(!result.is_null());
       uint8_t* dest = result->GetChars();
       DisallowHeapAllocation no_gc;
       String::WriteToFlat(*str, dest, begin, end);
       return result;
     } else {
       Handle<SeqTwoByteString> result = NewRawTwoByteString(length);
+      ASSERT(!result.is_null());
       uc16* dest = result->GetChars();
       DisallowHeapAllocation no_gc;
       String::WriteToFlat(*str, dest, begin, end);
index 7e465d5..063cf30 100644 (file)
@@ -138,7 +138,7 @@ MaybeObject* Heap::AllocateInternalizedStringImpl(
 MaybeObject* Heap::AllocateOneByteInternalizedString(Vector<const uint8_t> str,
                                                      uint32_t hash_field) {
   if (str.length() > String::kMaxLength) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+    return isolate()->ThrowInvalidStringLength();
   }
   // Compute map and object size.
   Map* map = ascii_internalized_string_map();
@@ -171,7 +171,7 @@ MaybeObject* Heap::AllocateOneByteInternalizedString(Vector<const uint8_t> str,
 MaybeObject* Heap::AllocateTwoByteInternalizedString(Vector<const uc16> str,
                                                      uint32_t hash_field) {
   if (str.length() > String::kMaxLength) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+    return isolate()->ThrowInvalidStringLength();
   }
   // Compute map and object size.
   Map* map = internalized_string_map();
index 6b2f8f7..f457883 100644 (file)
@@ -3871,7 +3871,7 @@ MaybeObject* Heap::AllocateExternalStringFromAscii(
     const ExternalAsciiString::Resource* resource) {
   size_t length = resource->length();
   if (length > static_cast<size_t>(String::kMaxLength)) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+    return isolate()->ThrowInvalidStringLength();
   }
 
   Map* map = external_ascii_string_map();
@@ -3893,7 +3893,7 @@ MaybeObject* Heap::AllocateExternalStringFromTwoByte(
     const ExternalTwoByteString::Resource* resource) {
   size_t length = resource->length();
   if (length > static_cast<size_t>(String::kMaxLength)) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+    return isolate()->ThrowInvalidStringLength();
   }
 
   // For small strings we check whether the resource contains only
@@ -4978,8 +4978,8 @@ MaybeObject* Heap::AllocateInternalizedStringImpl(
   int size;
   Map* map;
 
-  if (chars > String::kMaxLength) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+  if (chars < 0 || chars > String::kMaxLength) {
+    return isolate()->ThrowInvalidStringLength();
   }
   if (is_one_byte) {
     map = ascii_internalized_string_map();
@@ -5027,7 +5027,7 @@ MaybeObject* Heap::AllocateInternalizedStringImpl<false>(
 MaybeObject* Heap::AllocateRawOneByteString(int length,
                                             PretenureFlag pretenure) {
   if (length < 0 || length > String::kMaxLength) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+    return isolate()->ThrowInvalidStringLength();
   }
   int size = SeqOneByteString::SizeFor(length);
   ASSERT(size <= SeqOneByteString::kMaxSize);
@@ -5051,7 +5051,7 @@ MaybeObject* Heap::AllocateRawOneByteString(int length,
 MaybeObject* Heap::AllocateRawTwoByteString(int length,
                                             PretenureFlag pretenure) {
   if (length < 0 || length > String::kMaxLength) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+    return isolate()->ThrowInvalidStringLength();
   }
   int size = SeqTwoByteString::SizeFor(length);
   ASSERT(size <= SeqTwoByteString::kMaxSize);
index cfe5f1a..dfa584a 100644 (file)
@@ -3923,9 +3923,15 @@ HInstruction* HStringAdd::New(Zone* zone,
     HConstant* c_right = HConstant::cast(right);
     HConstant* c_left = HConstant::cast(left);
     if (c_left->HasStringValue() && c_right->HasStringValue()) {
-      Handle<String> concat = zone->isolate()->factory()->NewFlatConcatString(
-          c_left->StringValue(), c_right->StringValue());
-      return HConstant::New(zone, context, concat);
+      Handle<String> left_string = c_left->StringValue();
+      Handle<String> right_string = c_right->StringValue();
+      // Prevent possible exception by invalid string length.
+      if (left_string->length() + right_string->length() < String::kMaxLength) {
+        Handle<String> concat = zone->isolate()->factory()->NewFlatConcatString(
+            c_left->StringValue(), c_right->StringValue());
+        ASSERT(!concat.is_null());
+        return HConstant::New(zone, context, concat);
+      }
     }
   }
   return new(zone) HStringAdd(
index 0973589..4c2b479 100644 (file)
@@ -606,6 +606,7 @@ Handle<String> JsonParser<seq_ascii>::SlowScanJsonString(
   int length = Min(max_length, Max(kInitialSpecialStringLength, 2 * count));
   Handle<StringType> seq_string =
       NewRawString<StringType>(factory(), length, pretenure_);
+  ASSERT(!seq_string.is_null());
   // Copy prefix into seq_str.
   SinkChar* dest = seq_string->GetChars();
   String::WriteToFlat(*prefix, dest, start, end);
@@ -793,6 +794,7 @@ Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
   } while (c0_ != '"');
   int length = position_ - beg_pos;
   Handle<String> result = factory()->NewRawOneByteString(length, pretenure_);
+  ASSERT(!result.is_null());
   uint8_t* dest = SeqOneByteString::cast(*result)->GetChars();
   String::WriteToFlat(*source_, dest, beg_pos, position_);
 
index a75b3de..c063b67 100644 (file)
@@ -266,6 +266,7 @@ BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
                            factory_->ToObject(factory_->empty_string()));
   part_length_ = kInitialPartLength;
   current_part_ = factory_->NewRawOneByteString(part_length_);
+  ASSERT(!current_part_.is_null());
   tojson_string_ = factory_->toJSON_string();
   stack_ = factory_->NewJSArray(8);
 }
@@ -309,6 +310,7 @@ MaybeObject* BasicJsonStringifier::StringifyString(Isolate* isolate,
   if (object->IsOneByteRepresentationUnderneath()) {
     Handle<String> result =
         isolate->factory()->NewRawOneByteString(worst_case_length);
+    ASSERT(!result.is_null());
     DisallowHeapAllocation no_gc;
     return StringifyString_<SeqOneByteString>(
         isolate,
@@ -317,6 +319,7 @@ MaybeObject* BasicJsonStringifier::StringifyString(Isolate* isolate,
   } else {
     Handle<String> result =
         isolate->factory()->NewRawTwoByteString(worst_case_length);
+    ASSERT(!result.is_null());
     DisallowHeapAllocation no_gc;
     return StringifyString_<SeqTwoByteString>(
         isolate,
@@ -722,7 +725,6 @@ 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 {
@@ -741,6 +743,7 @@ void BasicJsonStringifier::Extend() {
   } else {
     current_part_ = factory_->NewRawTwoByteString(part_length_);
   }
+  ASSERT(!current_part_.is_null());
   current_index_ = 0;
 }
 
@@ -749,6 +752,7 @@ void BasicJsonStringifier::ChangeEncoding() {
   ShrinkCurrentPart();
   Accumulate();
   current_part_ = factory_->NewRawTwoByteString(part_length_);
+  ASSERT(!current_part_.is_null());
   current_index_ = 0;
   is_ascii_ = false;
 }
index 012c251..830dd7b 100644 (file)
@@ -466,6 +466,7 @@ bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re,
     // Unable to compile regexp.
     Handle<String> error_message =
         isolate->factory()->NewStringFromUtf8(CStrVector(result.error_message));
+    ASSERT(!error_message.is_null());
     CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
     return false;
   }
index a812b75..5eae107 100644 (file)
@@ -2013,8 +2013,8 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations(
       DropActivationsInActiveThread(shared_info_array, result, do_drop);
   if (error_message != NULL) {
     // Add error message as an array extra element.
-    Vector<const char> vector_message(error_message, StrLength(error_message));
-    Handle<String> str = isolate->factory()->NewStringFromAscii(vector_message);
+    Handle<String> str = isolate->factory()->NewStringFromAscii(
+        CStrVector(error_message));
     SetElementSloppy(result, len, str);
   }
   return result;
index 56eec54..c7ea634 100644 (file)
@@ -215,6 +215,7 @@ Handle<String> Parser::LookupCachedSymbol(int symbol_id) {
   Handle<String> result = symbol_cache_.at(symbol_id);
   if (result.is_null()) {
     result = scanner()->AllocateInternalizedString(isolate_);
+    ASSERT(!result.is_null());
     symbol_cache_.at(symbol_id) = result;
     return result;
   }
@@ -615,6 +616,7 @@ void ParserTraits::ReportMessageAt(Scanner::Location source_location,
   Handle<FixedArray> elements = factory->NewFixedArray(args.length());
   for (int i = 0; i < args.length(); i++) {
     Handle<String> arg_string = factory->NewStringFromUtf8(CStrVector(args[i]));
+    ASSERT(!arg_string.is_null());
     elements->set(i, *arg_string);
   }
   Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
@@ -672,7 +674,10 @@ Handle<String> ParserTraits::GetSymbol(Scanner* scanner) {
       parser_->scanner()->LogSymbol(parser_->log_, parser_->position());
     }
   }
-  return parser_->scanner()->AllocateInternalizedString(parser_->isolate_);
+  Handle<String> result =
+      parser_->scanner()->AllocateInternalizedString(parser_->isolate_);
+  ASSERT(!result.is_null());
+  return result;
 }
 
 
@@ -1709,8 +1714,8 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
         return;
       }
       Handle<String> message_string =
-          isolate()->factory()->NewStringFromUtf8(CStrVector("Variable"),
-                                                  TENURED);
+          isolate()->factory()->InternalizeOneByteString(
+              STATIC_ASCII_VECTOR("Variable"));
       Expression* expression =
           NewThrowTypeError(isolate()->factory()->redeclaration_string(),
                             message_string, name);
@@ -3816,6 +3821,7 @@ bool RegExpParser::simple() {
 RegExpTree* RegExpParser::ReportError(Vector<const char> message) {
   failed_ = true;
   *error_ = isolate()->factory()->NewStringFromAscii(message, NOT_TENURED);
+  ASSERT(!error_->is_null());
   // Zip to the end to make sure the no more input is read.
   current_ = kEndMarker;
   next_pos_ = in()->length();
index 89dbb0b..706f1ff 100644 (file)
@@ -3344,8 +3344,7 @@ class ReplacementStringBuilder {
         array_builder_(heap->isolate(), estimated_part_count),
         subject_(subject),
         character_count_(0),
-        is_ascii_(subject->IsOneByteRepresentation()),
-        overflowed_(false) {
+        is_ascii_(subject->IsOneByteRepresentation()) {
     // Require a non-zero initial size. Ensures that doubling the size to
     // extend the array will work.
     ASSERT(estimated_part_count > 0);
@@ -3393,11 +3392,6 @@ class ReplacementStringBuilder {
 
 
   Handle<String> ToString() {
-    if (overflowed_) {
-      heap_->isolate()->ThrowInvalidStringLength();
-      return Handle<String>();
-    }
-
     if (array_builder_.length() == 0) {
       return heap_->isolate()->factory()->empty_string();
     }
@@ -3405,6 +3399,7 @@ class ReplacementStringBuilder {
     Handle<String> joined_string;
     if (is_ascii_) {
       Handle<SeqOneByteString> seq = NewRawOneByteString(character_count_);
+      RETURN_IF_EMPTY_HANDLE_VALUE(heap_->isolate(), seq, Handle<String>());
       DisallowHeapAllocation no_gc;
       uint8_t* char_buffer = seq->GetChars();
       StringBuilderConcatHelper(*subject_,
@@ -3415,6 +3410,7 @@ class ReplacementStringBuilder {
     } else {
       // Non-ASCII.
       Handle<SeqTwoByteString> seq = NewRawTwoByteString(character_count_);
+      RETURN_IF_EMPTY_HANDLE_VALUE(heap_->isolate(), seq, Handle<String>());
       DisallowHeapAllocation no_gc;
       uc16* char_buffer = seq->GetChars();
       StringBuilderConcatHelper(*subject_,
@@ -3429,9 +3425,11 @@ class ReplacementStringBuilder {
 
   void IncrementCharacterCount(int by) {
     if (character_count_ > String::kMaxLength - by) {
-      overflowed_ = true;
+      STATIC_ASSERT(String::kMaxLength < kMaxInt);
+      character_count_ = kMaxInt;
+    } else {
+      character_count_ += by;
     }
-    character_count_ += by;
   }
 
  private:
@@ -3456,7 +3454,6 @@ class ReplacementStringBuilder {
   Handle<String> subject_;
   int character_count_;
   bool is_ascii_;
-  bool overflowed_;
 };
 
 
@@ -3913,10 +3910,13 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalAtomRegExpWithString(
        static_cast<int64_t>(pattern_len)) *
       static_cast<int64_t>(matches) +
       static_cast<int64_t>(subject_len);
-  if (result_len_64 > INT_MAX) {
-    v8::internal::Heap::FatalProcessOutOfMemory("invalid string length", true);
+  int result_len;
+  if (result_len_64 > static_cast<int64_t>(String::kMaxLength)) {
+    STATIC_ASSERT(String::kMaxLength < kMaxInt);
+    result_len = kMaxInt;  // Provoke exception.
+  } else {
+    result_len = static_cast<int>(result_len_64);
   }
-  int result_len = static_cast<int>(result_len_64);
 
   int subject_pos = 0;
   int result_pos = 0;
@@ -3929,6 +3929,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalAtomRegExpWithString(
     result = Handle<ResultSeqString>::cast(
         isolate->factory()->NewRawTwoByteString(result_len));
   }
+  RETURN_IF_EMPTY_HANDLE(isolate, result);
 
   for (int i = 0; i < matches; i++) {
     // Copy non-matched subject content.
@@ -4108,6 +4109,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithEmptyString(
     answer = Handle<ResultSeqString>::cast(
         isolate->factory()->NewRawTwoByteString(new_length));
   }
+  ASSERT(!answer.is_null());
 
   int prev = 0;
   int position = 0;
@@ -6565,7 +6567,7 @@ MUST_USE_RESULT static MaybeObject* ConvertCase(
   if (s->IsOneByteRepresentationUnderneath()) {
     Handle<SeqOneByteString> result =
         isolate->factory()->NewRawOneByteString(length);
-
+    ASSERT(!result.is_null());  // Same length as input.
     DisallowHeapAllocation no_gc;
     String::FlatContent flat_content = s->GetFlatContent();
     ASSERT(flat_content.IsFlat());
@@ -6585,6 +6587,8 @@ MUST_USE_RESULT static MaybeObject* ConvertCase(
   } else {
     result = isolate->factory()->NewRawTwoByteString(length);
   }
+  ASSERT(!result.is_null());  // Same length as input.
+
   MaybeObject* maybe = ConvertCaseHelper(isolate, *s, *result, length, mapping);
   Object* answer;
   if (!maybe->ToObject(&answer)) return maybe;
@@ -6598,6 +6602,7 @@ MUST_USE_RESULT static MaybeObject* ConvertCase(
     if (length < 0) length = -length;
     result = isolate->factory()->NewRawTwoByteString(length);
   }
+  RETURN_IF_EMPTY_HANDLE(isolate, result);
   return ConvertCaseHelper(isolate, *s, *result, length, mapping);
 }
 
@@ -7242,13 +7247,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
     String* element = String::cast(element_obj);
     int increment = element->length();
     if (increment > String::kMaxLength - length) {
-      return isolate->ThrowInvalidStringLength();
+      STATIC_ASSERT(String::kMaxLength < kMaxInt);
+      length = kMaxInt;  // Provoke exception;
+      break;
     }
     length += increment;
   }
 
   Handle<SeqTwoByteString> answer =
       isolate->factory()->NewRawTwoByteString(length);
+  RETURN_IF_EMPTY_HANDLE(isolate, answer);
 
   DisallowHeapAllocation no_gc;
 
@@ -9493,8 +9501,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowMessage) {
   CONVERT_SMI_ARG_CHECKED(message_id, 0);
   const char* message = GetBailoutReason(
       static_cast<BailoutReason>(message_id));
-  Handle<Name> message_handle =
+  Handle<String> message_handle =
       isolate->factory()->NewStringFromAscii(CStrVector(message));
+  RETURN_IF_EMPTY_HANDLE(isolate, message_handle);
   return isolate->Throw(*message_handle);
 }
 
index 81ec0c5..1e73ddd 100644 (file)
--- a/src/uri.h
+++ b/src/uri.h
@@ -127,9 +127,11 @@ Handle<String> URIUnescape::UnescapeSlow(
 
   int dest_position = 0;
   Handle<String> second_part;
+  ASSERT(unescaped_length <= String::kMaxLength);
   if (one_byte) {
     Handle<SeqOneByteString> dest =
         isolate->factory()->NewRawOneByteString(unescaped_length);
+    ASSERT(!dest.is_null());
     DisallowHeapAllocation no_allocation;
     Vector<const Char> vector = GetCharVector<Char>(string);
     for (int i = start_index; i < length; dest_position++) {
@@ -142,6 +144,7 @@ Handle<String> URIUnescape::UnescapeSlow(
   } else {
     Handle<SeqTwoByteString> dest =
         isolate->factory()->NewRawTwoByteString(unescaped_length);
+    ASSERT(!dest.is_null());
     DisallowHeapAllocation no_allocation;
     Vector<const Char> vector = GetCharVector<Char>(string);
     for (int i = start_index; i < length; dest_position++) {
@@ -263,11 +266,7 @@ 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) {
-        AllowHeapAllocation allocate_error_and_return;
-        isolate->ThrowInvalidStringLength();
-        return Handle<String>::null();
-      }
+      if (escaped_length > String::kMaxLength) break;  // Provoke exception.
     }
   }
 
@@ -276,6 +275,7 @@ Handle<String> URIEscape::Escape(Isolate* isolate, Handle<String> string) {
 
   Handle<SeqOneByteString> dest =
       isolate->factory()->NewRawOneByteString(escaped_length);
+  RETURN_IF_EMPTY_HANDLE_VALUE(isolate, dest, Handle<String>());
   int dest_position = 0;
 
   { DisallowHeapAllocation no_allocation;
index 7e5fe2a..d8a427c 100644 (file)
@@ -74,6 +74,7 @@
   'test-api/Threading2': [PASS, ['mode == debug', SLOW]],
   'test-api/Threading3': [PASS, ['mode == debug', SLOW]],
   'test-api/Threading4': [PASS, ['mode == debug', SLOW]],
+  'test-strings/StringOOM*': [PASS, ['mode == debug', SLOW]],
 }],  # ALWAYS
 
 ##############################################################################
index 0f37c3e..9168d86 100644 (file)
@@ -1352,3 +1352,64 @@ TEST(Latin1IgnoreCase) {
     CHECK_EQ(Min(upper, lower), test);
   }
 }
+
+
+class DummyResource: public v8::String::ExternalStringResource {
+ public:
+  virtual const uint16_t* data() const { return NULL; }
+  virtual size_t length() const { return 1 << 30; }
+};
+
+
+class DummyOneByteResource: public v8::String::ExternalOneByteStringResource {
+ public:
+  virtual const char* data() const { return NULL; }
+  virtual size_t length() const { return 1 << 30; }
+};
+
+
+TEST(InvalidExternalString) {
+  CcTest::InitializeVM();
+  LocalContext context;
+  Isolate* isolate = CcTest::i_isolate();
+  { HandleScope scope(isolate);
+    DummyOneByteResource r;
+    CHECK(isolate->factory()->NewExternalStringFromAscii(&r).is_null());
+    CHECK(isolate->has_pending_exception());
+    isolate->clear_pending_exception();
+  }
+
+  { HandleScope scope(isolate);
+    DummyResource r;
+    CHECK(isolate->factory()->NewExternalStringFromTwoByte(&r).is_null());
+    CHECK(isolate->has_pending_exception());
+    isolate->clear_pending_exception();
+  }
+}
+
+
+#define INVALID_STRING_TEST(FUN, TYPE)                                         \
+  TEST(StringOOM##FUN) {                                                       \
+    CcTest::InitializeVM();                                                    \
+    LocalContext context;                                                      \
+    Isolate* isolate = CcTest::i_isolate();                                    \
+    STATIC_ASSERT(String::kMaxLength < kMaxInt);                               \
+    static const int invalid = String::kMaxLength + 1;                         \
+    HandleScope scope(isolate);                                                \
+    Vector<TYPE> dummy = Vector<TYPE>::New(invalid);                           \
+    CHECK(isolate->factory()->FUN(Vector<const TYPE>::cast(dummy)).is_null()); \
+    memset(dummy.start(), 0x20, dummy.length() * sizeof(TYPE));                \
+    CHECK(isolate->has_pending_exception());                                   \
+    isolate->clear_pending_exception();                                        \
+    dummy.Dispose();                                                           \
+  }
+
+INVALID_STRING_TEST(NewStringFromAscii, char)
+INVALID_STRING_TEST(NewStringFromUtf8, char)
+INVALID_STRING_TEST(NewStringFromOneByte, uint8_t)
+INVALID_STRING_TEST(NewStringFromTwoByte, uint16_t)
+INVALID_STRING_TEST(InternalizeOneByteString, uint8_t)
+INVALID_STRING_TEST(InternalizeUtf8String, char)
+INVALID_STRING_TEST(InternalizeTwoByteString, uint16_t)
+
+#undef INVALID_STRING_TEST
index a527ae6..2de0110 100644 (file)
@@ -2,13 +2,25 @@
 // 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() {
+function replace1() {
   a.replace(/./g, b);
 }
 
-assertThrows(replace, RangeError);
+assertThrows(replace1, RangeError);
+
+
+var a = 'a';
+for (var i = 0; i < 16; i++) a += a;
+
+function replace2() {
+  a.replace(/a/g, a);
+}
+
+assertThrows(replace2, RangeError);
index 8c7debc..e2e4a9c 100644 (file)
@@ -68,6 +68,7 @@ class BaselineScanner {
             Vector<const uint16_t>(
                 reinterpret_cast<const uint16_t*>(source_),
                 length / 2));
+        CHECK_NOT_EMPTY_HANDLE(isolate, result);
         stream_ =
             new GenericStringUtf16CharacterStream(result, 0, result->length());
         break;
@@ -75,6 +76,7 @@ class BaselineScanner {
       case LATIN1: {
         Handle<String> result = isolate->factory()->NewStringFromOneByte(
             Vector<const uint8_t>(source_, length));
+        CHECK_NOT_EMPTY_HANDLE(isolate, result);
         stream_ =
             new GenericStringUtf16CharacterStream(result, 0, result->length());
         break;