[builtins] Port StringPad to Torque

Bug: v8:8996
Change-Id: I9927d7eb3b32f1f1eb07fd803e44d81bc205f390
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1772041
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63433}
This commit is contained in:
Z Nguyen-Huu 2019-08-28 10:57:45 -07:00 committed by Commit Bot
parent 1f4bec2775
commit 04256154e1
5 changed files with 113 additions and 140 deletions

View File

@ -1001,6 +1001,7 @@ torque_files = [
"src/builtins/string-endswith.tq",
"src/builtins/string-html.tq",
"src/builtins/string-iterator.tq",
"src/builtins/string-pad.tq",
"src/builtins/string-repeat.tq",
"src/builtins/string-slice.tq",
"src/builtins/string-startswith.tq",

View File

@ -1763,6 +1763,7 @@ extern operator '*' macro ConstexprInt31Mul(
constexpr int31, constexpr int31): constexpr int31;
extern operator '-' macro Int32Sub(int32, int32): int32;
extern operator '*' macro Int32Mul(int32, int32): int32;
extern operator '/' macro Int32Div(int32, int32): int32;
extern operator '%' macro Int32Mod(int32, int32): int32;
extern operator '&' macro Word32And(int32, int32): int32;
extern operator '&' macro Word32And(uint32, uint32): uint32;

View File

@ -926,11 +926,6 @@ namespace internal {
TFJ(StringPrototypeMatchAll, 1, kReceiver, kRegexp) \
/* ES6 #sec-string.prototype.localecompare */ \
CPP(StringPrototypeLocaleCompare) \
/* ES6 #sec-string.prototype.padEnd */ \
TFJ(StringPrototypePadEnd, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-string.prototype.padStart */ \
TFJ(StringPrototypePadStart, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-string.prototype.replace */ \
TFJ(StringPrototypeReplace, 2, kReceiver, kSearch, kReplace) \
/* ES6 #sec-string.prototype.search */ \

View File

@ -1330,141 +1330,6 @@ TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
Return(CallJS(callable, context, match_all_func, rx, s));
}
class StringPadAssembler : public StringBuiltinsAssembler {
public:
explicit StringPadAssembler(compiler::CodeAssemblerState* state)
: StringBuiltinsAssembler(state) {}
protected:
enum Variant { kStart, kEnd };
void Generate(Variant variant, const char* method_name, TNode<IntPtrT> argc,
TNode<Context> context) {
CodeStubArguments arguments(this, argc);
TNode<Object> receiver = arguments.GetReceiver();
TNode<String> receiver_string =
ToThisString(context, receiver, method_name);
TNode<Smi> const string_length = LoadStringLengthAsSmi(receiver_string);
TVARIABLE(String, var_fill_string, StringConstant(" "));
TVARIABLE(IntPtrT, var_fill_length, IntPtrConstant(1));
Label check_fill(this), dont_pad(this), invalid_string_length(this),
pad(this);
// If no max_length was provided, return the string.
GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &dont_pad);
TNode<Number> const max_length =
ToLength_Inline(context, arguments.AtIndex(0));
CSA_ASSERT(this, IsNumberNormalized(max_length));
// If max_length <= string_length, return the string.
GotoIfNot(TaggedIsSmi(max_length), &check_fill);
Branch(SmiLessThanOrEqual(CAST(max_length), string_length), &dont_pad,
&check_fill);
BIND(&check_fill);
{
GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &pad);
TNode<Object> const fill = arguments.AtIndex(1);
GotoIf(IsUndefined(fill), &pad);
var_fill_string = ToString_Inline(context, fill);
var_fill_length = LoadStringLengthAsWord(var_fill_string.value());
Branch(WordEqual(var_fill_length.value(), IntPtrConstant(0)), &dont_pad,
&pad);
}
BIND(&pad);
{
CSA_ASSERT(this,
IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)));
// Throw if max_length is greater than String::kMaxLength.
GotoIfNot(TaggedIsSmi(max_length), &invalid_string_length);
TNode<Smi> smi_max_length = CAST(max_length);
GotoIfNot(
SmiLessThanOrEqual(smi_max_length, SmiConstant(String::kMaxLength)),
&invalid_string_length);
CSA_ASSERT(this, SmiGreaterThan(smi_max_length, string_length));
TNode<Smi> const pad_length = SmiSub(smi_max_length, string_length);
VARIABLE(var_pad, MachineRepresentation::kTagged);
Label single_char_fill(this), multi_char_fill(this), return_result(this);
Branch(IntPtrEqual(var_fill_length.value(), IntPtrConstant(1)),
&single_char_fill, &multi_char_fill);
// Fast path for a single character fill. No need to calculate number of
// repetitions or remainder.
BIND(&single_char_fill);
{
var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
static_cast<Node*>(var_fill_string.value()),
pad_length));
Goto(&return_result);
}
BIND(&multi_char_fill);
{
TNode<Int32T> const fill_length_word32 =
TruncateIntPtrToInt32(var_fill_length.value());
TNode<Int32T> const pad_length_word32 = SmiToInt32(pad_length);
TNode<Int32T> const repetitions_word32 =
Int32Div(pad_length_word32, fill_length_word32);
TNode<Int32T> const remaining_word32 =
Int32Mod(pad_length_word32, fill_length_word32);
var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
var_fill_string.value(),
SmiFromInt32(repetitions_word32)));
GotoIfNot(remaining_word32, &return_result);
{
TNode<Object> const remainder_string = CallBuiltin(
Builtins::kStringSubstring, context, var_fill_string.value(),
IntPtrConstant(0), ChangeInt32ToIntPtr(remaining_word32));
var_pad.Bind(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
var_pad.value(), remainder_string));
Goto(&return_result);
}
}
BIND(&return_result);
CSA_ASSERT(this,
SmiEqual(pad_length, LoadStringLengthAsSmi(var_pad.value())));
arguments.PopAndReturn(
variant == kStart
? CallBuiltin(Builtins::kStringAdd_CheckNone, context,
var_pad.value(), receiver_string)
: CallBuiltin(Builtins::kStringAdd_CheckNone, context,
receiver_string, var_pad.value()));
}
BIND(&dont_pad);
arguments.PopAndReturn(receiver_string);
BIND(&invalid_string_length);
{
CallRuntime(Runtime::kThrowInvalidStringLength, context);
Unreachable();
}
}
};
TF_BUILTIN(StringPrototypePadEnd, StringPadAssembler) {
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
Generate(kEnd, "String.prototype.padEnd", argc, context);
}
TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) {
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
Generate(kStart, "String.prototype.padStart", argc, context);
}
// ES6 #sec-string.prototype.search
TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));

