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