Index: src/builtins/builtins-string.cc |
diff --git a/src/builtins/builtins-string.cc b/src/builtins/builtins-string.cc |
index e716dca73bfd562767759d07c71bfdb4812c5b91..19338fbd9c173ba1042b3b3cc40dadbeb9b9f7c8 100644 |
--- a/src/builtins/builtins-string.cc |
+++ b/src/builtins/builtins-string.cc |
@@ -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, |