Commit 7d26871d authored by jgruber's avatar jgruber Committed by Commit bot

[stubs] Port String.prototype.substr to TurboFan

BUG=v8:5415

Review-Url: https://codereview.chromium.org/2373493002
Cr-Commit-Position: refs/heads/master@{#39951}
parent 614e6157
......@@ -1410,6 +1410,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kStringPrototypeLocaleCompare, 1, true);
SimpleInstallFunction(prototype, "normalize",
Builtins::kStringPrototypeNormalize, 0, false);
SimpleInstallFunction(prototype, "substr", Builtins::kStringPrototypeSubstr,
2, true);
SimpleInstallFunction(prototype, "substring",
Builtins::kStringPrototypeSubstring, 2, true);
SimpleInstallFunction(prototype, "toString",
......
......@@ -873,6 +873,129 @@ BUILTIN(StringPrototypeNormalize) {
return *string;
}
// ES6 section B.2.3.1 String.prototype.substr ( start, length )
void Builtins::Generate_StringPrototypeSubstr(CodeStubAssembler* a) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Label out(a), handle_length(a);
Variable var_start(a, MachineRepresentation::kTagged);
Variable var_length(a, MachineRepresentation::kTagged);
Node* const receiver = a->Parameter(0);
Node* const start = a->Parameter(1);
Node* const length = a->Parameter(2);
Node* const context = a->Parameter(5);
Node* const zero = a->SmiConstant(Smi::FromInt(0));
// Check that {receiver} is coercible to Object and convert it to a String.
Node* const string =
a->ToThisString(context, receiver, "String.prototype.substr");
Node* const string_length = a->LoadStringLength(string);
// Conversions and bounds-checks for {start}.
{
Node* const start_int =
a->ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
Label if_issmi(a), if_isheapnumber(a, Label::kDeferred);
a->Branch(a->WordIsSmi(start_int), &if_issmi, &if_isheapnumber);
a->Bind(&if_issmi);
{
Node* const length_plus_start = a->SmiAdd(string_length, start_int);
var_start.Bind(a->Select(a->SmiLessThan(start_int, zero),
a->SmiMax(length_plus_start, zero), start_int));
a->Goto(&handle_length);
}
a->Bind(&if_isheapnumber);
{
// If {start} is a heap number, it is definitely out of bounds. If it is
// negative, {start} = max({string_length} + {start}),0) = 0'. If it is
// positive, set {start} to {string_length} which ultimately results in
// returning an empty string.
Node* const float_zero = a->Float64Constant(0.);
Node* const start_float = a->LoadHeapNumberValue(start_int);
var_start.Bind(a->Select(a->Float64LessThan(start_float, float_zero),
zero, string_length));
a->Goto(&handle_length);
}
}
// Conversions and bounds-checks for {length}.
a->Bind(&handle_length);
{
Label if_issmi(a), if_isheapnumber(a, Label::kDeferred);
// Default to {string_length} if {length} is undefined.
{
Label if_isundefined(a, Label::kDeferred), if_isnotundefined(a);
a->Branch(a->WordEqual(length, a->UndefinedConstant()), &if_isundefined,
&if_isnotundefined);
a->Bind(&if_isundefined);
var_length.Bind(string_length);
a->Goto(&if_issmi);
a->Bind(&if_isnotundefined);
var_length.Bind(
a->ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
}
a->Branch(a->WordIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
// Set {length} to min(max({length}, 0), {string_length} - {start}
a->Bind(&if_issmi);
{
Node* const positive_length = a->SmiMax(var_length.value(), zero);
Node* const minimal_length = a->SmiSub(string_length, var_start.value());
var_length.Bind(a->SmiMin(positive_length, minimal_length));
a->GotoUnless(a->SmiLessThanOrEqual(var_length.value(), zero), &out);
a->Return(a->EmptyStringConstant());
}
a->Bind(&if_isheapnumber);
{
// If {length} is a heap number, it is definitely out of bounds. There are
// two cases according to the spec: if it is negative, "" is returned; if
// it is positive, then length is set to {string_length} - {start}.
a->Assert(a->WordEqual(a->LoadMap(var_length.value()),
a->HeapNumberMapConstant()));
Label if_isnegative(a), if_ispositive(a);
Node* const float_zero = a->Float64Constant(0.);
Node* const length_float = a->LoadHeapNumberValue(var_length.value());
a->Branch(a->Float64LessThan(length_float, float_zero), &if_isnegative,
&if_ispositive);
a->Bind(&if_isnegative);
a->Return(a->EmptyStringConstant());
a->Bind(&if_ispositive);
{
var_length.Bind(a->SmiSub(string_length, var_start.value()));
a->GotoUnless(a->SmiLessThanOrEqual(var_length.value(), zero), &out);
a->Return(a->EmptyStringConstant());
}
}
}
a->Bind(&out);
{
Node* const end = a->SmiAdd(var_start.value(), var_length.value());
Node* const result = a->SubString(context, string, var_start.value(), end);
a->Return(result);
}
}
namespace {
compiler::Node* ToSmiBetweenZeroAnd(CodeStubAssembler* a,
......
......@@ -557,6 +557,8 @@ namespace internal {
CPP(StringPrototypeLocaleCompare) \
/* ES6 section 21.1.3.12 String.prototype.normalize ( [form] ) */ \
CPP(StringPrototypeNormalize) \
/* ES6 section B.2.3.1 String.prototype.substr ( start, length ) */ \
TFJ(StringPrototypeSubstr, 3) \
/* ES6 section 21.1.3.19 String.prototype.substring ( start, end ) */ \
TFJ(StringPrototypeSubstring, 3) \
/* ES6 section 21.1.3.25 String.prototype.toString () */ \
......
......@@ -34,7 +34,7 @@ var patternSymbol = utils.ImportNow("intl_pattern_symbol");
var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
var SetFunctionName = utils.SetFunctionName;
var StringIndexOf;
var StringSubstr;
var StringSubstr = GlobalString.prototype.substr;
var StringSubstring = GlobalString.prototype.substring;
utils.Import(function(from) {
......@@ -43,7 +43,6 @@ utils.Import(function(from) {
InternalRegExpMatch = from.InternalRegExpMatch;
InternalRegExpReplace = from.InternalRegExpReplace;
StringIndexOf = from.StringIndexOf;
StringSubstr = from.StringSubstr;
});
// Utilities for definitions
......
......@@ -237,21 +237,6 @@ function StringSplitJS(separator, limit) {
return %StringSplit(subject, separator_string, limit);
}
// ecma262/#sec-string.prototype.substr
function StringSubstr(start, length) {
CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
var s = TO_STRING(this);
var size = s.length;
start = TO_INTEGER(start);
length = IS_UNDEFINED(length) ? size : TO_INTEGER(length);
if (start < 0) start = MaxSimple(size + start, 0);
length = MinSimple(MaxSimple(length, 0), size - start);
if (length <= 0) return '';
return %_SubString(s, start, start + length);
}
// ECMA-262, 15.5.4.16
function StringToLowerCaseJS() {
......@@ -557,7 +542,6 @@ utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
"search", StringSearch,
"slice", StringSlice,
"split", StringSplitJS,
"substr", StringSubstr,
"startsWith", StringStartsWith,
"toLowerCase", StringToLowerCaseJS,
"toLocaleLowerCase", StringToLocaleLowerCase,
......@@ -588,7 +572,6 @@ utils.Export(function(to) {
to.StringReplace = StringReplace;
to.StringSlice = StringSlice;
to.StringSplit = StringSplitJS;
to.StringSubstr = StringSubstr;
});
})
......@@ -171,3 +171,57 @@ for (var i = 63; i >= 0; i--) {
assertEquals("", String.prototype.substr.call(string, start, length));
assertEquals(["this", "start", "length"], log);
}
// Bounds edge cases.
{
const str = "abc";
const negativeHeapNumber = -1 * 2**32;
const positiveHeapNumber = 2**32;
assertEquals("abc", str.substr(negativeHeapNumber));
assertEquals("abc", str.substr(negativeHeapNumber, str.length));
assertEquals("abc", str.substr(-str.length, str.length));
assertEquals("abc", str.substr(0, str.length));
assertEquals("bc", str.substr(-2, str.length));
assertEquals("c", str.substr(-1, str.length));
assertEquals("", str.substr(str.length));
assertEquals("", str.substr(4));
assertEquals("", str.substr(positiveHeapNumber));
assertEquals("abc", str.substr(negativeHeapNumber, positiveHeapNumber));
assertEquals("abc", str.substr(negativeHeapNumber, positiveHeapNumber));
assertEquals("abc", str.substr(-str.length, positiveHeapNumber));
assertEquals("abc", str.substr(0, positiveHeapNumber));
assertEquals("bc", str.substr(-2, positiveHeapNumber));
assertEquals("c", str.substr(-1, positiveHeapNumber));
assertEquals("", str.substr(str.length, positiveHeapNumber));
assertEquals("", str.substr(4, positiveHeapNumber));
assertEquals("", str.substr(positiveHeapNumber, positiveHeapNumber));
assertEquals("", str.substr(negativeHeapNumber, negativeHeapNumber));
assertEquals("", str.substr(negativeHeapNumber, negativeHeapNumber));
assertEquals("", str.substr(-str.length, negativeHeapNumber));
assertEquals("", str.substr(0, negativeHeapNumber));
assertEquals("", str.substr(-2, negativeHeapNumber));
assertEquals("", str.substr(-1, negativeHeapNumber));
assertEquals("", str.substr(str.length, negativeHeapNumber));
assertEquals("", str.substr(4, negativeHeapNumber));
assertEquals("", str.substr(positiveHeapNumber, negativeHeapNumber));
assertEquals("", str.substr(negativeHeapNumber, -1));
assertEquals("", str.substr(negativeHeapNumber, -1));
assertEquals("", str.substr(-str.length, -1));
assertEquals("", str.substr(0, -1));
assertEquals("", str.substr(-2, -1));
assertEquals("", str.substr(-1, -1));
assertEquals("", str.substr(str.length, -1));
assertEquals("", str.substr(4, -1));
assertEquals("", str.substr(positiveHeapNumber, -1));
assertEquals("abc", str.substr(undefined));
assertEquals("abc", str.substr(undefined, undefined));
}
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