Port StringPrototypeSubString to Torque

Bug: v8:8996
Change-Id: I63ae821086c42c14a317e866fb4f0f799f4c4f7c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1597555
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Simon Z√ľnd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61360}
diff --git a/BUILD.gn b/BUILD.gn
index 26f218e..27769cf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -956,6 +956,7 @@
   "src/builtins/string-iterator.tq",
   "src/builtins/string-repeat.tq",
   "src/builtins/string-startswith.tq",
+  "src/builtins/string-substring.tq",
   "src/builtins/typed-array-createtypedarray.tq",
   "src/builtins/typed-array-every.tq",
   "src/builtins/typed-array-filter.tq",
@@ -1010,6 +1011,7 @@
   "string-html",
   "string-iterator",
   "string-repeat",
+  "string-substring",
   "test",
   "typed-array",
   "typed-array-createtypedarray",
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index d5052aa..24be75a 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -984,9 +984,6 @@
   TFJ(StringPrototypeSplit, SharedFunctionInfo::kDontAdaptArgumentsSentinel)   \
   /* ES6 #sec-string.prototype.substr */                                       \
   TFJ(StringPrototypeSubstr, SharedFunctionInfo::kDontAdaptArgumentsSentinel)  \
-  /* ES6 #sec-string.prototype.substring */                                    \
-  TFJ(StringPrototypeSubstring,                                                \
-      SharedFunctionInfo::kDontAdaptArgumentsSentinel)                         \
   TFJ(StringPrototypeTrim, SharedFunctionInfo::kDontAdaptArgumentsSentinel)    \
   TFJ(StringPrototypeTrimEnd, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
   TFJ(StringPrototypeTrimStart,                                                \
diff --git a/src/builtins/builtins-string-gen.cc b/src/builtins/builtins-string-gen.cc
index 905d31a..0b42225 100644
--- a/src/builtins/builtins-string-gen.cc
+++ b/src/builtins/builtins-string-gen.cc
@@ -1920,56 +1920,6 @@
   }
 }
 
-TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
-    SloppyTNode<Context> context, SloppyTNode<Object> value,
-    SloppyTNode<Smi> limit) {
-  Label out(this);
-  TVARIABLE(Smi, var_result);
-
-  TNode<Number> const value_int =
-      ToInteger_Inline(context, value, CodeStubAssembler::kTruncateMinusZero);
-
-  Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
-  Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
-
-  BIND(&if_issmi);
-  {
-    TNode<Smi> value_smi = CAST(value_int);
-    Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
-    Branch(SmiAbove(value_smi, limit), &if_isoutofbounds, &if_isinbounds);
-
-    BIND(&if_isinbounds);
-    {
-      var_result = CAST(value_int);
-      Goto(&out);
-    }
-
-    BIND(&if_isoutofbounds);
-    {
-      TNode<Smi> const zero = SmiConstant(0);
-      var_result =
-          SelectConstant<Smi>(SmiLessThan(value_smi, zero), zero, limit);
-      Goto(&out);
-    }
-  }
-
-  BIND(&if_isnotsmi);
-  {
-    // {value} is a heap number - in this case, it is definitely out of bounds.
-    TNode<HeapNumber> value_int_hn = CAST(value_int);
-
-    TNode<Float64T> const float_zero = Float64Constant(0.);
-    TNode<Smi> const smi_zero = SmiConstant(0);
-    TNode<Float64T> const value_float = LoadHeapNumberValue(value_int_hn);
-    var_result = SelectConstant<Smi>(Float64LessThan(value_float, float_zero),
-                                     smi_zero, limit);
-    Goto(&out);
-  }
-
-  BIND(&out);
-  return var_result.value();
-}
-
 TF_BUILTIN(StringSubstring, CodeStubAssembler) {
   TNode<String> string = CAST(Parameter(Descriptor::kString));
   TNode<IntPtrT> from = UncheckedCast<IntPtrT>(Parameter(Descriptor::kFrom));
@@ -1978,61 +1928,6 @@
   Return(SubString(string, from, to));
 }
 
