[stubs] Port String.prototype.substr to TurboFan
BUG=v8:5415 Review-Url: https://codereview.chromium.org/2373493002 Cr-Commit-Position: refs/heads/master@{#39951}
This commit is contained in:
parent
614e615775
commit
7d26871d2d
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user