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 
122 namespace panda::ecmascript {
123 enum class CompareStringsOption : uint8_t;
124 }
125 
126 namespace panda::ecmascript::builtins {
127 constexpr int32_t ENCODE_MAX_UTF16 = 0X10FFFF;
128 constexpr uint16_t ENCODE_LEAD_LOW = 0xD800;
129 constexpr uint16_t ENCODE_TRAIL_LOW = 0xDC00;
130 constexpr uint32_t ENCODE_FIRST_FACTOR = 0x400;
131 constexpr uint32_t ENCODE_SECOND_FACTOR = 0x10000;
132 constexpr double DOUBLE_INT_MAX = static_cast<double>(INT_MAX);
133 constexpr double DOUBLE_INT_MIN = static_cast<double>(INT_MIN);
134 
135 class BuiltinsString : public base::BuiltinsBase {
136 public:
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
GetStringFunctions()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.
GetStringPrototypeFunctions()256     static Span<const base::BuiltinFunctionEntry> GetStringPrototypeFunctions()
257     {
258         return Span<const base::BuiltinFunctionEntry>(STRING_PROTOTYPE_FUNCTIONS);
259     }
260 
GetNumPrototypeInlinedProperties()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 
272 private:
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);
IsUTF16HighSurrogate(uint16_t ch)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     }
IsUTF16LowSurrogate(uint16_t ch)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 
307 class StringSplitResultCache : public TaggedArray {
308 public:
Cast(TaggedObject *object)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 
320 private:
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 
328 class StringToListResultCache : public TaggedArray {
329 public:
Cast(TaggedObject *object)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