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 9namespace runtime { 10extern transitioning runtime ToString(Context, JSAny): String; 11} 12 13@export 14transitioning macro ToStringImpl(context: Context, o: JSAny): String { 15 let result: JSAny = o; 16 while (true) { 17 typeswitch (result) { 18 case (num: Number): { 19 return NumberToString(num); 20 } 21 case (str: String): { 22 return str; 23 } 24 case (oddball: Oddball): { 25 return oddball.to_string; 26 } 27 case (JSReceiver): { 28 result = NonPrimitiveToPrimitive_String(context, result); 29 continue; 30 } 31 case (Symbol): { 32 ThrowTypeError(MessageTemplate::kSymbolToString); 33 } 34 case (JSAny): { 35 return runtime::ToString(context, o); 36 } 37 } 38 } 39 unreachable; 40} 41 42transitioning builtin ToString(context: Context, o: JSAny): String { 43 return ToStringImpl(context, o); 44} 45 46extern macro StringBuiltinsAssembler::SubString( 47 String, uintptr, uintptr): String; 48 49// ES6 #sec-string.prototype.tostring 50transitioning javascript builtin 51StringPrototypeToString( 52 js-implicit context: NativeContext, receiver: JSAny)(): JSAny { 53 return ToThisValue( 54 receiver, PrimitiveType::kString, 'String.prototype.toString'); 55} 56 57// ES6 #sec-string.prototype.valueof 58transitioning javascript builtin 59StringPrototypeValueOf( 60 js-implicit context: NativeContext, receiver: JSAny)(): JSAny { 61 return ToThisValue( 62 receiver, PrimitiveType::kString, 'String.prototype.valueOf'); 63} 64 65extern macro StringBuiltinsAssembler::LoadSurrogatePairAt( 66 String, intptr, intptr, constexpr UnicodeEncoding): int32; 67extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint( 68 int32): String; 69 70// This function assumes StringPrimitiveWithNoCustomIteration is true. 71transitioning builtin StringToList(implicit context: Context)(string: String): 72 JSArray { 73 const kind = ElementsKind::PACKED_ELEMENTS; 74 const stringLength: intptr = string.length_intptr; 75 76 const nativeContext = LoadNativeContext(context); 77 const map: Map = LoadJSArrayElementsMap(kind, nativeContext); 78 const array: JSArray = AllocateJSArray( 79 kind, map, stringLength, SmiTag(stringLength), 80 AllocationFlag::kAllowLargeObjectAllocation); 81 const elements = UnsafeCast<FixedArray>(array.elements); 82 const encoding = UnicodeEncoding::UTF16; 83 let arrayLength: Smi = 0; 84 let i: intptr = 0; 85 while (i < stringLength) { 86 const ch: int32 = LoadSurrogatePairAt(string, stringLength, i, encoding); 87 const value: String = StringFromSingleUTF16EncodedCodePoint(ch); 88 elements[arrayLength] = value; 89 // Increment and continue the loop. 90 i = i + value.length_intptr; 91 arrayLength++; 92 } 93 assert(arrayLength >= 0); 94 assert(SmiTag(stringLength) >= arrayLength); 95 array.length = arrayLength; 96 97 return array; 98} 99 100transitioning macro GenerateStringAt(implicit context: Context)( 101 receiver: JSAny, position: JSAny, 102 methodName: constexpr string): never labels 103IfInBounds(String, uintptr, uintptr), IfOutOfBounds { 104 // 1. Let O be ? RequireObjectCoercible(this value). 105 // 2. Let S be ? ToString(O). 106 const string: String = ToThisString(receiver, methodName); 107 108 // 3. Let position be ? ToInteger(pos). 109 const indexNumber: Number = ToInteger_Inline(position); 110 111 // Convert the {position} to a uintptr and check that it's in bounds of 112 // the {string}. 113 typeswitch (indexNumber) { 114 case (indexSmi: Smi): { 115 const length: uintptr = string.length_uintptr; 116 const index: uintptr = Unsigned(Convert<intptr>(indexSmi)); 117 // Max string length fits Smi range, so we can do an unsigned bounds 118 // check. 119 const kMaxStringLengthFitsSmi: constexpr bool = 120 kStringMaxLengthUintptr < kSmiMaxValue; 121 static_assert(kMaxStringLengthFitsSmi); 122 if (index >= length) goto IfOutOfBounds; 123 goto IfInBounds(string, index, length); 124 } 125 case (indexHeapNumber: HeapNumber): { 126 assert(IsNumberNormalized(indexHeapNumber)); 127 // Valid string indices fit into Smi range, so HeapNumber index is 128 // definitely an out of bounds case. 129 goto IfOutOfBounds; 130 } 131 } 132} 133 134// ES6 #sec-string.prototype.charat 135transitioning javascript builtin StringPrototypeCharAt( 136 js-implicit context: NativeContext, 137 receiver: JSAny)(position: JSAny): JSAny { 138 try { 139 GenerateStringAt(receiver, position, 'String.prototype.charAt') 140 otherwise IfInBounds, IfOutOfBounds; 141 } label IfInBounds(string: String, index: uintptr, _length: uintptr) { 142 const code: int32 = StringCharCodeAt(string, index); 143 return StringFromSingleCharCode(code); 144 } label IfOutOfBounds { 145 return kEmptyString; 146 } 147} 148 149// ES6 #sec-string.prototype.charcodeat 150transitioning javascript builtin StringPrototypeCharCodeAt( 151 js-implicit context: NativeContext, 152 receiver: JSAny)(position: JSAny): JSAny { 153 try { 154 GenerateStringAt(receiver, position, 'String.prototype.charCodeAt') 155 otherwise IfInBounds, IfOutOfBounds; 156 } label IfInBounds(string: String, index: uintptr, _length: uintptr) { 157 const code: int32 = StringCharCodeAt(string, index); 158 return Convert<Smi>(code); 159 } label IfOutOfBounds { 160 return kNaN; 161 } 162} 163 164// ES6 #sec-string.prototype.codepointat 165transitioning javascript builtin StringPrototypeCodePointAt( 166 js-implicit context: NativeContext, 167 receiver: JSAny)(position: JSAny): JSAny { 168 try { 169 GenerateStringAt(receiver, position, 'String.prototype.codePointAt') 170 otherwise IfInBounds, IfOutOfBounds; 171 } label IfInBounds(string: String, index: uintptr, length: uintptr) { 172 // This is always a call to a builtin from Javascript, so we need to 173 // produce UTF32. 174 const code: int32 = LoadSurrogatePairAt( 175 string, Signed(length), Signed(index), UnicodeEncoding::UTF32); 176 return Convert<Smi>(code); 177 } label IfOutOfBounds { 178 return Undefined; 179 } 180} 181 182// ES6 String.prototype.concat(...args) 183// ES6 #sec-string.prototype.concat 184transitioning javascript builtin StringPrototypeConcat( 185 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 186 // Check that {receiver} is coercible to Object and convert it to a String. 187 let string: String = ToThisString(receiver, 'String.prototype.concat'); 188 189 // Concatenate all the arguments passed to this builtin. 190 const length: intptr = Convert<intptr>(arguments.length); 191 for (let i: intptr = 0; i < length; i++) { 192 const temp: String = ToString_Inline(arguments[i]); 193 string = string + temp; 194 } 195 return string; 196} 197 198extern transitioning runtime 199SymbolDescriptiveString(implicit context: Context)(Symbol): String; 200 201// ES #sec-string-constructor 202// https://tc39.github.io/ecma262/#sec-string-constructor 203transitioning javascript builtin StringConstructor( 204 js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, 205 target: JSFunction)(...arguments): JSAny { 206 const length: intptr = Convert<intptr>(arguments.length); 207 let s: String; 208 // 1. If no arguments were passed to this function invocation, let s be "". 209 if (length == 0) { 210 s = EmptyStringConstant(); 211 } else { 212 // 2. Else, 213 // 2. a. If NewTarget is undefined and Type(value) is Symbol, return 214 // SymbolDescriptiveString(value). 215 if (newTarget == Undefined) { 216 typeswitch (arguments[0]) { 217 case (value: Symbol): { 218 return SymbolDescriptiveString(value); 219 } 220 case (JSAny): { 221 } 222 } 223 } 224 // 2. b. Let s be ? ToString(value). 225 s = ToString_Inline(arguments[0]); 226 } 227 // 3. If NewTarget is undefined, return s. 228 if (newTarget == Undefined) { 229 return s; 230 } 231 // 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, 232 // "%String.prototype%")). 233 const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget)); 234 const obj = 235 UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map)); 236 obj.value = s; 237 return obj; 238} 239 240transitioning builtin StringAddConvertLeft(implicit context: Context)( 241 left: JSAny, right: String): String { 242 return ToStringImpl(context, ToPrimitiveDefault(left)) + right; 243} 244 245transitioning builtin StringAddConvertRight(implicit context: Context)( 246 left: String, right: JSAny): String { 247 return left + ToStringImpl(context, ToPrimitiveDefault(right)); 248} 249 250builtin StringCharAt(implicit context: Context)( 251 receiver: String, position: uintptr): String { 252 // Load the character code at the {position} from the {receiver}. 253 const code: int32 = StringCharCodeAt(receiver, position); 254 // And return the single character string with only that {code} 255 return StringFromSingleCharCode(code); 256} 257} 258