string-pad.tq 3.58 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 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;
    }
24
    const maxLength: Number = ToLength_Inline(arguments[0]);
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
    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) {
43
        fillString = ToString_Inline(fill);
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
        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
98
  StringPrototypePadStart(js-implicit context: NativeContext, receiver: JSAny)(
99 100 101 102 103 104 105
      ...arguments): String {
    const methodName: constexpr string = 'String.prototype.padStart';
    return StringPad(receiver, arguments, methodName, kStringPadStart);
  }

  // ES6 #sec-string.prototype.padend
  transitioning javascript builtin
106
  StringPrototypePadEnd(js-implicit context: NativeContext, receiver: JSAny)(
107 108 109 110 111
      ...arguments): String {
    const methodName: constexpr string = 'String.prototype.padEnd';
    return StringPad(receiver, arguments, methodName, kStringPadEnd);
  }
}