1// Copyright 2019 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include 'src/builtins/builtins-string-gen.h' 6 7namespace string { 8 9extern enum TrimMode extends uint31 constexpr 'String::TrimMode' { 10 kTrim, 11 kTrimStart, 12 kTrimEnd 13} 14 15@export 16macro IsWhiteSpaceOrLineTerminator(charCode: char16|char8): bool { 17 // 0x0020 - SPACE (Intentionally out of order to fast path a commmon case) 18 if (charCode == 0x0020) { 19 return true; 20 } 21 22 // Common Non-whitespace characters from (0x000E, 0x00A0) 23 if (Unsigned(Convert<int32>(charCode) - 0x000E) < 0x0092) { 24 return false; 25 } 26 27 // 0x0009 - HORIZONTAL TAB 28 if (charCode < 0x0009) { 29 return false; 30 } 31 // 0x000A - LINE FEED OR NEW LINE 32 // 0x000B - VERTICAL TAB 33 // 0x000C - FORMFEED 34 // 0x000D - HORIZONTAL TAB 35 if (charCode <= 0x000D) { 36 return true; 37 } 38 39 // 0x00A0 - NO-BREAK SPACE 40 if (charCode == 0x00A0) { 41 return true; 42 } 43 44 // 0x1680 - Ogham Space Mark 45 if (charCode == 0x1680) { 46 return true; 47 } 48 49 // 0x2000 - EN QUAD 50 if (charCode < 0x2000) { 51 return false; 52 } 53 // 0x2001 - EM QUAD 54 // 0x2002 - EN SPACE 55 // 0x2003 - EM SPACE 56 // 0x2004 - THREE-PER-EM SPACE 57 // 0x2005 - FOUR-PER-EM SPACE 58 // 0x2006 - SIX-PER-EM SPACE 59 // 0x2007 - FIGURE SPACE 60 // 0x2008 - PUNCTUATION SPACE 61 // 0x2009 - THIN SPACE 62 // 0x200A - HAIR SPACE 63 if (charCode <= 0x200A) { 64 return true; 65 } 66 67 // 0x2028 - LINE SEPARATOR 68 if (charCode == 0x2028) { 69 return true; 70 } 71 // 0x2029 - PARAGRAPH SEPARATOR 72 if (charCode == 0x2029) { 73 return true; 74 } 75 // 0x202F - NARROW NO-BREAK SPACE 76 if (charCode == 0x202F) { 77 return true; 78 } 79 // 0x205F - MEDIUM MATHEMATICAL SPACE 80 if (charCode == 0x205F) { 81 return true; 82 } 83 // 0xFEFF - BYTE ORDER MARK 84 if (charCode == 0xFEFF) { 85 return true; 86 } 87 // 0x3000 - IDEOGRAPHIC SPACE 88 if (charCode == 0x3000) { 89 return true; 90 } 91 92 return false; 93} 94 95transitioning macro StringTrimLoop<T: type>(implicit context: Context)( 96 stringSlice: ConstSlice<T>, startIndex: intptr, endIndex: intptr, 97 increment: intptr): intptr { 98 let index = startIndex; 99 while (true) { 100 if (index == endIndex) { 101 return index; 102 } 103 104 const char: T = *stringSlice.AtIndex(index); 105 if (!IsWhiteSpaceOrLineTerminator(char)) { 106 return index; 107 } 108 index = index + increment; 109 } 110 unreachable; 111} 112 113transitioning macro StringTrimBody<T: type>(implicit context: Context)( 114 string: String, slice: ConstSlice<T>, variant: constexpr TrimMode): String { 115 const stringLength: intptr = string.length_intptr; 116 117 let startIndex: intptr = 0; 118 let endIndex: intptr = stringLength - 1; 119 if (variant == TrimMode::kTrim || variant == TrimMode::kTrimStart) { 120 startIndex = StringTrimLoop(slice, startIndex, stringLength, 1); 121 if (startIndex == stringLength) { 122 return kEmptyString; 123 } 124 } 125 126 if (variant == TrimMode::kTrim || variant == TrimMode::kTrimEnd) { 127 endIndex = StringTrimLoop(slice, endIndex, -1, -1); 128 if (endIndex == -1) { 129 return kEmptyString; 130 } 131 } 132 133 return SubString(string, Unsigned(startIndex), Unsigned(endIndex + 1)); 134} 135 136transitioning macro StringTrim(implicit context: Context)( 137 receiver: JSAny, _arguments: Arguments, methodName: constexpr string, 138 variant: constexpr TrimMode): String { 139 const receiverString: String = ToThisString(receiver, methodName); 140 141 try { 142 StringToSlice(receiverString) otherwise OneByte, TwoByte; 143 } label OneByte(slice: ConstSlice<char8>) { 144 return StringTrimBody(receiverString, slice, variant); 145 } label TwoByte(slice: ConstSlice<char16>) { 146 return StringTrimBody(receiverString, slice, variant); 147 } 148} 149 150// ES6 #sec-string.prototype.trim 151transitioning javascript builtin 152StringPrototypeTrim( 153 js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { 154 const methodName: constexpr string = 'String.prototype.trim'; 155 return StringTrim(receiver, arguments, methodName, TrimMode::kTrim); 156} 157 158// https://github.com/tc39/proposal-string-left-right-trim 159transitioning javascript builtin 160StringPrototypeTrimStart( 161 js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { 162 const methodName: constexpr string = 'String.prototype.trimLeft'; 163 return StringTrim(receiver, arguments, methodName, TrimMode::kTrimStart); 164} 165 166// https://github.com/tc39/proposal-string-left-right-trim 167transitioning javascript builtin 168StringPrototypeTrimEnd( 169 js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { 170 const methodName: constexpr string = 'String.prototype.trimRight'; 171 return StringTrim(receiver, arguments, methodName, TrimMode::kTrimEnd); 172} 173} 174