[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:
parent
1f4bec2775
commit
04256154e1
1
BUILD.gn
1
BUILD.gn
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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 */ \
|
||||
|
@ -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
111
src/builtins/string-pad.tq
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user