| // 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 { |
| // ES6 #sec-string.prototype.tostring |
| transitioning javascript builtin |
| StringPrototypeToString(js-implicit context: Context, receiver: JSAny)(): |
| JSAny { |
| return ToThisValue(receiver, kString, 'String.prototype.toString'); |
| } |
| |
| // ES6 #sec-string.prototype.valueof |
| transitioning javascript builtin |
| StringPrototypeValueOf(js-implicit context: Context, receiver: JSAny)(): |
| JSAny { |
| return ToThisValue(receiver, kString, 'String.prototype.valueOf'); |
| } |
| |
| extern macro StringBuiltinsAssembler::LoadSurrogatePairAt( |
| String, intptr, intptr, constexpr UnicodeEncoding): int32; |
| extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint( |
| int32): String; |
| |
| // This function assumes StringPrimitiveWithNoCustomIteration is true. |
| transitioning builtin StringToList(implicit context: Context)(string: String): |
| JSArray { |
| const kind = PACKED_ELEMENTS; |
| const stringLength: intptr = string.length_intptr; |
| |
| const nativeContext = LoadNativeContext(context); |
| const map: Map = LoadJSArrayElementsMap(kind, nativeContext); |
| const array: JSArray = AllocateJSArray( |
| kind, map, stringLength, SmiTag(stringLength), |
| kAllowLargeObjectAllocation); |
| const elements = UnsafeCast<FixedArray>(array.elements); |
| const encoding = UTF16; |
| let arrayLength: Smi = 0; |
| let i: intptr = 0; |
| while (i < stringLength) { |
| const ch: int32 = LoadSurrogatePairAt(string, stringLength, i, encoding); |
| const value: String = StringFromSingleUTF16EncodedCodePoint(ch); |
| elements[arrayLength] = value; |
| // Increment and continue the loop. |
| i = i + value.length_intptr; |
| arrayLength++; |
| } |
| assert(arrayLength >= 0); |
| assert(SmiTag(stringLength) >= arrayLength); |
| array.length = arrayLength; |
| |
| return array; |
| } |
| |
| transitioning macro GenerateStringAt(implicit context: Context)( |
| receiver: JSAny, position: JSAny, |
| methodName: constexpr string): never labels |
| IfInBounds(String, intptr, intptr), IfOutOfBounds { |
| // Check that {receiver} is coercible to Object and convert it to a String. |
| const string: String = ToThisString(receiver, methodName); |
| // Convert the {position} to a Smi and check that it's in bounds of |
| // the {string}. |
| const indexNumber: Number = |
| ToInteger_Inline(context, position, kTruncateMinusZero); |
| if (TaggedIsNotSmi(indexNumber)) goto IfOutOfBounds; |
| const index: intptr = SmiUntag(UnsafeCast<Smi>(indexNumber)); |
| const length: intptr = string.length_intptr; |
| if (Convert<uintptr>(index) >= Convert<uintptr>(length)) goto IfOutOfBounds; |
| goto IfInBounds(string, index, length); |
| } |
| |
| // ES6 #sec-string.prototype.charat |
| transitioning javascript builtin StringPrototypeCharAt( |
| js-implicit context: Context, receiver: JSAny)(position: JSAny): JSAny { |
| try { |
| GenerateStringAt(receiver, position, 'String.prototype.charAt') |
| otherwise IfInBounds, IfOutOfBounds; |
| } |
| label IfInBounds(string: String, index: intptr, _length: intptr) { |
| const code: int32 = StringCharCodeAt(string, index); |
| return StringFromSingleCharCode(code); |
| } |
| label IfOutOfBounds { |
| return kEmptyString; |
| } |
| } |
| |
| // ES6 #sec-string.prototype.charcodeat |
| transitioning javascript builtin StringPrototypeCharCodeAt( |
| js-implicit context: Context, receiver: JSAny)(position: JSAny): JSAny { |
| try { |
| GenerateStringAt(receiver, position, 'String.prototype.charCodeAt') |
| otherwise IfInBounds, IfOutOfBounds; |
| } |
| label IfInBounds(string: String, index: intptr, _length: intptr) { |
| const code: int32 = StringCharCodeAt(string, index); |
| return Convert<Smi>(code); |
| } |
| label IfOutOfBounds { |
| return kNaN; |
| } |
| } |
| |
| // ES6 #sec-string.prototype.codepointat |
| transitioning javascript builtin StringPrototypeCodePointAt( |
| js-implicit context: Context, receiver: JSAny)(position: JSAny): JSAny { |
| try { |
| GenerateStringAt(receiver, position, 'String.prototype.codePointAt') |
| otherwise IfInBounds, IfOutOfBounds; |
| } |
| label IfInBounds(string: String, index: intptr, length: intptr) { |
| // This is always a call to a builtin from Javascript, so we need to |
| // produce UTF32. |
| const code: int32 = LoadSurrogatePairAt(string, length, index, UTF32); |
| return Convert<Smi>(code); |
| } |
| label IfOutOfBounds { |
| return Undefined; |
| } |
| } |
| |
| // ES6 String.prototype.concat(...args) |
| // ES6 #sec-string.prototype.concat |
| transitioning javascript builtin StringPrototypeConcat( |
| js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { |
| // Check that {receiver} is coercible to Object and convert it to a String. |
| let string: String = ToThisString(receiver, 'String.prototype.concat'); |
| |
| // Concatenate all the arguments passed to this builtin. |
| const length: intptr = Convert<intptr>(arguments.length); |
| for (let i: intptr = 0; i < length; i++) { |
| const temp: String = ToString_Inline(context, arguments[i]); |
| string = string + temp; |
| } |
| return string; |
| } |
| |
| extern transitioning runtime |
| SymbolDescriptiveString(implicit context: Context)(Symbol): String; |
| |
| // ES #sec-string-constructor |
| // https://tc39.github.io/ecma262/#sec-string-constructor |
| transitioning javascript builtin StringConstructor( |
| js-implicit context: Context, receiver: JSAny, newTarget: JSAny, |
| target: JSFunction)(...arguments): JSAny { |
| const length: intptr = Convert<intptr>(arguments.length); |
| let s: String; |
| // 1. If no arguments were passed to this function invocation, let s be "". |
| if (length == 0) { |
| s = EmptyStringConstant(); |
| } else { |
| // 2. Else, |
| // 2. a. If NewTarget is undefined and Type(value) is Symbol, return |
| // SymbolDescriptiveString(value). |
| if (newTarget == Undefined) { |
| typeswitch (arguments[0]) { |
| case (value: Symbol): { |
| return SymbolDescriptiveString(value); |
| } |
| case (JSAny): { |
| } |
| } |
| } |
| // 2. b. Let s be ? ToString(value). |
| s = ToString_Inline(context, arguments[0]); |
| } |
| // 3. If NewTarget is undefined, return s. |
| if (newTarget == Undefined) { |
| return s; |
| } |
| // 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, |
| // "%String.prototype%")). |
| const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget)); |
| const obj = |
| UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map)); |
| obj.value = s; |
| return obj; |
| } |
| |
| transitioning builtin StringAddConvertLeft(implicit context: Context)( |
| left: JSAny, right: String): String { |
| return ToStringImpl(context, ToPrimitiveDefault(left)) + right; |
| } |
| |
| transitioning builtin StringAddConvertRight(implicit context: Context)( |
| left: String, right: JSAny): String { |
| return left + ToStringImpl(context, ToPrimitiveDefault(right)); |
| } |
| } |