| Index: src/builtins/builtins-string-gen.cc
|
| diff --git a/src/builtins/builtins-string-gen.cc b/src/builtins/builtins-string-gen.cc
|
| index 5b457b7083b27fc469ff67dbf9a965da05fab93d..18fba261e8957c6a29e1c0ca305b0f0f45de1fc7 100644
|
| --- a/src/builtins/builtins-string-gen.cc
|
| +++ b/src/builtins/builtins-string-gen.cc
|
| @@ -155,6 +155,41 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
| return SmiLessThan(value, SmiConstant(0));
|
| }
|
|
|
| + // substr and slice have a common way of handling the {start} argument.
|
| + void ConvertAndBoundsCheckStartArgument(Node* context, Variable* var_start,
|
| + Node* start, Node* string_length) {
|
| + Node* const start_int =
|
| + ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
|
| + Node* const zero = SmiConstant(Smi::kZero);
|
| +
|
| + Label done(this);
|
| + Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
|
| + Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
|
| +
|
| + BIND(&if_issmi);
|
| + {
|
| + var_start->Bind(
|
| + Select(SmiLessThan(start_int, zero),
|
| + [&] { return SmiMax(SmiAdd(string_length, start_int), zero); },
|
| + [&] { return start_int; }, MachineRepresentation::kTagged));
|
| + Goto(&done);
|
| + }
|
| +
|
| + 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 = Float64Constant(0.);
|
| + Node* const start_float = LoadHeapNumberValue(start_int);
|
| + var_start->Bind(SelectTaggedConstant(
|
| + Float64LessThan(start_float, float_zero), zero, string_length));
|
| + Goto(&done);
|
| + }
|
| + BIND(&done);
|
| + }
|
| +
|
| // Implements boilerplate logic for {match, split, replace, search} of the
|
| // form:
|
| //
|
| @@ -1293,6 +1328,89 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
| }
|
| }
|
|
|
| +// ES6 section 21.1.3.18 String.prototype.slice ( start, end )
|
| +TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
|
| + Label out(this);
|
| + VARIABLE(var_start, MachineRepresentation::kTagged);
|
| + VARIABLE(var_end, MachineRepresentation::kTagged);
|
| +
|
| + const int kStart = 0;
|
| + const int kEnd = 1;
|
| + Node* argc =
|
| + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
|
| + CodeStubArguments args(this, argc);
|
| + Node* const receiver = args.GetReceiver();
|
| + Node* const start =
|
| + args.GetOptionalArgumentValue(kStart, UndefinedConstant());
|
| + Node* const end = args.GetOptionalArgumentValue(kEnd, UndefinedConstant());
|
| + Node* const context = Parameter(BuiltinDescriptor::kContext);
|
| +
|
| + Node* const smi_zero = SmiConstant(0);
|
| +
|
| + // 1. Let O be ? RequireObjectCoercible(this value).
|
| + RequireObjectCoercible(context, receiver, "String.prototype.slice");
|
| +
|
| + // 2. Let S be ? ToString(O).
|
| + Callable tostring_callable = CodeFactory::ToString(isolate());
|
| + Node* const subject_string = CallStub(tostring_callable, context, receiver);
|
| +
|
| + // 3. Let len be the number of elements in S.
|
| + Node* const length = LoadStringLength(subject_string);
|
| +
|
| + // Conversions and bounds-checks for {start}.
|
| + ConvertAndBoundsCheckStartArgument(context, &var_start, start, length);
|
| +
|
| + // 5. If end is undefined, let intEnd be len;
|
| + var_end.Bind(length);
|
| + GotoIf(WordEqual(end, UndefinedConstant()), &out);
|
| +
|
| + // else let intEnd be ? ToInteger(end).
|
| + Node* const end_int =
|
| + ToInteger(context, end, CodeStubAssembler::kTruncateMinusZero);
|
| +
|
| + // 7. If intEnd < 0, let to be max(len + intEnd, 0);
|
| + // otherwise let to be min(intEnd, len).
|
| + Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
|
| + Branch(TaggedIsSmi(end_int), &if_issmi, &if_isheapnumber);
|
| +
|
| + BIND(&if_issmi);
|
| + {
|
| + Node* const length_plus_end = SmiAdd(length, end_int);
|
| + var_end.Bind(Select(SmiLessThan(end_int, smi_zero),
|
| + [&] { return SmiMax(length_plus_end, smi_zero); },
|
| + [&] { return SmiMin(length, end_int); },
|
| + MachineRepresentation::kTagged));
|
| + Goto(&out);
|
| + }
|
| +
|
| + BIND(&if_isheapnumber);
|
| + {
|
| + // If {end} is a heap number, it is definitely out of bounds. If it is
|
| + // negative, {int_end} = max({length} + {int_end}),0) = 0'. If it is
|
| + // positive, set {int_end} to {length} which ultimately results in
|
| + // returning an empty string.
|
| + Node* const float_zero = Float64Constant(0.);
|
| + Node* const end_float = LoadHeapNumberValue(end_int);
|
| + var_end.Bind(SelectTaggedConstant(Float64LessThan(end_float, float_zero),
|
| + smi_zero, length));
|
| + Goto(&out);
|
| + }
|
| +
|
| + Label return_emptystring(this);
|
| + BIND(&out);
|
| + {
|
| + GotoIf(SmiLessThanOrEqual(var_end.value(), var_start.value()),
|
| + &return_emptystring);
|
| + Node* const result =
|
| + SubString(context, subject_string, var_start.value(), var_end.value(),
|
| + SubStringFlags::FROM_TO_ARE_BOUNDED);
|
| + args.PopAndReturn(result);
|
| + }
|
| +
|
| + BIND(&return_emptystring);
|
| + args.PopAndReturn(EmptyStringConstant());
|
| +}
|
| +
|
| // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
|
| TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
|
| Label out(this);
|
| @@ -1397,8 +1515,8 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
|
| }
|
|
|
| // ES6 #sec-string.prototype.substr
|
| -TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) {
|
| - Label out(this), handle_length(this);
|
| +TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
|
| + Label out(this);
|
|
|
| VARIABLE(var_start, MachineRepresentation::kTagged);
|
| VARIABLE(var_length, MachineRepresentation::kTagged);
|
| @@ -1417,94 +1535,62 @@ TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) {
|
| Node* const string_length = LoadStringLength(string);
|
|
|
| // Conversions and bounds-checks for {start}.
|
| - {
|
| - Node* const start_int =
|
| - ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
|
| -
|
| - Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
|
| - Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
|
| -
|
| - BIND(&if_issmi);
|
| - {
|
| - Node* const length_plus_start = SmiAdd(string_length, start_int);
|
| - var_start.Bind(Select(SmiLessThan(start_int, zero),
|
| - [&] { return SmiMax(length_plus_start, zero); },
|
| - [&] { return start_int; },
|
| - MachineRepresentation::kTagged));
|
| - Goto(&handle_length);
|
| - }
|
| -
|
| - 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 = Float64Constant(0.);
|
| - Node* const start_float = LoadHeapNumberValue(start_int);
|
| - var_start.Bind(SelectTaggedConstant(
|
| - Float64LessThan(start_float, float_zero), zero, string_length));
|
| - Goto(&handle_length);
|
| - }
|
| - }
|
| + ConvertAndBoundsCheckStartArgument(context, &var_start, start, string_length);
|
|
|
| // Conversions and bounds-checks for {length}.
|
| - BIND(&handle_length);
|
| + Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
|
| +
|
| + // Default to {string_length} if {length} is undefined.
|
| {
|
| - Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
|
| + Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
|
| + Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
|
| + &if_isnotundefined);
|
|
|
| - // Default to {string_length} if {length} is undefined.
|
| - {
|
| - Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
|
| - Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
|
| - &if_isnotundefined);
|
| + BIND(&if_isundefined);
|
| + var_length.Bind(string_length);
|
| + Goto(&if_issmi);
|
|
|
| - BIND(&if_isundefined);
|
| - var_length.Bind(string_length);
|
| - Goto(&if_issmi);
|
| + BIND(&if_isnotundefined);
|
| + var_length.Bind(
|
| + ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
|
| + }
|
|
|
| - BIND(&if_isnotundefined);
|
| - var_length.Bind(
|
| - ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
|
| - }
|
| + Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
|
|
|
| - Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
|
| + // Set {length} to min(max({length}, 0), {string_length} - {start}
|
| + BIND(&if_issmi);
|
| + {
|
| + Node* const positive_length = SmiMax(var_length.value(), zero);
|
|
|
| - // Set {length} to min(max({length}, 0), {string_length} - {start}
|
| - BIND(&if_issmi);
|
| - {
|
| - Node* const positive_length = SmiMax(var_length.value(), zero);
|
| + Node* const minimal_length = SmiSub(string_length, var_start.value());
|
| + var_length.Bind(SmiMin(positive_length, minimal_length));
|
|
|
| - Node* const minimal_length = SmiSub(string_length, var_start.value());
|
| - var_length.Bind(SmiMin(positive_length, minimal_length));
|
| + GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
|
| + Return(EmptyStringConstant());
|
| + }
|
|
|
| - GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
|
| - Return(EmptyStringConstant());
|
| - }
|
| + 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}.
|
|
|
| - 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}.
|
| + CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
|
|
|
| - CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
|
| + Label if_isnegative(this), if_ispositive(this);
|
| + Node* const float_zero = Float64Constant(0.);
|
| + Node* const length_float = LoadHeapNumberValue(var_length.value());
|
| + Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
|
| + &if_ispositive);
|
|
|
| - Label if_isnegative(this), if_ispositive(this);
|
| - Node* const float_zero = Float64Constant(0.);
|
| - Node* const length_float = LoadHeapNumberValue(var_length.value());
|
| - Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
|
| - &if_ispositive);
|
| + BIND(&if_isnegative);
|
| + Return(EmptyStringConstant());
|
|
|
| - BIND(&if_isnegative);
|
| + BIND(&if_ispositive);
|
| + {
|
| + var_length.Bind(SmiSub(string_length, var_start.value()));
|
| + GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
|
| Return(EmptyStringConstant());
|
| -
|
| - BIND(&if_ispositive);
|
| - {
|
| - var_length.Bind(SmiSub(string_length, var_start.value()));
|
| - GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
|
| - Return(EmptyStringConstant());
|
| - }
|
| }
|
| }
|
|
|
|
|