Commit 9ba80269 authored by yangguo@chromium.org's avatar yangguo@chromium.org

Throw exception on invalid string length instead of OOM.

R=bmeurer@chromium.org
BUG=349329
LOG=Y

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20112 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bfcc117f
...@@ -5475,6 +5475,8 @@ Local<String> v8::String::Concat(Handle<String> left, Handle<String> right) { ...@@ -5475,6 +5475,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> right_string = Utils::OpenHandle(*right);
i::Handle<i::String> result = isolate->factory()->NewConsString(left_string, i::Handle<i::String> result = isolate->factory()->NewConsString(left_string,
right_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); return Utils::ToLocal(result);
} }
...@@ -6966,9 +6968,12 @@ Handle<String> CpuProfileNode::GetFunctionName() const { ...@@ -6966,9 +6968,12 @@ Handle<String> CpuProfileNode::GetFunctionName() const {
return ToApiHandle<String>( return ToApiHandle<String>(
isolate->factory()->InternalizeUtf8String(entry->name())); isolate->factory()->InternalizeUtf8String(entry->name()));
} else { } 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_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);
} }
} }
......
...@@ -377,9 +377,7 @@ Handle<String> Factory::NewConsString(Handle<String> left, ...@@ -377,9 +377,7 @@ Handle<String> Factory::NewConsString(Handle<String> left,
// Make sure that an out of memory exception is thrown if the length // Make sure that an out of memory exception is thrown if the length
// of the new cons string is too large. // of the new cons string is too large.
if (length > String::kMaxLength || length < 0) { if (length > String::kMaxLength || length < 0) {
isolate()->context()->mark_out_of_memory(); isolate()->ThrowInvalidStringLength();
V8::FatalProcessOutOfMemory("String concatenation result too large.");
UNREACHABLE();
return Handle<String>::null(); return Handle<String>::null();
} }
......
...@@ -83,11 +83,14 @@ Handle<String> FuncNameInferrer::MakeNameFromStackHelper(int pos, ...@@ -83,11 +83,14 @@ Handle<String> FuncNameInferrer::MakeNameFromStackHelper(int pos,
return MakeNameFromStackHelper(pos + 1, prev); return MakeNameFromStackHelper(pos + 1, prev);
} else { } else {
if (prev->length() > 0) { 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(); Factory* factory = isolate()->factory();
Handle<String> curr = factory->NewConsString( Handle<String> curr = factory->NewConsString(factory->dot_string(), name);
factory->dot_string(), names_stack_.at(pos).name); CHECK_NOT_EMPTY_HANDLE(isolate(), curr);
return MakeNameFromStackHelper(pos + 1, curr = factory->NewConsString(prev, curr);
factory->NewConsString(prev, curr)); CHECK_NOT_EMPTY_HANDLE(isolate(), curr);
return MakeNameFromStackHelper(pos + 1, curr);
} else { } else {
return MakeNameFromStackHelper(pos + 1, names_stack_.at(pos).name); return MakeNameFromStackHelper(pos + 1, names_stack_.at(pos).name);
} }
......
...@@ -951,6 +951,12 @@ Failure* Isolate::ThrowIllegalOperation() { ...@@ -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) { void Isolate::ScheduleThrow(Object* exception) {
// When scheduling a throw we first throw the exception to get the // When scheduling a throw we first throw the exception to get the
// error reporting if it is uncaught before rescheduling it. // error reporting if it is uncaught before rescheduling it.
......
...@@ -789,6 +789,7 @@ class Isolate { ...@@ -789,6 +789,7 @@ class Isolate {
// Return pending location if any or unfilled structure. // Return pending location if any or unfilled structure.
MessageLocation GetMessageLocation(); MessageLocation GetMessageLocation();
Failure* ThrowIllegalOperation(); Failure* ThrowIllegalOperation();
Failure* ThrowInvalidStringLength();
// Promote a scheduled exception to pending. Asserts has_scheduled_exception. // Promote a scheduled exception to pending. Asserts has_scheduled_exception.
Failure* PromoteScheduledException(); Failure* PromoteScheduledException();
......
...@@ -51,6 +51,8 @@ class BasicJsonStringifier BASE_EMBEDDED { ...@@ -51,6 +51,8 @@ class BasicJsonStringifier BASE_EMBEDDED {
enum Result { UNCHANGED, SUCCESS, EXCEPTION, CIRCULAR, STACK_OVERFLOW }; enum Result { UNCHANGED, SUCCESS, EXCEPTION, CIRCULAR, STACK_OVERFLOW };
void Accumulate();
void Extend(); void Extend();
void ChangeEncoding(); void ChangeEncoding();
...@@ -178,6 +180,7 @@ class BasicJsonStringifier BASE_EMBEDDED { ...@@ -178,6 +180,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
int current_index_; int current_index_;
int part_length_; int part_length_;
bool is_ascii_; bool is_ascii_;
bool overflowed_;
static const int kJsonEscapeTableEntrySize = 8; static const int kJsonEscapeTableEntrySize = 8;
static const char* const JsonEscapeTable; static const char* const JsonEscapeTable;
...@@ -254,7 +257,10 @@ const char* const BasicJsonStringifier::JsonEscapeTable = ...@@ -254,7 +257,10 @@ const char* const BasicJsonStringifier::JsonEscapeTable =
BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate) 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(); factory_ = isolate_->factory();
accumulator_store_ = Handle<JSValue>::cast( accumulator_store_ = Handle<JSValue>::cast(
factory_->ToObject(factory_->empty_string())); factory_->ToObject(factory_->empty_string()));
...@@ -269,9 +275,12 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) { ...@@ -269,9 +275,12 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
switch (SerializeObject(object)) { switch (SerializeObject(object)) {
case UNCHANGED: case UNCHANGED:
return isolate_->heap()->undefined_value(); return isolate_->heap()->undefined_value();
case SUCCESS: case SUCCESS: {
ShrinkCurrentPart(); ShrinkCurrentPart();
return *factory_->NewConsString(accumulator(), current_part_); Accumulate();
if (overflowed_) return isolate_->ThrowInvalidStringLength();
return *accumulator();
}
case CIRCULAR: case CIRCULAR:
return isolate_->Throw(*factory_->NewTypeError( return isolate_->Throw(*factory_->NewTypeError(
"circular_structure", HandleVector<Object>(NULL, 0))); "circular_structure", HandleVector<Object>(NULL, 0)));
...@@ -486,7 +495,9 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeGeneric( ...@@ -486,7 +495,9 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeGeneric(
part_length_ = kInitialPartLength; // Allocate conservatively. part_length_ = kInitialPartLength; // Allocate conservatively.
Extend(); // Attach current part and allocate new part. Extend(); // Attach current part and allocate new part.
// Attach result string to the accumulator. // 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; return SUCCESS;
} }
...@@ -708,8 +719,20 @@ void BasicJsonStringifier::ShrinkCurrentPart() { ...@@ -708,8 +719,20 @@ void BasicJsonStringifier::ShrinkCurrentPart() {
} }
void BasicJsonStringifier::Extend() { 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_)); set_accumulator(factory_->NewConsString(accumulator(), current_part_));
}
}
void BasicJsonStringifier::Extend() {
Accumulate();
if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) { if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
part_length_ *= kPartLengthGrowthFactor; part_length_ *= kPartLengthGrowthFactor;
} }
...@@ -724,7 +747,7 @@ void BasicJsonStringifier::Extend() { ...@@ -724,7 +747,7 @@ void BasicJsonStringifier::Extend() {
void BasicJsonStringifier::ChangeEncoding() { void BasicJsonStringifier::ChangeEncoding() {
ShrinkCurrentPart(); ShrinkCurrentPart();
set_accumulator(factory_->NewConsString(accumulator(), current_part_)); Accumulate();
current_part_ = factory_->NewRawTwoByteString(part_length_); current_part_ = factory_->NewRawTwoByteString(part_length_);
current_index_ = 0; current_index_ = 0;
is_ascii_ = false; is_ascii_ = false;
......
...@@ -2922,6 +2922,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { ...@@ -2922,6 +2922,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
Factory* heap_factory = isolate()->factory(); Factory* heap_factory = isolate()->factory();
Handle<String> tempstr = Handle<String> tempstr =
heap_factory->NewConsString(heap_factory->dot_for_string(), name); heap_factory->NewConsString(heap_factory->dot_for_string(), name);
RETURN_IF_EMPTY_HANDLE_VALUE(isolate(), tempstr, 0);
Handle<String> tempname = heap_factory->InternalizeString(tempstr); Handle<String> tempname = heap_factory->InternalizeString(tempstr);
Variable* temp = scope_->DeclarationScope()->NewTemporary(tempname); Variable* temp = scope_->DeclarationScope()->NewTemporary(tempname);
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
......
...@@ -3330,7 +3330,8 @@ class ReplacementStringBuilder { ...@@ -3330,7 +3330,8 @@ class ReplacementStringBuilder {
array_builder_(heap->isolate(), estimated_part_count), array_builder_(heap->isolate(), estimated_part_count),
subject_(subject), subject_(subject),
character_count_(0), 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 // Require a non-zero initial size. Ensures that doubling the size to
// extend the array will work. // extend the array will work.
ASSERT(estimated_part_count > 0); ASSERT(estimated_part_count > 0);
...@@ -3378,6 +3379,11 @@ class ReplacementStringBuilder { ...@@ -3378,6 +3379,11 @@ class ReplacementStringBuilder {
Handle<String> ToString() { Handle<String> ToString() {
if (overflowed_) {
heap_->isolate()->ThrowInvalidStringLength();
return Handle<String>();
}
if (array_builder_.length() == 0) { if (array_builder_.length() == 0) {
return heap_->isolate()->factory()->empty_string(); return heap_->isolate()->factory()->empty_string();
} }
...@@ -3409,7 +3415,7 @@ class ReplacementStringBuilder { ...@@ -3409,7 +3415,7 @@ class ReplacementStringBuilder {
void IncrementCharacterCount(int by) { void IncrementCharacterCount(int by) {
if (character_count_ > String::kMaxLength - by) { if (character_count_ > String::kMaxLength - by) {
V8::FatalProcessOutOfMemory("String.replace result too large."); overflowed_ = true;
} }
character_count_ += by; character_count_ += by;
} }
...@@ -3436,6 +3442,7 @@ class ReplacementStringBuilder { ...@@ -3436,6 +3442,7 @@ class ReplacementStringBuilder {
Handle<String> subject_; Handle<String> subject_;
int character_count_; int character_count_;
bool is_ascii_; bool is_ascii_;
bool overflowed_;
}; };
...@@ -4034,7 +4041,9 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithString( ...@@ -4034,7 +4041,9 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithString(
capture_count, capture_count,
global_cache.LastSuccessfulMatch()); global_cache.LastSuccessfulMatch());
return *(builder.ToString()); Handle<String> result = builder.ToString();
RETURN_IF_EMPTY_HANDLE(isolate, result);
return *result;
} }
...@@ -4180,8 +4189,8 @@ Handle<String> StringReplaceOneCharWithString(Isolate* isolate, ...@@ -4180,8 +4189,8 @@ Handle<String> StringReplaceOneCharWithString(Isolate* isolate,
replace, replace,
found, found,
recursion_limit - 1); recursion_limit - 1);
if (*found) return isolate->factory()->NewConsString(new_first, second);
if (new_first.is_null()) return new_first; if (new_first.is_null()) return new_first;
if (*found) return isolate->factory()->NewConsString(new_first, second);
Handle<String> new_second = Handle<String> new_second =
StringReplaceOneCharWithString(isolate, StringReplaceOneCharWithString(isolate,
...@@ -4190,8 +4199,8 @@ Handle<String> StringReplaceOneCharWithString(Isolate* isolate, ...@@ -4190,8 +4199,8 @@ Handle<String> StringReplaceOneCharWithString(Isolate* isolate,
replace, replace,
found, found,
recursion_limit - 1); recursion_limit - 1);
if (*found) return isolate->factory()->NewConsString(first, new_second);
if (new_second.is_null()) return new_second; if (new_second.is_null()) return new_second;
if (*found) return isolate->factory()->NewConsString(first, new_second);
return subject; return subject;
} else { } else {
...@@ -4200,6 +4209,7 @@ Handle<String> StringReplaceOneCharWithString(Isolate* isolate, ...@@ -4200,6 +4209,7 @@ Handle<String> StringReplaceOneCharWithString(Isolate* isolate,
*found = true; *found = true;
Handle<String> first = isolate->factory()->NewSubString(subject, 0, index); Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
Handle<String> cons1 = isolate->factory()->NewConsString(first, replace); Handle<String> cons1 = isolate->factory()->NewConsString(first, replace);
RETURN_IF_EMPTY_HANDLE_VALUE(isolate, cons1, Handle<String>());
Handle<String> second = Handle<String> second =
isolate->factory()->NewSubString(subject, index + 1, subject->length()); isolate->factory()->NewSubString(subject, index + 1, subject->length());
return isolate->factory()->NewConsString(cons1, second); return isolate->factory()->NewConsString(cons1, second);
...@@ -4225,6 +4235,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceOneCharWithString) { ...@@ -4225,6 +4235,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceOneCharWithString) {
&found, &found,
kRecursionLimit); kRecursionLimit);
if (!result.is_null()) return *result; if (!result.is_null()) return *result;
if (isolate->has_pending_exception()) return Failure::Exception();
return *StringReplaceOneCharWithString(isolate, return *StringReplaceOneCharWithString(isolate,
FlattenGetString(subject), FlattenGetString(subject),
search, search,
...@@ -6225,7 +6236,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_URIEscape) { ...@@ -6225,7 +6236,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_URIEscape) {
Handle<String> result = string->IsOneByteRepresentationUnderneath() Handle<String> result = string->IsOneByteRepresentationUnderneath()
? URIEscape::Escape<uint8_t>(isolate, source) ? URIEscape::Escape<uint8_t>(isolate, source)
: URIEscape::Escape<uc16>(isolate, source); : URIEscape::Escape<uc16>(isolate, source);
if (result.is_null()) return Failure::OutOfMemoryException(0x12); RETURN_IF_EMPTY_HANDLE(isolate, result);
return *result; return *result;
} }
...@@ -6359,9 +6370,9 @@ MUST_USE_RESULT static MaybeObject* ConvertCaseHelper( ...@@ -6359,9 +6370,9 @@ MUST_USE_RESULT static MaybeObject* ConvertCaseHelper(
int char_length = mapping->get(current, 0, chars); int char_length = mapping->get(current, 0, chars);
if (char_length == 0) char_length = 1; if (char_length == 0) char_length = 1;
current_length += char_length; current_length += char_length;
if (current_length > Smi::kMaxValue) { if (current_length > String::kMaxLength) {
isolate->context()->mark_out_of_memory(); AllowHeapAllocation allocate_error_and_return;
return Failure::OutOfMemoryException(0x13); return isolate->ThrowInvalidStringLength();
} }
} }
// Try again with the real length. Return signed if we need // Try again with the real length. Return signed if we need
...@@ -7016,7 +7027,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringAdd) { ...@@ -7016,7 +7027,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringAdd) {
CONVERT_ARG_HANDLE_CHECKED(String, str1, 0); CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
CONVERT_ARG_HANDLE_CHECKED(String, str2, 1); CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
isolate->counters()->string_add_runtime()->Increment(); 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;
} }
...@@ -7063,10 +7076,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { ...@@ -7063,10 +7076,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
HandleScope scope(isolate); HandleScope scope(isolate);
ASSERT(args.length() == 3); ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
if (!args[1]->IsSmi()) { if (!args[1]->IsSmi()) return isolate->ThrowInvalidStringLength();
isolate->context()->mark_out_of_memory();
return Failure::OutOfMemoryException(0x14);
}
int array_length = args.smi_at(1); int array_length = args.smi_at(1);
CONVERT_ARG_HANDLE_CHECKED(String, special, 2); CONVERT_ARG_HANDLE_CHECKED(String, special, 2);
...@@ -7140,8 +7150,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { ...@@ -7140,8 +7150,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
return isolate->Throw(isolate->heap()->illegal_argument_string()); return isolate->Throw(isolate->heap()->illegal_argument_string());
} }
if (increment > String::kMaxLength - position) { if (increment > String::kMaxLength - position) {
isolate->context()->mark_out_of_memory(); return isolate->ThrowInvalidStringLength();
return Failure::OutOfMemoryException(0x15);
} }
position += increment; position += increment;
} }
...@@ -7176,20 +7185,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { ...@@ -7176,20 +7185,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
SealHandleScope shs(isolate); HandleScope scope(isolate);
ASSERT(args.length() == 3); ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(JSArray, array, 0); CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
if (!args[1]->IsSmi()) { if (!args[1]->IsSmi()) return isolate->ThrowInvalidStringLength();
isolate->context()->mark_out_of_memory();
return Failure::OutOfMemoryException(0x16);
}
int array_length = args.smi_at(1); 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()) { Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()));
return isolate->Throw(isolate->heap()->illegal_argument_string());
}
FixedArray* fixed_array = FixedArray::cast(array->elements());
if (fixed_array->length() < array_length) { if (fixed_array->length() < array_length) {
array_length = fixed_array->length(); array_length = fixed_array->length();
} }
...@@ -7198,38 +7202,32 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { ...@@ -7198,38 +7202,32 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
return isolate->heap()->empty_string(); return isolate->heap()->empty_string();
} else if (array_length == 1) { } else if (array_length == 1) {
Object* first = fixed_array->get(0); Object* first = fixed_array->get(0);
if (first->IsString()) return first; RUNTIME_ASSERT(first->IsString());
return first;
} }
int separator_length = separator->length(); int separator_length = separator->length();
int max_nof_separators = int max_nof_separators =
(String::kMaxLength + separator_length - 1) / separator_length; (String::kMaxLength + separator_length - 1) / separator_length;
if (max_nof_separators < (array_length - 1)) { if (max_nof_separators < (array_length - 1)) {
isolate->context()->mark_out_of_memory(); return isolate->ThrowInvalidStringLength();
return Failure::OutOfMemoryException(0x17);
} }
int length = (array_length - 1) * separator_length; int length = (array_length - 1) * separator_length;
for (int i = 0; i < array_length; i++) { for (int i = 0; i < array_length; i++) {
Object* element_obj = fixed_array->get(i); Object* element_obj = fixed_array->get(i);
if (!element_obj->IsString()) { RUNTIME_ASSERT(element_obj->IsString());
// TODO(1161): handle this case.
return isolate->Throw(isolate->heap()->illegal_argument_string());
}
String* element = String::cast(element_obj); String* element = String::cast(element_obj);
int increment = element->length(); int increment = element->length();
if (increment > String::kMaxLength - length) { if (increment > String::kMaxLength - length) {
isolate->context()->mark_out_of_memory(); return isolate->ThrowInvalidStringLength();
return Failure::OutOfMemoryException(0x18);
} }
length += increment; length += increment;
} }
Object* object; Handle<SeqTwoByteString> answer =
{ MaybeObject* maybe_object = isolate->factory()->NewRawTwoByteString(length);
isolate->heap()->AllocateRawTwoByteString(length);
if (!maybe_object->ToObject(&object)) return maybe_object; DisallowHeapAllocation no_gc;
}
SeqTwoByteString* answer = SeqTwoByteString::cast(object);
uc16* sink = answer->GetChars(); uc16* sink = answer->GetChars();
#ifdef DEBUG #ifdef DEBUG
...@@ -7237,13 +7235,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { ...@@ -7237,13 +7235,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
#endif #endif
String* first = String::cast(fixed_array->get(0)); String* first = String::cast(fixed_array->get(0));
String* seperator_raw = *separator;
int first_length = first->length(); int first_length = first->length();
String::WriteToFlat(first, sink, 0, first_length); String::WriteToFlat(first, sink, 0, first_length);
sink += first_length; sink += first_length;
for (int i = 1; i < array_length; i++) { for (int i = 1; i < array_length; i++) {
ASSERT(sink + separator_length <= end); ASSERT(sink + separator_length <= end);
String::WriteToFlat(separator, sink, 0, separator_length); String::WriteToFlat(seperator_raw, sink, 0, separator_length);
sink += separator_length; sink += separator_length;
String* element = String::cast(fixed_array->get(i)); String* element = String::cast(fixed_array->get(i));
...@@ -7256,7 +7255,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { ...@@ -7256,7 +7255,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
// Use %_FastAsciiArrayJoin instead. // Use %_FastAsciiArrayJoin instead.
ASSERT(!answer->IsOneByteRepresentation()); ASSERT(!answer->IsOneByteRepresentation());
return answer; return *answer;
} }
template <typename Char> template <typename Char>
...@@ -7357,9 +7356,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { ...@@ -7357,9 +7356,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) {
// Throw an exception if the resulting string is too large. See // Throw an exception if the resulting string is too large. See
// https://code.google.com/p/chromium/issues/detail?id=336820 // https://code.google.com/p/chromium/issues/detail?id=336820
// for details. // for details.
return isolate->Throw(*isolate->factory()-> return isolate->ThrowInvalidStringLength();
NewRangeError("invalid_string_length",
HandleVector<Object>(NULL, 0)));
} }
if (is_ascii) { if (is_ascii) {
......
...@@ -264,7 +264,8 @@ Handle<String> URIEscape::Escape(Isolate* isolate, Handle<String> string) { ...@@ -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. // We don't allow strings that are longer than a maximal length.
ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow. ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
if (escaped_length > String::kMaxLength) { if (escaped_length > String::kMaxLength) {
isolate->context()->mark_out_of_memory(); AllowHeapAllocation allocate_error_and_return;
isolate->ThrowInvalidStringLength();
return Handle<String>::null(); return Handle<String>::null();
} }
} }
......
...@@ -1209,24 +1209,17 @@ TEST(AsciiArrayJoin) { ...@@ -1209,24 +1209,17 @@ TEST(AsciiArrayJoin) {
// starting with 'bad', followed by 2^14 times the string s. That means the // 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 // 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. // 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_14 = Math.pow(2, 14);"
"var two_17 = Math.pow(2, 17);" "var two_17 = Math.pow(2, 17);"
"var s = Array(two_17 + 1).join('c');" "var s = Array(two_17 + 1).join('c');"
"var a = ['bad'];" "var a = ['bad'];"
"for (var i = 1; i <= two_14; i++) a.push(s);" "for (var i = 1; i <= two_14; i++) a.push(s);"
"a.join("");"; "a.join("");").IsEmpty());
CHECK(try_catch.HasCaught());
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());
} }
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
# When that bug is fixed, revert the expectation to: # When that bug is fixed, revert the expectation to:
# Skip long running test in debug and allow it to timeout in release mode. # Skip long running test in debug and allow it to timeout in release mode.
# regress/regress-524: [PASS, TIMEOUT, ['mode == debug', SKIP]], # regress/regress-524: [PASS, TIMEOUT, ['mode == debug', SKIP]],
'string-oom-slow-*': [SLOW, ['mode == debug', SKIP]],
# This test non-deterministically runs out of memory on Windows ia32. # This test non-deterministically runs out of memory on Windows ia32.
'regress/regress-crbug-160010': [SKIP], 'regress/regress-crbug-160010': [SKIP],
......
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment