// 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 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(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(indexNumber)); const length: intptr = string.length_intptr; if (Convert(index) >= Convert(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(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(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(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(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(newTarget)); const obj = UnsafeCast(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)); } }