Commit 99cb4d35 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[cleanup] Harden the SubString CSA/Runtime implementations.

Remove the self-healing for invalid parameters in the
CodeStubAssembler::SubString helper and the %SubString runtime function,
which is used as a fallback for the CodeStubAssembler implementation.
All call sites must do appropriate parameter validation anyways now that
the self-hosted JavaScript builtins using these helpers are gone, and we
have proper contracts with the uses.

Also remove the context parameter from the CodeStubAssembler::SubString
method, which is unnecessary, since this can no longer throw an
exception.

Bug: v8:5269, v8:6936, v8:7109, v8:7137
Change-Id: I19d93bad5f41faa0561c4561a48f78fcba99a549
Reviewed-on: https://chromium-review.googlesource.com/795720Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49702}
parent 8eac5a7c
...@@ -152,7 +152,7 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( ...@@ -152,7 +152,7 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
// Calculate the substring of the first match before creating the result array // Calculate the substring of the first match before creating the result array
// to avoid an unnecessary write barrier storing the first result. // to avoid an unnecessary write barrier storing the first result.
Node* const first = SubString(context, string, start, end); Node* const first = SubString(string, start, end);
Node* const result = Node* const result =
AllocateRegExpResult(context, num_results, start, string); AllocateRegExpResult(context, num_results, start, string);
...@@ -188,7 +188,7 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( ...@@ -188,7 +188,7 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1));
Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1);
Node* const capture = SubString(context, string, start, end); Node* const capture = SubString(string, start, end);
StoreFixedArrayElement(result_elements, to_cursor, capture); StoreFixedArrayElement(result_elements, to_cursor, capture);
Goto(&next_iter); Goto(&next_iter);
...@@ -2008,7 +2008,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, ...@@ -2008,7 +2008,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
Node* const match_to = LoadFixedArrayElement( Node* const match_to = LoadFixedArrayElement(
match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
Node* match = SubString(context, string, match_from, match_to); Node* match = SubString(string, match_from, match_to);
var_match.Bind(match); var_match.Bind(match);
Goto(&if_didmatch); Goto(&if_didmatch);
...@@ -2430,7 +2430,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, ...@@ -2430,7 +2430,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
Node* const from = last_matched_until; Node* const from = last_matched_until;
Node* const to = match_from; Node* const to = match_from;
Node* const substr = SubString(context, string, from, to); Node* const substr = SubString(string, from, to);
array.Push(substr); array.Push(substr);
GotoIf(WordEqual(array.length(), int_limit), &out); GotoIf(WordEqual(array.length(), int_limit), &out);
...@@ -2469,7 +2469,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, ...@@ -2469,7 +2469,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
BIND(&select_capture); BIND(&select_capture);
{ {
Node* const substr = SubString(context, string, from, to); Node* const substr = SubString(string, from, to);
var_value.Bind(substr); var_value.Bind(substr);
Goto(&store_value); Goto(&store_value);
} }
...@@ -2507,7 +2507,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, ...@@ -2507,7 +2507,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
Node* const from = var_last_matched_until.value(); Node* const from = var_last_matched_until.value();
Node* const to = string_length; Node* const to = string_length;
Node* const substr = SubString(context, string, from, to); Node* const substr = SubString(string, from, to);
array.Push(substr); array.Push(substr);
Goto(&out); Goto(&out);
...@@ -2893,7 +2893,7 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( ...@@ -2893,7 +2893,7 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
// TODO(jgruber): We could skip many of the checks that using SubString // TODO(jgruber): We could skip many of the checks that using SubString
// here entails. // here entails.
Node* const first_part = Node* const first_part =
SubString(context, string, var_last_match_end.value(), match_start); SubString(string, var_last_match_end.value(), match_start);
Node* const result = StringAdd(context, var_result.value(), first_part); Node* const result = StringAdd(context, var_result.value(), first_part);
var_result.Bind(result); var_result.Bind(result);
...@@ -2903,7 +2903,7 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( ...@@ -2903,7 +2903,7 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
BIND(&if_replaceisnotempty); BIND(&if_replaceisnotempty);
{ {
Node* const first_part = Node* const first_part =
SubString(context, string, var_last_match_end.value(), match_start); SubString(string, var_last_match_end.value(), match_start);
Node* result = StringAdd(context, var_result.value(), first_part); Node* result = StringAdd(context, var_result.value(), first_part);
result = StringAdd(context, result, replace_string); result = StringAdd(context, result, replace_string);
...@@ -2932,7 +2932,7 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( ...@@ -2932,7 +2932,7 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
{ {
TNode<Smi> const string_length = LoadStringLengthAsSmi(string); TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
Node* const last_part = Node* const last_part =
SubString(context, string, var_last_match_end.value(), string_length); SubString(string, var_last_match_end.value(), string_length);
Node* const result = StringAdd(context, var_result.value(), last_part); Node* const result = StringAdd(context, var_result.value(), last_part);
var_result.Bind(result); var_result.Bind(result);
Goto(&out); Goto(&out);
......
...@@ -1773,8 +1773,7 @@ TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) { ...@@ -1773,8 +1773,7 @@ TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
GotoIf(SmiLessThanOrEqual(var_end.value(), var_start.value()), GotoIf(SmiLessThanOrEqual(var_end.value(), var_start.value()),
&return_emptystring); &return_emptystring);
Node* const result = Node* const result =
SubString(context, subject_string, var_start.value(), var_end.value(), SubString(subject_string, var_start.value(), var_end.value());
SubStringFlags::FROM_TO_ARE_BOUNDED);
args.PopAndReturn(result); args.PopAndReturn(result);
} }
...@@ -1972,7 +1971,7 @@ TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) { ...@@ -1972,7 +1971,7 @@ TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
BIND(&out); BIND(&out);
{ {
TNode<Smi> const end = SmiAdd(var_start, var_result_length); TNode<Smi> const end = SmiAdd(var_start, var_result_length);
Node* const result = SubString(context, string, var_start, end); Node* const result = SubString(string, var_start, end);
args.PopAndReturn(result); args.PopAndReturn(result);
} }
} }
...@@ -2027,12 +2026,11 @@ TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd( ...@@ -2027,12 +2026,11 @@ TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
} }
TF_BUILTIN(SubString, CodeStubAssembler) { TF_BUILTIN(SubString, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* string = Parameter(Descriptor::kString); Node* string = Parameter(Descriptor::kString);
Node* from = Parameter(Descriptor::kFrom); Node* from = Parameter(Descriptor::kFrom);
Node* to = Parameter(Descriptor::kTo); Node* to = Parameter(Descriptor::kTo);
Return(SubString(context, string, from, to)); Return(SubString(string, from, to));
} }
// ES6 #sec-string.prototype.substring // ES6 #sec-string.prototype.substring
...@@ -2085,8 +2083,7 @@ TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) { ...@@ -2085,8 +2083,7 @@ TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
BIND(&out); BIND(&out);
{ {
Node* result = Node* result = SubString(string, var_start.value(), var_end.value());
SubString(context, string, var_start.value(), var_end.value());
args.PopAndReturn(result); args.PopAndReturn(result);
} }
} }
...@@ -2140,9 +2137,8 @@ void StringTrimAssembler::Generate(String::TrimMode mode, ...@@ -2140,9 +2137,8 @@ void StringTrimAssembler::Generate(String::TrimMode mode,
IntPtrConstant(-1), -1, &return_emptystring); IntPtrConstant(-1), -1, &return_emptystring);
} }
arguments.PopAndReturn(SubString(context, string, SmiTag(var_start), arguments.PopAndReturn(SubString(string, SmiTag(var_start),
SmiAdd(SmiTag(var_end), SmiConstant(1)), SmiAdd(SmiTag(var_end), SmiConstant(1))));
SubStringFlags::FROM_TO_ARE_BOUNDED));
BIND(&if_runtime); BIND(&if_runtime);
arguments.PopAndReturn( arguments.PopAndReturn(
......
...@@ -4636,8 +4636,8 @@ Node* CodeStubAssembler::StringFromCharCode(Node* code) { ...@@ -4636,8 +4636,8 @@ Node* CodeStubAssembler::StringFromCharCode(Node* code) {
// |from_string| must be a sequential string. // |from_string| must be a sequential string.
// 0 <= |from_index| <= |from_index| + |character_count| < from_string.length. // 0 <= |from_index| <= |from_index| + |character_count| < from_string.length.
Node* CodeStubAssembler::AllocAndCopyStringCharacters( Node* CodeStubAssembler::AllocAndCopyStringCharacters(
Node* context, Node* from, Node* from_instance_type, Node* from, Node* from_instance_type, TNode<IntPtrT> from_index,
TNode<IntPtrT> from_index, TNode<Smi> character_count) { TNode<Smi> character_count) {
Label end(this), one_byte_sequential(this), two_byte_sequential(this); Label end(this), one_byte_sequential(this), two_byte_sequential(this);
Variable var_result(this, MachineRepresentation::kTagged); Variable var_result(this, MachineRepresentation::kTagged);
...@@ -4647,7 +4647,8 @@ Node* CodeStubAssembler::AllocAndCopyStringCharacters( ...@@ -4647,7 +4647,8 @@ Node* CodeStubAssembler::AllocAndCopyStringCharacters(
// The subject string is a sequential one-byte string. // The subject string is a sequential one-byte string.
BIND(&one_byte_sequential); BIND(&one_byte_sequential);
{ {
Node* result = AllocateSeqOneByteString(context, character_count); Node* result =
AllocateSeqOneByteString(NoContextConstant(), character_count);
CopyStringCharacters(from, result, from_index, IntPtrConstant(0), CopyStringCharacters(from, result, from_index, IntPtrConstant(0),
SmiUntag(character_count), String::ONE_BYTE_ENCODING, SmiUntag(character_count), String::ONE_BYTE_ENCODING,
String::ONE_BYTE_ENCODING); String::ONE_BYTE_ENCODING);
...@@ -4659,7 +4660,8 @@ Node* CodeStubAssembler::AllocAndCopyStringCharacters( ...@@ -4659,7 +4660,8 @@ Node* CodeStubAssembler::AllocAndCopyStringCharacters(
// The subject string is a sequential two-byte string. // The subject string is a sequential two-byte string.
BIND(&two_byte_sequential); BIND(&two_byte_sequential);
{ {
Node* result = AllocateSeqTwoByteString(context, character_count); Node* result =
AllocateSeqTwoByteString(NoContextConstant(), character_count);
CopyStringCharacters(from, result, from_index, IntPtrConstant(0), CopyStringCharacters(from, result, from_index, IntPtrConstant(0),
SmiUntag(character_count), String::TWO_BYTE_ENCODING, SmiUntag(character_count), String::TWO_BYTE_ENCODING,
String::TWO_BYTE_ENCODING); String::TWO_BYTE_ENCODING);
...@@ -4672,11 +4674,7 @@ Node* CodeStubAssembler::AllocAndCopyStringCharacters( ...@@ -4672,11 +4674,7 @@ Node* CodeStubAssembler::AllocAndCopyStringCharacters(
return var_result.value(); return var_result.value();
} }
Node* CodeStubAssembler::SubString(Node* string, Node* from, Node* to) {
Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Node* to, SubStringFlags flags) {
DCHECK(flags == SubStringFlags::NONE ||
flags == SubStringFlags::FROM_TO_ARE_BOUNDED);
VARIABLE(var_result, MachineRepresentation::kTagged); VARIABLE(var_result, MachineRepresentation::kTagged);
ToDirectStringAssembler to_direct(state(), string); ToDirectStringAssembler to_direct(state(), string);
Label end(this), runtime(this); Label end(this), runtime(this);
...@@ -4686,14 +4684,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -4686,14 +4684,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
CSA_ASSERT(this, IsString(string)); CSA_ASSERT(this, IsString(string));
// Make sure that both from and to are non-negative smis. // Make sure that both from and to are non-negative smis.
CSA_ASSERT(this, TaggedIsPositiveSmi(from));
if (flags == SubStringFlags::NONE) { CSA_ASSERT(this, TaggedIsPositiveSmi(to));
GotoIfNot(TaggedIsPositiveSmi(from), &runtime);
GotoIfNot(TaggedIsPositiveSmi(to), &runtime);
} else {
CSA_ASSERT(this, TaggedIsPositiveSmi(from));
CSA_ASSERT(this, TaggedIsPositiveSmi(to));
}
TNode<Smi> const substr_length = SmiSub(to, from); TNode<Smi> const substr_length = SmiSub(to, from);
TNode<Smi> const string_length = LoadStringLengthAsSmi(string); TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
...@@ -4759,9 +4751,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -4759,9 +4751,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// encoding at this point. // encoding at this point.
GotoIf(to_direct.is_external(), &external_string); GotoIf(to_direct.is_external(), &external_string);
var_result.Bind( var_result.Bind(AllocAndCopyStringCharacters(
AllocAndCopyStringCharacters(context, direct_string, instance_type, direct_string, instance_type, SmiUntag(offset), substr_length));
SmiUntag(offset), substr_length));
Counters* counters = isolate()->counters(); Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1); IncrementCounter(counters->sub_string_native(), 1);
...@@ -4774,9 +4765,9 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -4774,9 +4765,9 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
{ {
Node* const fake_sequential_string = to_direct.PointerToString(&runtime); Node* const fake_sequential_string = to_direct.PointerToString(&runtime);
var_result.Bind(AllocAndCopyStringCharacters( var_result.Bind(
context, fake_sequential_string, instance_type, SmiUntag(offset), AllocAndCopyStringCharacters(fake_sequential_string, instance_type,
substr_length)); SmiUntag(offset), substr_length));
Counters* counters = isolate()->counters(); Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1); IncrementCounter(counters->sub_string_native(), 1);
...@@ -4794,14 +4785,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -4794,14 +4785,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
BIND(&original_string_or_invalid_length); BIND(&original_string_or_invalid_length);
{ {
if (flags == SubStringFlags::NONE) { CSA_ASSERT(this, SmiEqual(substr_length, string_length));
// Longer than original string's length or negative: unsafe arguments.
GotoIf(SmiAbove(substr_length, string_length), &runtime);
} else {
// with flag SubStringFlags::FROM_TO_ARE_BOUNDED, the only way we can
// get here is if substr_length is equal to string_length.
CSA_ASSERT(this, SmiEqual(substr_length, string_length));
}
// Equal length - check if {from, to} == {0, str.length}. // Equal length - check if {from, to} == {0, str.length}.
GotoIf(SmiAbove(from, SmiConstant(0)), &runtime); GotoIf(SmiAbove(from, SmiConstant(0)), &runtime);
...@@ -4818,8 +4802,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -4818,8 +4802,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// Fall back to a runtime call. // Fall back to a runtime call.
BIND(&runtime); BIND(&runtime);
{ {
var_result.Bind( var_result.Bind(CallRuntime(Runtime::kSubString, NoContextConstant(),
CallRuntime(Runtime::kSubString, context, string, from, to)); string, from, to));
Goto(&end); Goto(&end);
} }
......
...@@ -1150,14 +1150,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1150,14 +1150,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Return the single character string with only {code}. // Return the single character string with only {code}.
Node* StringFromCharCode(Node* code); Node* StringFromCharCode(Node* code);
enum class SubStringFlags { NONE, FROM_TO_ARE_BOUNDED };
// Return a new string object which holds a substring containing the range // Return a new string object which holds a substring containing the range
// [from,to[ of string. |from| and |to| are expected to be tagged. // [from,to[ of string. |from| and |to| are expected to be tagged.
// If flags has the value FROM_TO_ARE_BOUNDED then from and to are in Node* SubString(Node* string, Node* from, Node* to);
// the range [0, string-length)
Node* SubString(Node* context, Node* string, Node* from, Node* to,
SubStringFlags flags = SubStringFlags::NONE);
// Return a new string object produced by concatenating |first| with |second|. // Return a new string object produced by concatenating |first| with |second|.
Node* StringAdd(Node* context, Node* first, Node* second, Node* StringAdd(Node* context, Node* first, Node* second,
...@@ -1920,8 +1915,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1920,8 +1915,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* CollectFeedbackForString(Node* instance_type); Node* CollectFeedbackForString(Node* instance_type);
void GenerateEqual_Same(Node* value, Label* if_equal, Label* if_notequal, void GenerateEqual_Same(Node* value, Label* if_equal, Label* if_notequal,
Variable* var_type_feedback = nullptr); Variable* var_type_feedback = nullptr);
Node* AllocAndCopyStringCharacters(Node* context, Node* from, Node* AllocAndCopyStringCharacters(Node* from, Node* from_instance_type,
Node* from_instance_type,
TNode<IntPtrT> from_index, TNode<IntPtrT> from_index,
TNode<Smi> character_count); TNode<Smi> character_count);
......
...@@ -219,32 +219,13 @@ RUNTIME_FUNCTION(Runtime_StringLastIndexOf) { ...@@ -219,32 +219,13 @@ RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
RUNTIME_FUNCTION(Runtime_SubString) { RUNTIME_FUNCTION(Runtime_SubString) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length()); DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, string, 0); CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
int start, end; CONVERT_INT32_ARG_CHECKED(start, 1);
// We have a fast integer-only case here to avoid a conversion to double in CONVERT_INT32_ARG_CHECKED(end, 2);
// the common case where from and to are Smis. DCHECK_LE(0, start);
if (args[1]->IsSmi() && args[2]->IsSmi()) { DCHECK_LE(start, end);
CONVERT_SMI_ARG_CHECKED(from_number, 1); DCHECK_LE(end, string->length());
CONVERT_SMI_ARG_CHECKED(to_number, 2);
start = from_number;
end = to_number;
} else if (args[1]->IsNumber() && args[2]->IsNumber()) {
CONVERT_DOUBLE_ARG_CHECKED(from_number, 1);
CONVERT_DOUBLE_ARG_CHECKED(to_number, 2);
start = FastD2IChecked(from_number);
end = FastD2IChecked(to_number);
} else {
return isolate->ThrowIllegalOperation();
}
// The following condition is intentionally robust because the SubString
// builtin delegates here and we test this in
// cctest/test-strings/RobustSubStringStub.
if (end < start || start < 0 || end > string->length()) {
return isolate->ThrowIllegalOperation();
}
isolate->counters()->sub_string_runtime()->Increment(); isolate->counters()->sub_string_runtime()->Increment();
return *isolate->factory()->NewSubString(string, start, end); return *isolate->factory()->NewSubString(string, start, end);
} }
......
...@@ -1335,99 +1335,6 @@ UNINITIALIZED_TEST(OneByteArrayJoin) { ...@@ -1335,99 +1335,6 @@ UNINITIALIZED_TEST(OneByteArrayJoin) {
isolate->Dispose(); isolate->Dispose();
} }
static void CheckException(const char* source) {
// An empty handle is returned upon exception.
CHECK(CompileRun(source).IsEmpty());
}
TEST(RobustSubStringStub) {
// This tests whether the SubStringStub can handle unsafe arguments.
// If not recognized, those unsafe arguments lead to out-of-bounds reads.
FLAG_allow_natives_syntax = true;
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::Value> result;
Handle<String> string;
CompileRun("var short = 'abcdef';");
// Invalid indices.
CheckException("%_SubString(short, 0, 10000);");
CheckException("%_SubString(short, -1234, 5);");
CheckException("%_SubString(short, 5, 2);");
// Special HeapNumbers.
CheckException("%_SubString(short, 1, Infinity);");
CheckException("%_SubString(short, NaN, 5);");
// String arguments.
CheckException("%_SubString(short, '2', '5');");
// Ordinary HeapNumbers can be handled (in runtime).
result = CompileRun("%_SubString(short, Math.sqrt(4), 5.1);");
string = v8::Utils::OpenHandle(v8::String::Cast(*result));
CHECK_EQ(0, strcmp("cde", string->ToCString().get()));
CompileRun("var long = 'abcdefghijklmnopqrstuvwxyz';");
// Invalid indices.
CheckException("%_SubString(long, 0, 10000);");
CheckException("%_SubString(long, -1234, 17);");
CheckException("%_SubString(long, 17, 2);");
// Special HeapNumbers.
CheckException("%_SubString(long, 1, Infinity);");
CheckException("%_SubString(long, NaN, 17);");
// String arguments.
CheckException("%_SubString(long, '2', '17');");
// Ordinary HeapNumbers within bounds can be handled (in runtime).
result = CompileRun("%_SubString(long, Math.sqrt(4), 17.1);");
string = v8::Utils::OpenHandle(v8::String::Cast(*result));
CHECK_EQ(0, strcmp("cdefghijklmnopq", string->ToCString().get()));
// Test that out-of-bounds substring of a slice fails when the indices
// would have been valid for the underlying string.
CompileRun("var slice = long.slice(1, 15);");
CheckException("%_SubString(slice, 0, 17);");
}
TEST(RobustSubStringStubExternalStrings) {
// Ensure that the specific combination of calling the SubStringStub on an
// external string and triggering a GC on string allocation does not crash.
// See crbug.com/649967.
FLAG_allow_natives_syntax = true;
#ifdef VERIFY_HEAP
FLAG_verify_heap = true;
#endif
CcTest::InitializeVM();
v8::HandleScope handle_scope(CcTest::isolate());
v8::Local<v8::String> underlying =
CompileRun(
"var str = 'abcdefghijklmnopqrstuvwxyz';"
"str")
->ToString(CcTest::isolate()->GetCurrentContext())
.ToLocalChecked();
CHECK(v8::Utils::OpenHandle(*underlying)->IsSeqOneByteString());
const int length = underlying->Length();
uc16* two_byte = NewArray<uc16>(length + 1);
underlying->Write(two_byte);
Resource* resource = new Resource(two_byte, length);
CHECK(underlying->MakeExternal(resource));
CHECK(v8::Utils::OpenHandle(*underlying)->IsExternalTwoByteString());
v8::Local<v8::Script> script = v8_compile(v8_str("%_SubString(str, 5, 8)"));
// Trigger a GC on string allocation.
i::heap::SimulateFullSpace(CcTest::heap()->new_space());
v8::Local<v8::Value> result;
CHECK(script->Run(v8::Isolate::GetCurrent()->GetCurrentContext())
.ToLocal(&result));
Handle<String> string = v8::Utils::OpenHandle(v8::String::Cast(*result));
CHECK_EQ(0, strcmp("fgh", string->ToCString().get()));
}
namespace { namespace {
int* global_use_counts = nullptr; int* global_use_counts = nullptr;
......
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