1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_BUILTINS_BUILTINS_STRING_H
17#define ECMASCRIPT_BUILTINS_BUILTINS_STRING_H
18
19#include "ecmascript/base/builtins_base.h"
20#include "ecmascript/ecma_runtime_call_info.h"
21#include "ecmascript/js_tagged_value.h"
22
23// List of functions in String, excluding the '@@' properties.
24// V(name, func, length, stubIndex)
25// where BuiltinsString::func refers to the native implementation of String[name].
26//       kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available.
27#define BUILTIN_STRING_FUNCTIONS(V)                             \
28    /* String.fromCharCode ( ...codeUnits ) */                  \
29    V("fromCharCode",  FromCharCode,  1, StringFromCharCode)    \
30    /* String.fromCodePoint ( ...codePoints ) */                \
31    V("fromCodePoint", FromCodePoint, 1, INVALID)               \
32    /* String.raw ( template, ...substitutions ) */             \
33    V("raw",           Raw,           1, INVALID)
34
35// List of functions in String.prototype, excluding the constructor and '@@' properties.
36// V(name, func, length, stubIndex)
37// where BuiltinsString::func refers to the native implementation of String.prototype[name].
38// The following functions in String.prototype are not implemented yet:
39//   - String.prototype.isWellFormed ( )
40//   - String.prototype.toWellFormed ( )
41#define BUILTIN_STRING_PROTOTYPE_FUNCTIONS(V)                                       \
42    /* String.prototype.at ( index ) */                                             \
43    V("at",                At,                1, INVALID)                           \
44    /* String.prototype.charAt ( pos ) */                                           \
45    V("charAt",            CharAt,            1, StringCharAt)                      \
46    /* String.prototype.charCodeAt ( pos ) */                                       \
47    V("charCodeAt",        CharCodeAt,        1, StringCharCodeAt)                  \
48    /* String.prototype.codePointAt ( pos ) */                                      \
49    V("codePointAt",       CodePointAt,       1, StringCodePointAt)                 \
50    /* String.prototype.concat ( ...args ) */                                       \
51    V("concat",            Concat,            1, StringConcat)                      \
52    /* String.prototype.endsWith ( searchString [ , endPosition ] ) */              \
53    V("endsWith",          EndsWith,          1, StringEndsWith)                    \
54    /* String.prototype.includes ( searchString [ , position ] ) */                 \
55    V("includes",          Includes,          1, INVALID)                           \
56    /* String.prototype.indexOf ( searchString [ , position ] ) */                  \
57    V("indexOf",           IndexOf,           1, StringIndexOf)                     \
58    /* String.prototype.lastIndexOf ( searchString [ , position ] ) */              \
59    V("lastIndexOf",       LastIndexOf,       1, INVALID)                           \
60    /* String.prototype.localeCompare ( that [ , reserved1 [ , reserved2 ] ] ) */   \
61    V("localeCompare",     LocaleCompare,     1, StringLocaleCompare)               \
62    /* String.prototype.match ( regexp ) */                                         \
63    V("match",             Match,             1, INVALID)                           \
64    /* String.prototype.matchAll ( regexp ) */                                      \
65    V("matchAll",          MatchAll,          1, INVALID)                           \
66    /* String.prototype.normalize ( [ form ] ) */                                   \
67    V("normalize",         Normalize,         0, INVALID)                           \
68    /* String.prototype.isWellFormed ( ) */                                         \
69    V("isWellFormed",      IsWellFormed,      0, INVALID)                           \
70    /* String.prototype.toWellFormed ( ) */                                         \
71    V("toWellFormed",      ToWellFormed,      0, INVALID)                           \
72    /* String.prototype.padEnd ( maxLength [ , fillString ] ) */                    \
73    V("padEnd",            PadEnd,            1, StringPadEnd)                      \
74    /* String.prototype.padStart ( maxLength [ , fillString ] ) */                  \
75    V("padStart",          PadStart,          1, StringPadStart)                    \
76    /* String.prototype.repeat ( count ) */                                         \
77    V("repeat",            Repeat,            1, INVALID)                           \
78    /* String.prototype.replace ( searchValue, replaceValue ) */                    \
79    V("replace",           Replace,           2, StringReplace)                     \
80    /* String.prototype.replaceAll ( searchValue, replaceValue ) */                 \
81    V("replaceAll",        ReplaceAll,        2, INVALID)                           \
82    /* String.prototype.search ( regexp ) */                                        \
83    V("search",            Search,            1, INVALID)                           \
84    /* String.prototype.slice ( start, end ) */                                     \
85    V("slice",             Slice,             2, StringSlice)                       \
86    /* String.prototype.split ( separator, limit ) */                               \
87    V("split",             Split,             2, INVALID)                           \
88    /* String.prototype.startsWith ( searchString [ , position ] ) */               \
89    V("startsWith",        StartsWith,        1, StringStartsWith)                  \
90    /* In Annex B.2.2: Additional Properties of the String.prototype Object */      \
91    /* String.prototype.substr ( start, length ) */                                 \
92    V("substr",            SubStr,            2, StringSubStr)                      \
93    /* String.prototype.substring ( start, end ) */                                 \
94    V("substring",         Substring,         2, StringSubstring)                   \
95    /* String.prototype.toLocaleLowerCase ( [ reserved1 [ , reserved2 ] ] ) */      \
96    V("toLocaleLowerCase", ToLocaleLowerCase, 0, INVALID)                           \
97    /* String.prototype.toLocaleUpperCase ( [ reserved1 [ , reserved2 ] ] ) */      \
98    V("toLocaleUpperCase", ToLocaleUpperCase, 0, INVALID)                           \
99    /* String.prototype.toLowerCase ( ) */                                          \
100    V("toLowerCase",       ToLowerCase,       0, StringToLowerCase)                 \
101    /* String.prototype.toString ( ) */                                             \
102    V("toString",          ToString,          0, INVALID)                           \
103    /* String.prototype.toUpperCase ( ) */                                          \
104    V("toUpperCase",       ToUpperCase,       0, INVALID)                           \
105    /* String.prototype.trim ( ) */                                                 \
106    V("trim",              Trim,              0, StringTrim)                        \
107    /* String.prototype.trimEnd ( ) */                                              \
108    V("trimEnd",           TrimEnd,           0, StringTrimEnd)                     \
109    /* In Annex B.2.2: Additional Properties of the String.prototype Object */      \
110    /* Equivalent to trimStart. For compatibility only. */                          \
111    /* String.prototype.trimLeft ( ) */                                             \
112    V("trimLeft",          TrimLeft,          0, StringTrimLeft)                    \
113    /* In Annex B.2.2: Additional Properties of the String.prototype Object */      \
114    /* Equivalent to trimEnd. For compatibility only. */                            \
115    /* String.prototype.trimEnd ( ) */                                              \
116    V("trimRight",         TrimRight,         0, StringTrimRight)                   \
117    /* String.prototype.trimStart ( ) */                                            \
118    V("trimStart",         TrimStart,         0, StringTrimStart)                   \
119    /* String.prototype.valueOf ( ) */                                              \
120    V("valueOf",           ValueOf,           0, INVALID)
121
122namespace panda::ecmascript {
123enum class CompareStringsOption : uint8_t;
124}
125
126namespace panda::ecmascript::builtins {
127constexpr int32_t ENCODE_MAX_UTF16 = 0X10FFFF;
128constexpr uint16_t ENCODE_LEAD_LOW = 0xD800;
129constexpr uint16_t ENCODE_TRAIL_LOW = 0xDC00;
130constexpr uint32_t ENCODE_FIRST_FACTOR = 0x400;
131constexpr uint32_t ENCODE_SECOND_FACTOR = 0x10000;
132constexpr double DOUBLE_INT_MAX = static_cast<double>(INT_MAX);
133constexpr double DOUBLE_INT_MIN = static_cast<double>(INT_MIN);
134
135class BuiltinsString : public base::BuiltinsBase {
136public:
137    // 21.1.1.1
138    static JSTaggedValue StringConstructor(EcmaRuntimeCallInfo *argv);
139    // 21.1.2.1
140    static JSTaggedValue FromCharCode(EcmaRuntimeCallInfo *argv);
141    // 21.1.2.2
142    static JSTaggedValue FromCodePoint(EcmaRuntimeCallInfo *argv);
143    // 21.1.2.4
144    static JSTaggedValue Raw(EcmaRuntimeCallInfo *argv);
145
146    static JSTaggedValue GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched,
147                                         const JSHandle<EcmaString> &srcString, int position,
148                                         const JSHandle<TaggedArray> &captureList,
149                                         const JSHandle<JSTaggedValue> &namedCaptures,
150                                         const JSHandle<EcmaString> &replacement);
151    // 21.1.3.1
152    static JSTaggedValue CharAt(EcmaRuntimeCallInfo *argv);
153    // 21.1.3.2
154    static JSTaggedValue CharCodeAt(EcmaRuntimeCallInfo *argv);
155    // 21.1.3.3
156    static JSTaggedValue CodePointAt(EcmaRuntimeCallInfo *argv);
157    // 21.1.3.4
158    static JSTaggedValue Concat(EcmaRuntimeCallInfo *argv);
159    // 21.1.3.5 String.prototype.constructor
160    // 21.1.3.6
161    static JSTaggedValue EndsWith(EcmaRuntimeCallInfo *argv);
162    // 21.1.3.7
163    static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv);
164    // 21.1.3.8
165    static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv);
166    // 21.1.3.9
167    static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv);
168    // 21.1.3.10
169    static JSTaggedValue LocaleCompare(EcmaRuntimeCallInfo *argv);
170    static JSTaggedValue DoLocaleCompare(JSThread *thread,
171                                         const JSHandle<EcmaString> &thisHandle,
172                                         const JSHandle<EcmaString> &thatHandle,
173                                         const JSHandle<JSTaggedValue> &locales,
174                                         const JSHandle<JSTaggedValue> &options);
175    static JSTaggedValue LocaleCompareGC(JSThread *thread,
176                                         const JSHandle<EcmaString> &thisHandle,
177                                         const JSHandle<EcmaString> &thatHandle,
178                                         const JSHandle<JSTaggedValue> &locales,
179                                         const JSHandle<JSTaggedValue> &options,
180                                         CompareStringsOption csOption,
181                                         bool cacheable);
182    // 21.1.3.11
183    static JSTaggedValue Match(EcmaRuntimeCallInfo *argv);
184
185    static JSTaggedValue MatchAll(EcmaRuntimeCallInfo *argv);
186    // 21.1.3.12
187    static JSTaggedValue Normalize(EcmaRuntimeCallInfo *argv);
188
189    static JSTaggedValue IsWellFormed(EcmaRuntimeCallInfo *argv);
190
191    static JSTaggedValue ToWellFormed(EcmaRuntimeCallInfo *argv);
192
193    static JSTaggedValue PadStart(EcmaRuntimeCallInfo *argv);
194
195    static JSTaggedValue PadEnd(EcmaRuntimeCallInfo *argv);
196    // 21.1.3.13
197    static JSTaggedValue Repeat(EcmaRuntimeCallInfo *argv);
198    // 21.1.3.14
199    static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv);
200    // 21.1.3.14.1 Runtime Semantics: GetSubstitution()
201    static JSTaggedValue ReplaceAll(EcmaRuntimeCallInfo *argv);
202    // 21.1.3.15
203    static JSTaggedValue Search(EcmaRuntimeCallInfo *argv);
204    // 21.1.3.16
205    static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv);
206    // 21.1.3.17
207    static JSTaggedValue Split(EcmaRuntimeCallInfo *argv);
208    // 21.1.3.18
209    static JSTaggedValue StartsWith(EcmaRuntimeCallInfo *argv);
210    // 21.1.3.19
211    static JSTaggedValue Substring(EcmaRuntimeCallInfo *argv);
212    // 21.1.3.20
213    static JSTaggedValue ToLocaleLowerCase(EcmaRuntimeCallInfo *argv);
214    // 21.1.3.21
215    static JSTaggedValue ToLocaleUpperCase(EcmaRuntimeCallInfo *argv);
216    // 21.1.3.22
217    static JSTaggedValue ToLowerCase(EcmaRuntimeCallInfo *argv);
218    // 21.1.3.23
219    static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv);
220    // 21.1.3.24
221    static JSTaggedValue ToUpperCase(EcmaRuntimeCallInfo *argv);
222    // 21.1.3.25
223    static JSTaggedValue Trim(EcmaRuntimeCallInfo *argv);
224
225    static JSTaggedValue TrimStart(EcmaRuntimeCallInfo *argv);
226
227    static JSTaggedValue TrimEnd(EcmaRuntimeCallInfo *argv);
228
229    static JSTaggedValue TrimLeft(EcmaRuntimeCallInfo *argv);
230
231    static JSTaggedValue TrimRight(EcmaRuntimeCallInfo *argv);
232    // 21.1.3.26
233    static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv);
234    // 21.1.3.27
235    static JSTaggedValue GetStringIterator(EcmaRuntimeCallInfo *argv);
236    // 21.1.3
237    static JSTaggedValue ThisStringValue(JSThread *thread, JSTaggedValue value);
238    // 21.1.2.27
239    static JSTaggedValue CreateIterator(EcmaRuntimeCallInfo *argv);
240    // 10.1.2
241    static uint16_t UTF16Decode(uint16_t lead, uint16_t trail);
242    // annexB B.2.3.1
243    static JSTaggedValue SubStr(EcmaRuntimeCallInfo *argv);
244    // 22.1.3.1
245    static JSTaggedValue At(EcmaRuntimeCallInfo *argv);
246
247    static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv);
248
249    // Excluding the '@@' internal properties
250    static Span<const base::BuiltinFunctionEntry> GetStringFunctions()
251    {
252        return Span<const base::BuiltinFunctionEntry>(STRING_FUNCTIONS);
253    }
254
255    // Excluding the constructor and '@@' internal properties.
256    static Span<const base::BuiltinFunctionEntry> GetStringPrototypeFunctions()
257    {
258        return Span<const base::BuiltinFunctionEntry>(STRING_PROTOTYPE_FUNCTIONS);
259    }
260
261    static size_t GetNumPrototypeInlinedProperties()
262    {
263        // 3 : 3 more inline properties in String.prototype:
264        //   (1) String.prototype.constructor
265        //   (2) String.prototype [ @@iterator ]
266        //   (3) get length
267        return GetStringPrototypeFunctions().Size() + 3;
268    }
269    static JSTaggedValue StringToList(JSThread *thread, JSHandle<EcmaString> &str);
270    static JSTaggedValue StringToSList(JSThread *thread, JSHandle<EcmaString> &str);
271
272private:
273#define BUILTIN_STRING_FUNCTION_ENTRY(name, method, length, builtinId) \
274    base::BuiltinFunctionEntry::Create(name, BuiltinsString::method, length, kungfu::BuiltinsStubCSigns::builtinId),
275
276    static constexpr std::array STRING_FUNCTIONS = {
277        BUILTIN_STRING_FUNCTIONS(BUILTIN_STRING_FUNCTION_ENTRY)
278    };
279    static constexpr std::array STRING_PROTOTYPE_FUNCTIONS = {
280        BUILTIN_STRING_PROTOTYPE_FUNCTIONS(BUILTIN_STRING_FUNCTION_ENTRY)
281    };
282#undef BUILTIN_STRING_FUNCTION_ENTRY
283
284    static JSTaggedValue Pad(EcmaRuntimeCallInfo *argv, bool isStart);
285    static int32_t ConvertDoubleToInt(double d);
286    static JSTaggedValue CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm,
287        const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim = UINT32_MAX - 1);
288    static JSTaggedValue CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm,
289        const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
290        uint32_t thisLength, uint32_t seperatorLength, uint32_t lim);
291    static JSTaggedValue CreateArrayThisStringAndSeperatorStringAreNotEmpty(
292        JSThread *thread, EcmaVM *ecmaVm,
293        const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
294        uint32_t thisLength, uint32_t seperatorLength, uint32_t lim = UINT32_MAX - 1);
295    static bool IsUTF16HighSurrogate(uint16_t ch)
296    {
297        return base::utf_helper::DECODE_LEAD_LOW <= ch && ch <= base::utf_helper::DECODE_LEAD_HIGH;
298    }
299    static bool IsUTF16LowSurrogate(uint16_t ch)
300    {
301        return base::utf_helper::DECODE_TRAIL_LOW <= ch && ch <= base::utf_helper::DECODE_TRAIL_HIGH;
302    }
303    static uint32_t UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail);
304    // 21.1.3.17.1
305};
306
307class StringSplitResultCache : public TaggedArray {
308public:
309    static StringSplitResultCache *Cast(TaggedObject *object)
310    {
311        return reinterpret_cast<StringSplitResultCache*>(object);
312    }
313    static JSTaggedValue CreateCacheTable(const JSThread *thread);
314    static JSTaggedValue FindCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache,
315        const JSHandle<EcmaString> &string, const JSHandle<EcmaString> &pattern, bool isOneByte = false);
316    static void SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache,
317        const JSHandle<EcmaString> &string, const JSHandle<EcmaString> &pattern,
318        const JSHandle<TaggedArray> &result);
319
320private:
321    static constexpr int CACHE_SIZE = 256;
322    static constexpr int STRING_INDEX = 0;
323    static constexpr int PATTERN_INDEX = 1;
324    static constexpr int ARRAY_INDEX = 2;
325    static constexpr int ENTRY_SIZE = 3;
326};
327
328class StringToListResultCache : public TaggedArray {
329public:
330    static StringToListResultCache *Cast(TaggedObject *object)
331    {
332        return reinterpret_cast<StringToListResultCache*>(object);
333    }
334    static JSTaggedValue CreateCacheTable(const JSThread *thread);
335    static JSTaggedValue FindCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache,
336        const JSHandle<EcmaString> &string);
337    static void SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache,
338        const JSHandle<EcmaString> &string, const JSHandle<TaggedArray> &result);
339
340    static constexpr int MAX_STRING_LENGTH = 20;
341    static constexpr int CACHE_SIZE = 128;
342    static constexpr int STRING_INDEX = 0;
343    static constexpr int ARRAY_INDEX = 1;
344    static constexpr int ENTRY_SIZE = 2;
345};
346}  // namespace panda::ecmascript::builtins
347#endif  // ECMASCRIPT_BUILTINS_BUILTINS_STRING_H
348