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..a9b726af80bde86d9993053d1a4595bdb64c17c6 100644 |
--- a/src/builtins/builtins-string-gen.cc |
+++ b/src/builtins/builtins-string-gen.cc |
@@ -1293,6 +1293,123 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) { |
} |
} |
+// ES6 section 21.1.3.18 String.prototype.slice ( start, end ) |
+TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) { |
+ Label handle_end(this), out(this); |
+ VARIABLE(var_start, MachineRepresentation::kTagged); |
+ VARIABLE(var_end, MachineRepresentation::kTagged); |
+ |
+ Node* const receiver = Parameter(Descriptor::kReceiver); |
+ Node* const start = Parameter(Descriptor::kStart); |
+ Node* const end = Parameter(Descriptor::kEnd); |
+ Node* const context = Parameter(Descriptor::kContext); |
+ |
+ Node* const smi_zero = SmiConstant(0); |
+ |
+ // 1. Let O be ? RequireObjectCoercible(this value). |
+ RequireObjectCoercible(context, receiver, "String.prototype.slice"); |
+ |
+ // String and integer conversions. |
+ // TODO(jgruber): The old implementation used Uint32Max instead of SmiMax - |
jgruber
2017/05/11 08:57:40
Nit: This comment doesn't apply here.
mvstanton
2017/05/12 11:00:42
Done.
|
+ // but AFAIK there should not be a difference since arrays are capped at Smi |
+ // lengths. |
+ |
+ // 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}. |
jgruber
2017/05/11 08:57:40
From a quick look this seems identical to arg hand
mvstanton
2017/05/12 11:00:42
Indeed, I made a utility method in the string code
|
+ { |
+ // 4. Let intStart be ? ToInteger(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); |
+ |
+ // 6. If intStart < 0, let from be max(len + intStart, 0); |
+ // otherwise, let from be min(intStart, len). |
+ BIND(&if_issmi); |
+ { |
+ Node* const length_plus_start = SmiAdd(length, start_int); |
jgruber
2017/05/11 08:57:40
Nit: Just noticed that this could move into the Se
mvstanton
2017/05/12 11:00:42
Oh yeah! Done.
|
+ var_start.Bind(Select(SmiLessThan(start_int, smi_zero), |
+ [&] { return SmiMax(length_plus_start, smi_zero); }, |
+ [&] { return SmiMin(start_int, length); }, |
+ MachineRepresentation::kTagged)); |
+ Goto(&handle_end); |
+ } |
+ |
+ 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), smi_zero, length)); |
+ Goto(&handle_end); |
+ } |
+ } |
+ |
+ BIND(&handle_end); |
+ { |
+ // 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); |
jgruber
2017/05/11 08:57:40
Same here.
mvstanton
2017/05/12 11:00:42
Done.
|
+ 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); |
+ Return(result); |
+ } |
+ |
+ BIND(&return_emptystring); |
+ Return(EmptyStringConstant()); |
+} |
+ |
// ES6 section 21.1.3.19 String.prototype.split ( separator, limit ) |
TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { |
Label out(this); |