111
src/builtins/string-pad.tq Normal file
View File

@ -0,0 +1,111 @@
// 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.
#include 'src/builtins/builtins-string-gen.h'
namespace string {
extern transitioning builtin
StringSubstring(implicit context: Context)(String, intptr, intptr): String;
const kStringPadStart: constexpr int31 = 0;
const kStringPadEnd: constexpr int31 = 1;
transitioning macro StringPad(implicit context: Context)(
receiver: JSAny, arguments: Arguments, methodName: constexpr string,
variant: constexpr int31): String {
const receiverString: String = ToThisString(receiver, methodName);
const stringLength: Smi = receiverString.length_smi;
if (arguments.length == 0) {
return receiverString;
}
const maxLength: Number = ToLength_Inline(context, arguments[0]);
assert(IsNumberNormalized(maxLength));
typeswitch (maxLength) {
case (smiMaxLength: Smi): {
if (smiMaxLength <= stringLength) {
return receiverString;
}
}
case (Number): {
}
}
let fillString: String = ' ';
let fillLength: intptr = 1;
if (arguments.length != 1) {
const fill = arguments[1];
if (fill != Undefined) {
fillString = ToString_Inline(context, fill);
fillLength = fillString.length_intptr;
if (fillLength == 0) {
return receiverString;
}
}
}
// Pad.
assert(fillLength > 0);
// Throw if max_length is greater than String::kMaxLength.
if (!TaggedIsSmi(maxLength)) {
ThrowInvalidStringLength(context);
}
const smiMaxLength: Smi = UnsafeCast<Smi>(maxLength);
if (smiMaxLength > SmiConstant(kStringMaxLength)) {
ThrowInvalidStringLength(context);
}
assert(smiMaxLength > stringLength);
const padLength: Smi = smiMaxLength - stringLength;
let padding: String;
if (fillLength == 1) {
// Single char fill.
// Fast path for a single character fill. No need to calculate number of
// repetitions or remainder.
padding = StringRepeat(context, fillString, padLength);
} else {
// Multi char fill.
const fillLengthWord32: int32 = TruncateIntPtrToInt32(fillLength);
const padLengthWord32: int32 = Convert<int32>(padLength);
const repetitionsWord32: int32 = padLengthWord32 / fillLengthWord32;
const remainingWord32: int32 = padLengthWord32 % fillLengthWord32;
padding =
StringRepeat(context, fillString, Convert<Smi>(repetitionsWord32));
if (remainingWord32 != 0) {
const remainderString =
StringSubstring(fillString, 0, Convert<intptr>(remainingWord32));
padding = padding + remainderString;
}
}
// Return result.
assert(padLength == padding.length_smi);
if (variant == kStringPadStart) {
return padding + receiverString;
}
assert(variant == kStringPadEnd);
return receiverString + padding;
}
// ES6 #sec-string.prototype.padstart
transitioning javascript builtin
StringPrototypePadStart(js-implicit context: Context, receiver: JSAny)(
...arguments): String {
const methodName: constexpr string = 'String.prototype.padStart';
return StringPad(receiver, arguments, methodName, kStringPadStart);
}
// ES6 #sec-string.prototype.padend
transitioning javascript builtin
StringPrototypePadEnd(js-implicit context: Context, receiver: JSAny)(
...arguments): String {
const methodName: constexpr string = 'String.prototype.padEnd';
return StringPad(receiver, arguments, methodName, kStringPadEnd);
}
}