-// ES6 #sec-string.prototype.substring
-TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
-  const int kStartArg = 0;
-  const int kEndArg = 1;
-
-  Node* const argc =
-      ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
-  CodeStubArguments args(this, argc);
-
-  TNode<Object> receiver = args.GetReceiver();
-  TNode<Object> start = args.GetOptionalArgumentValue(kStartArg);
-  TNode<Object> end = args.GetOptionalArgumentValue(kEndArg);
-  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
-  Label out(this);
-
-  TVARIABLE(Smi, var_start);
-  TVARIABLE(Smi, var_end);
-
-  // Check that {receiver} is coercible to Object and convert it to a String.
-  TNode<String> const string =
-      ToThisString(context, receiver, "String.prototype.substring");
-
-  TNode<Smi> const length = LoadStringLengthAsSmi(string);
-
-  // Conversion and bounds-checks for {start}.
-  var_start = ToSmiBetweenZeroAnd(context, start, length);
-
-  // Conversion and bounds-checks for {end}.
-  {
-    var_end = length;
-    GotoIf(IsUndefined(end), &out);
-
-    var_end = ToSmiBetweenZeroAnd(context, end, length);
-
-    Label if_endislessthanstart(this);
-    Branch(SmiLessThan(var_end.value(), var_start.value()),
-           &if_endislessthanstart, &out);
-
-    BIND(&if_endislessthanstart);
-    {
-      TNode<Smi> const tmp = var_end.value();
-      var_end = var_start.value();
-      var_start = tmp;
-      Goto(&out);
-    }
-  }
-
-  BIND(&out);
-  {
-    args.PopAndReturn(SubString(string, SmiUntag(var_start.value()),
-                                SmiUntag(var_end.value())));
-  }
-}
-
 // ES6 #sec-string.prototype.trim
 TF_BUILTIN(StringPrototypeTrim, StringTrimAssembler) {
   TNode<IntPtrT> argc =
diff --git a/src/builtins/builtins-string-gen.h b/src/builtins/builtins-string-gen.h
index 042929b..8df6b74 100644
--- a/src/builtins/builtins-string-gen.h
+++ b/src/builtins/builtins-string-gen.h
@@ -64,10 +64,6 @@
   void GenerateStringRelationalComparison(Node* context, Node* left,
                                           Node* right, Operation op);
 
-  TNode<Smi> ToSmiBetweenZeroAnd(SloppyTNode<Context> context,
-                                 SloppyTNode<Object> value,
-                                 SloppyTNode<Smi> limit);
-
   typedef std::function<TNode<Object>(
       TNode<String> receiver, TNode<IntPtrT> length, TNode<IntPtrT> index)>
       StringAtAccessor;
diff --git a/src/builtins/string-substring.tq b/src/builtins/string-substring.tq
new file mode 100644
index 0000000..f322eee
--- /dev/null
+++ b/src/builtins/string-substring.tq
@@ -0,0 +1,50 @@
+// Copyright 2019 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+namespace string_substring {
+
+  extern macro SubString(String, intptr, intptr): String;
+
+  transitioning macro ToSmiBetweenZeroAnd(implicit context: Context)(
+      value: Object, limit: Smi): Smi {
+    const valueInt: Number =
+        ToInteger_Inline(context, value, kTruncateMinusZero);
+    typeswitch (valueInt) {
+      case (valueSmi: Smi): {
+        if (SmiAbove(valueSmi, limit)) {
+          return valueSmi < 0 ? 0 : limit;
+        }
+        return valueSmi;
+      }
+      // {value} is a heap number - in this case, it is definitely out of
+      // bounds.
+      case (hn: HeapNumber): {
+        const valueFloat: float64 = LoadHeapNumberValue(hn);
+        return valueFloat < 0. ? 0 : limit;
+      }
+    }
+  }
+
+  // ES6 #sec-string.prototype.substring
+  transitioning javascript builtin StringPrototypeSubstring(
+      implicit context: Context)(receiver: Object, ...arguments): String {
+    // Check that {receiver} is coercible to Object and convert it to a String.
+    const string: String = ToThisString(receiver, 'String.prototype.substring');
+    const length = string.length_smi;
+
+    // Conversion and bounds-checks for {start}.
+    let start: Smi = ToSmiBetweenZeroAnd(arguments[0], length);
+
+    // Conversion and bounds-checks for {end}.
+    let end: Smi = arguments[1] == Undefined ?
+        length :
+        ToSmiBetweenZeroAnd(arguments[1], length);
+    if (end < start) {
+      const tmp: Smi = end;
+      end = start;
+      start = tmp;
+    }
+    return SubString(string, SmiUntag(start), SmiUntag(end));
+  }
+}