| 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,
 | 
| 
 |