1/*
2 * Copyright (c) 2021-2024 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#include "ecmascript/builtins/builtins_global.h"
17
18#include <random>
19#include <sstream>
20#include <string>
21#include <vector>
22
23#include "ecmascript/interpreter/interpreter.h"
24#include "ecmascript/js_object-inl.h"
25#include "ecmascript/module/js_module_deregister.h"
26#include "ecmascript/module/module_path_helper.h"
27#include "ecmascript/stubs/runtime_stubs.h"
28#include "ecmascript/containers/containers_errors.h"
29#include "ecmascript/jspandafile/js_pandafile_manager.h"
30#include "ecmascript/module/js_module_manager.h"
31
32namespace panda::ecmascript::builtins {
33using NumberHelper = base::NumberHelper;
34using StringHelper = base::StringHelper;
35using GlobalError = containers::ContainerError;
36// bitmap for "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" + "@*+-./"
37constexpr std::uint8_t ESCAPE_BIT_MAP[128] = {
38    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
41    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
42    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
43    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
44    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
45    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
46constexpr std::uint8_t ESCAPE_HEX_TO_CHAR[16] = {
47    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
48};
49constexpr std::uint8_t ESCAPE_CHAR_TO_HEX[128] = {
50    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
54    0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56    0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
58};
59constexpr std::uint8_t ESCAPE_HEX_MASK = 0xf;
60constexpr std::uint8_t ESCAPE_HEX_BIT4 = 4;
61constexpr std::uint8_t ESCAPE_HEX_BIT8 = 8;
62constexpr std::uint8_t ESCAPE_HEX_BIT12 = 12;
63constexpr std::uint8_t ESCAPE_CHAR_OFFSET2 = 2;
64constexpr std::uint8_t ESCAPE_CHAR_OFFSET3 = 3;
65constexpr std::uint8_t ESCAPE_CHAR_OFFSET4 = 4;
66constexpr std::uint8_t ESCAPE_CHAR_OFFSET5 = 5;
67constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0';
68
69// 18.2.1
70JSTaggedValue BuiltinsGlobal::NotSupportEval(EcmaRuntimeCallInfo *msg)
71{
72    JSThread *thread = msg->GetThread();
73    BUILTINS_API_TRACE(thread, Global, NotSupportEval);
74    [[maybe_unused]] EcmaHandleScope handleScope(thread);
75    THROW_TYPE_ERROR_AND_RETURN(thread, "not support eval()", JSTaggedValue::Exception());
76}
77
78// 18.2.2
79JSTaggedValue BuiltinsGlobal::IsFinite(EcmaRuntimeCallInfo *msg)
80{
81    ASSERT(msg);
82    JSThread *thread = msg->GetThread();
83    BUILTINS_API_TRACE(thread, Global, IsFinite);
84    [[maybe_unused]] EcmaHandleScope handleScope(thread);
85    JSHandle<JSTaggedValue> numberInput = GetCallArg(msg, 0);
86    // 1. Let num be ToNumber(number).
87    JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput);
88    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
89    // 3. If num is NaN, +Infinite, or -Infinite, return false.
90    // 4. Otherwise, return true.
91    if (std::isfinite(number.GetNumber())) {
92        return GetTaggedBoolean(true);
93    }
94    return GetTaggedBoolean(false);
95}
96
97// 18.2.3
98JSTaggedValue BuiltinsGlobal::IsNaN(EcmaRuntimeCallInfo *msg)
99{
100    ASSERT(msg);
101    JSThread *thread = msg->GetThread();
102    BUILTINS_API_TRACE(thread, Global, IsNaN);
103    [[maybe_unused]] EcmaHandleScope handleScope(thread);
104    JSHandle<JSTaggedValue> numberInput = GetCallArg(msg, 0);
105    // 1. Let num be ToNumber(number).
106    JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput);
107    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
108
109    // 3. If num is NaN, return true.
110    if (std::isnan(number.GetNumber())) {
111        return GetTaggedBoolean(true);
112    }
113    // 4. Otherwise, return false.
114    return GetTaggedBoolean(false);
115}
116
117bool BuiltinsGlobal::IsUnescapedURI(uint16_t ch)
118{
119    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
120        return true;
121    }
122    return IsInMarkURISet(ch);
123}
124
125bool BuiltinsGlobal::IsInUnescapedURISet(uint16_t ch)
126{
127    if (ch == '#') {
128        return true;
129    }
130    return IsUnescapedURI(ch) || IsReservedURI(ch);
131}
132
133bool BuiltinsGlobal::IsInReservedURISet(uint16_t ch)
134{
135    if (ch == '#') {
136        return true;
137    }
138    return IsReservedURI(ch);
139}
140
141bool BuiltinsGlobal::IsReservedURI(uint16_t ch)
142{
143    std::u16string str(u";/?:@&=+$,");
144    std::u16string::size_type index = str.find(ch);
145    return (index != std::u16string::npos);
146}
147
148bool BuiltinsGlobal::IsInMarkURISet(uint16_t ch)
149{
150    std::u16string str(u"-_.!~*'()");
151    std::u16string::size_type index = str.find(ch);
152    return (index != std::u16string::npos);
153}
154
155bool BuiltinsGlobal::IsHexDigits(uint16_t ch)
156{
157    return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f');
158}
159
160// 18.2.6
161JSTaggedValue BuiltinsGlobal::DecodeURI(EcmaRuntimeCallInfo *msg)
162{
163    ASSERT(msg);
164    JSThread *thread = msg->GetThread();
165    BUILTINS_API_TRACE(thread, Global, DecodeURI);
166    [[maybe_unused]] EcmaHandleScope handleScope(thread);
167    // 1. Let uriString be ToString(encodedURI).
168    // 2. ReturnIfAbrupt(uriString).
169    JSHandle<EcmaString> uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
170    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
171
172    // 3. Let reservedURISet be a String containing one instance of each code unit valid in uriReserved plus "#".
173    // 4. Return Decode(uriString, reservedURISet).
174    return Decode(thread, uriString, IsInReservedURISet);
175}
176
177JSTaggedValue BuiltinsGlobal::EncodeURI(EcmaRuntimeCallInfo *msg)
178{
179    ASSERT(msg);
180    JSThread *thread = msg->GetThread();
181    BUILTINS_API_TRACE(thread, Global, EncodeURI);
182    [[maybe_unused]] EcmaHandleScope handleScope(thread);
183    // 1. Let uriString be ToString(uri).
184    // 2. ReturnIfAbrupt(uriString).
185    JSHandle<EcmaString> uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
186    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
187
188    // 3. Let unescapedURISet be a String containing one instance of
189    //    each code unit valid in uriReserved and uriUnescaped plus "#".
190    // 4. Return Encode(uriString, unescapedURISet).
191    return Encode(thread, uriString, IsInUnescapedURISet);
192}
193
194JSTaggedValue BuiltinsGlobal::DecodeURIComponent(EcmaRuntimeCallInfo *msg)
195{
196    ASSERT(msg);
197    JSThread *thread = msg->GetThread();
198    BUILTINS_API_TRACE(thread, Global, DecodeURIComponent);
199    [[maybe_unused]] EcmaHandleScope handleScope(thread);
200    // 1. Let componentString be ToString(encodedURIComponent).
201    // 2. ReturnIfAbrupt(componentString).
202    JSHandle<EcmaString> componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
203    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
204
205    // 3. Let reservedURIComponentSet be the empty String.
206    // 4. Return Decode(componentString, reservedURIComponentSet).
207    return Decode(thread, componentString, []([[maybe_unused]] uint16_t unused) { return false; });
208}
209
210JSTaggedValue BuiltinsGlobal::EncodeURIComponent(EcmaRuntimeCallInfo *msg)
211{
212    ASSERT(msg);
213    JSThread *thread = msg->GetThread();
214    BUILTINS_API_TRACE(thread, Global, EncodeURIComponent);
215    [[maybe_unused]] EcmaHandleScope handleScope(thread);
216    // 1. Let componentString be ToString(uriComponent).
217    // 2. ReturnIfAbrupt(componentString).
218    JSHandle<EcmaString> componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
219    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
220
221    // 3. Let unescapedURIComponentSet be a String containing one instance of each code unit valid in uriUnescaped.
222    // 4. Return Encode(componentString, unescapedURIComponentSet).
223    return Encode(thread, componentString, IsUnescapedURI);
224}
225
226// Runtime Semantics
227JSTaggedValue BuiltinsGlobal::Encode(JSThread *thread, const JSHandle<EcmaString> &str, judgURIFunc IsInURISet)
228{
229    BUILTINS_API_TRACE(thread, Global, Encode);
230    // 1. Let strLen be the number of code units in string.
231    CString errorMsg;
232    uint32_t strLen = EcmaStringAccessor(str).GetLength();
233    // 2. Let R be the empty String.
234    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
235    std::u16string resStr;
236    JSHandle<EcmaString> string = str;
237    if (EcmaStringAccessor(str).IsTreeString()) {
238        string = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), str));
239    }
240    // 3. Let k be 0.
241    // 4. Repeat
242    uint32_t k = 0;
243    while (true) {
244        // a. If k equals strLen, return R.
245        if (k == strLen) {
246            auto *uint16tData = reinterpret_cast<uint16_t *>(resStr.data());
247            uint32_t resSize = resStr.size();
248            return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue();
249        }
250
251        // b. Let C be the code unit at index k within string.
252        // c. If C is in unescapedSet, then
253        //   i. Let S be a String containing only the code unit C.
254        //   ii. Let R be a new String value computed by concatenating the previous value of R and S.
255        // d. Else C is not in unescapedSet,
256        uint16_t cc = EcmaStringAccessor(string).Get(k);
257        if (IsInURISet(cc)) {
258            std::u16string sStr = StringHelper::Utf16ToU16String(&cc, 1);
259            resStr.append(sStr);
260        } else {
261            // i. If the code unit value of C is not less than 0xDC00 and not greater than 0xDFFF,
262            //    throw a URIError exception.
263            if (cc >= base::utf_helper::DECODE_TRAIL_LOW && cc <= base::utf_helper::DECODE_TRAIL_HIGH) {
264                errorMsg = "DecodeURI: invalid character: " + ConvertToString(string.GetTaggedValue());
265                THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
266            }
267
268            // ii. If the code unit value of C is less than 0xD800 or greater than 0xDBFF, then
269            //    1. Let V be the code unit value of C.
270            // iii. Else,
271            //    1. Increase k by 1.
272            //    2. If k equals strLen, throw a URIError exception.
273            //    3. Let kChar be the code unit value of the code unit at index k within string.
274            //    4. If kChar is less than 0xDC00 or greater than 0xDFFF, throw a URIError exception.
275            //    5. Let V be UTF16Decode(C, kChar).
276            uint32_t vv;
277            if (cc < base::utf_helper::DECODE_LEAD_LOW || cc > base::utf_helper::DECODE_LEAD_HIGH) {
278                vv = cc;
279            } else {
280                k++;
281                if (k == strLen) {
282                    errorMsg = "DecodeURI: invalid character: " + ConvertToString(string.GetTaggedValue());
283                    THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
284                }
285                uint16_t kc = EcmaStringAccessor(string).Get(k);
286                if (kc < base::utf_helper::DECODE_TRAIL_LOW || kc > base::utf_helper::DECODE_TRAIL_HIGH) {
287                    errorMsg = "DecodeURI: invalid character: " + ConvertToString(string.GetTaggedValue());
288                    THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
289                }
290                vv = base::utf_helper::UTF16Decode(cc, kc);
291            }
292
293            // iv. Let Octets be the array of octets resulting by applying the UTF-8 transformation to V,
294            //     and let L be the array size.
295            // v. Let j be 0.
296            // vi. Repeat, while j < L
297            //    1. Let jOctet be the value at index j within Octets.
298            //    2. Let S be a String containing three code units "%XY" where XY are two uppercase hexadecimal
299            //       digits encoding the value of jOctet.
300            //    3. Let R be a new String value computed by concatenating the previous value of R and S.
301            //    4. Increase j by 1.
302            std::string oct = StringHelper::Utf32ToString(vv);
303            std::string hexStr("0123456789ABCDEF");
304
305            uint32_t length = oct.length();
306            std::stringstream tmpStr;
307            for (uint32_t j = 0; j < length; j++) {
308                uint8_t joct = static_cast<uint8_t>(oct.at(j));
309                tmpStr << '%' << hexStr.at((joct >> 4U) & BIT_MASK)  // NOLINT
310                       << hexStr.at(joct & BIT_MASK);                // 4: means shift right by 4 digits
311            }
312            resStr.append(StringHelper::StringToU16string(tmpStr.str()));
313        }
314
315        // e. Increase k by 1.
316        k++;
317    }
318}
319
320uint8_t BuiltinsGlobal::GetValueFromTwoHex(uint16_t front, uint16_t behind)
321{
322    ASSERT(IsHexDigits(front) && IsHexDigits(behind));
323    std::u16string hexString(u"0123456789ABCDEF");
324
325    size_t idxf = StringHelper::FindFromU16ToUpper(hexString, &front);
326    size_t idxb = StringHelper::FindFromU16ToUpper(hexString, &behind);
327    uint8_t res = ((idxf << 4U) | idxb) & BIT_MASK_FF;  // NOLINT 4: means shift left by 4 digits
328    return res;
329}
330
331uint16_t BuiltinsGlobal::GetValueFromHexString(const JSHandle<EcmaString> &string)
332{
333    uint32_t size = EcmaStringAccessor(string).GetLength();
334    ASSERT(size > 0 && size <= 4); // NOLINT 4: means 4 hex digits
335    std::u16string hexString(u"0123456789ABCDEF");
336
337    uint16_t ret = 0;
338    for (uint32_t i = 0; i < size; ++i) {
339        uint16_t ch = EcmaStringAccessor(string).Get(i);
340        size_t idx = StringHelper::FindFromU16ToUpper(hexString, &ch);
341        ret = ((ret << 4U) | idx) & BIT_MASK_4F; // NOLINT 4: means shift left by 4
342    }
343    return ret;
344}
345
346// 22.1.3.17.2 StringPad ( S, maxLength, fillString, placement )
347EcmaString *BuiltinsGlobal::StringPad(JSThread *thread, const JSHandle<EcmaString> &source,
348                                      uint32_t maxLength, const JSHandle<EcmaString> &fillString,
349                                      Placement placement)
350{
351    // 1. Let stringLength be the length of S.
352    uint32_t stringLength = EcmaStringAccessor(source).GetLength();
353    // 2. If maxLength ≤ stringLength, return S.
354    if (maxLength <= stringLength) {
355        return *source;
356    }
357    // 3. If fillString is the empty String, return S.
358    uint32_t targetStrLen = EcmaStringAccessor(fillString).GetLength();
359    if (targetStrLen == 0) {
360        return *source;
361    }
362    // 4. Let fillLen be maxLength - stringLength.
363    uint32_t fillLen = maxLength - stringLength;
364    EcmaVM *vm = thread->GetEcmaVM();
365    //5. Let truncatedStringFiller be the String value consisting of repeated concatenations
366    // of fillString truncated to length fillLen.
367    uint32_t repeatTimes = std::ceil(fillLen / targetStrLen);
368    EcmaString *p = nullptr;
369    JSHandle<EcmaString> stringFiller = vm->GetFactory()->NewFromStdString(std::string("\0"));
370    for (uint32_t k = 0; k < repeatTimes; ++k) {
371        p = EcmaStringAccessor::Concat(vm, stringFiller, fillString);
372        stringFiller = JSHandle<EcmaString>(thread, p);
373    }
374    JSHandle<EcmaString> truncatedStringFiller(thread,
375        EcmaStringAccessor::FastSubString(vm, stringFiller, 0, fillLen));
376    // 6. If placement is start, return the string-concatenation of truncatedStringFiller and S.
377    // 7. Else, return the string-concatenation of S and truncatedStringFiller.
378    if (placement == Placement::START) {
379        return EcmaStringAccessor::Concat(vm, truncatedStringFiller, source);
380    } else {
381        return EcmaStringAccessor::Concat(vm, source, truncatedStringFiller);
382    }
383}
384
385// Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail )
386uint16_t BuiltinsGlobal::UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)
387{
388    // 1. Assert: lead is a leading surrogate and trail is a trailing surrogate.
389    ASSERT(IsUTF16HighSurrogate(lead) && IsUTF16LowSurrogate(trail));
390    // 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000.
391    uint16_t cp = ((lead - 0xD800) << 10UL) + (trail - 0xDC00) + 0x10000;
392    // 3. Return the code point cp.
393    return cp;
394}
395
396// 11.1.5 Static Semantics: StringToCodePoints ( string )
397EcmaString *BuiltinsGlobal::StringToCodePoints(JSThread *thread, const JSHandle<EcmaString> &string)
398{
399    // 1. Let codePoints be a new empty List.
400    std::u16string codePoints;
401    // 2. Let size be the length of string.
402    uint32_t size = EcmaStringAccessor(string).GetLength();
403    // 3. Let position be 0.
404    uint32_t position = 0;
405    // 4. Repeat, while position < size,
406    //    a. Let cp be CodePointAt(string, position).
407    //    b. Append cp.[[CodePoint]] to codePoints.
408    //    c. Set position to position + cp.[[CodeUnitCount]].
409    while (position < size) {
410        // i.Let first be the code unit at index position within string.
411        uint16_t first = EcmaStringAccessor(string).Get(position);
412        uint16_t cp = first - CHAR16_LETTER_NULL;
413        uint8_t codeUnitCount = 0;
414        bool isUnpairedSurrogate = false;
415        // ii. If first is neither a leading surrogate nor a trailing surrogate, then
416        //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
417        if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
418            codeUnitCount = 1; // 1 means: code unit count
419            isUnpairedSurrogate = false;
420        } else if (IsUTF16HighSurrogate(first) || position + 1 == size) {
421            // iii. If first is a trailing surrogate or position + 1 = size, then
422            //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
423            codeUnitCount = 1;
424            isUnpairedSurrogate = true;
425        } else {
426            // iv. Let second be the code unit at index position + 1 within string.
427            uint16_t second = EcmaStringAccessor(string).Get(position + 1);
428            // v. If second is not a trailing surrogate, then
429            //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
430            if (!IsUTF16LowSurrogate(second)) {
431                codeUnitCount = 1; // 1 means: code unit count
432                isUnpairedSurrogate = true;
433            } else {
434            // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
435            // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
436                cp = UTF16SurrogatePairToCodePoint(first, second);
437                codeUnitCount = 2; // 2 means: code unit count
438                isUnpairedSurrogate = false;
439            }
440        }
441        codePoints.push_back(cp);
442        position = position + codeUnitCount;
443    }
444    // 5. Return codePoints.
445    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
446    uint16_t *ptr = reinterpret_cast<uint16_t *>(codePoints.data());
447    JSHandle<EcmaString> codePointsString = factory->NewFromUtf16Literal(ptr, codePoints.size());
448    return *codePointsString;
449}
450
451// Runtime Semantics
452JSTaggedValue BuiltinsGlobal::Decode(JSThread *thread, const JSHandle<EcmaString> &str, judgURIFunc IsInURISet)
453{
454    BUILTINS_API_TRACE(thread, Global, Decode);
455    // 1. Let strLen be the number of code units in string.
456    int32_t strLen = static_cast<int32_t>(EcmaStringAccessor(str).GetLength());
457    // 2. Let R be the empty String.
458    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
459    std::u16string resStr;
460    JSHandle<EcmaString> string = str;
461    if (EcmaStringAccessor(str).IsTreeString()) {
462        string = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), str));
463    }
464
465    // 3. Let k be 0.
466    // 4. Repeat
467    int32_t k = 0;
468    while (true) {
469        if (k == strLen) {
470            // a. If k equals strLen, return R.
471            auto *uint16tData = reinterpret_cast<uint16_t *>(resStr.data());
472            uint32_t resSize = resStr.size();
473            return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue();
474        }
475
476        // b. Let C be the code unit at index k within string.
477        // c. If C is not "%", then
478        //    i. Let S be the String containing only the code unit C.
479        // d. Else C is "%",
480        //   i. Let start be k.
481        //   iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2).
482        //   v. Increase k by 2.
483        //   vi. If the most significant bit in B is 0, then
484        //      1. Let C be the code unit with code unit value B.
485        //      2. If C is not in reservedSet, then
486        //         a. Let S be the String containing only the code unit C.
487        //      3. Else C is in reservedSet,
488        //         a. Let S be the substring of string from index start to index k inclusive.
489        uint16_t cc = EcmaStringAccessor(string).Get(k);
490        std::u16string sStr;
491        if (cc != '%') {
492            if (cc == 0 && strLen == 1) {
493                JSHandle<EcmaString> tmpEcmaString = factory->NewFromUtf16Literal(&cc, 1);
494                return tmpEcmaString.GetTaggedValue();
495            }
496            sStr = StringHelper::Utf16ToU16String(&cc, 1);
497        } else {
498            DecodePercentEncoding(thread, string, k, IsInURISet, strLen, sStr);
499            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
500        }
501        resStr.append(sStr);
502        k++;
503    }
504}
505
506void BuiltinsGlobal::HandleSingleByteCharacter(JSThread *thread, uint8_t &bb,
507                                               const JSHandle<EcmaString> &str,
508                                               uint32_t &start, int32_t &k,
509                                               std::u16string &sStr, judgURIFunc IsInURISet)
510{
511    if (!IsInURISet(bb)) {
512        sStr = StringHelper::Utf8ToU16String(&bb, 1);
513    } else {
514        auto substr = EcmaStringAccessor::FastSubString(
515            thread->GetEcmaVM(), str, start, k - start + 1U);
516        sStr = StringHelper::StringToU16string(
517            EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION));
518    }
519}
520
521
522JSTaggedValue BuiltinsGlobal::DecodePercentEncoding(JSThread *thread, const JSHandle<EcmaString> &str, int32_t &k,
523                                                    judgURIFunc IsInURISet, int32_t strLen, std::u16string &sStr)
524{
525    [[maybe_unused]] uint32_t start = static_cast<uint32_t>(k);
526    CString errorMsg;
527    // ii. If k + 2 is greater than or equal to strLen, throw a URIError exception.
528    // iii. If the code units at index (k+1) and (k + 2) within string do not represent hexadecimal digits,
529    //      throw a URIError exception.
530    if ((k + 2) >= strLen) {  // 2: means plus 2
531        errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
532        THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
533    }
534    uint16_t frontChar = EcmaStringAccessor(str).Get(k + 1);
535    uint16_t behindChar = EcmaStringAccessor(str).Get(k + 2);  // 2: means plus 2
536    if (!(IsHexDigits(frontChar) && IsHexDigits(behindChar))) {
537        errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
538        THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
539    }
540    uint8_t bb = GetValueFromTwoHex(frontChar, behindChar);
541    k += 2;  // 2: means plus 2
542    if ((bb & BIT_MASK_ONE) == 0) {
543        HandleSingleByteCharacter(thread, bb, str, start, k, sStr, IsInURISet);
544    } else {
545        // vii. Else the most significant bit in B is 1,
546        //   1. Let n be the smallest nonnegative integer such that (B << n) & 0x80 is equal to 0.
547        //   3. Let Octets be an array of 8-bit integers of size n.
548        //   4. Put B into Octets at index 0.
549        //   6. Let j be 1.
550        //   7. Repeat, while j < n
551        //     a. Increase k by 1.
552        //     d. Let B be the 8-bit value represented by the two hexadecimal digits at
553        //        index (k + 1) and (k + 2).
554        //     f. Increase k by 2.
555        //     g. Put B into Octets at index j.
556        //     h. Increase j by 1.
557        //   9. If V < 0x10000, then
558        //     a. Let C be the code unit V.
559        //     b. If C is not in reservedSet, then
560        //        i. Let S be the String containing only the code unit C.
561        //     c. Else C is in reservedSet,
562        //        i. Let S be the substring of string from index start to index k inclusive.
563        //   10. Else V ≥ 0x10000,
564        //     a. Let L be (((V – 0x10000) & 0x3FF) + 0xDC00).
565        //     b. Let H be ((((V – 0x10000) >> 10) & 0x3FF) + 0xD800).
566        //     c. Let S be the String containing the two code units H and L.
567        int32_t n = 0;
568        while ((((static_cast<uint32_t>(bb) << static_cast<uint32_t>(n)) & BIT_MASK_ONE) != 0)) {
569            n++;
570            if (n > 4) { // 4 : 4 means less than 4
571                break;
572            }
573        }
574        // 2. If n equals 1 or n is greater than 4, throw a URIError exception.
575        if ((n == 1) || (n > 4)) {
576            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
577            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
578        }
579
580        std::vector<uint8_t> oct = {bb};
581
582        // 5. If k + (3 × (n – 1)) is greater than or equal to strLen, throw a URIError exception.
583        if (k + (3 * (n - 1)) >= strLen) {  // 3: means multiply by 3
584            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
585            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
586        }
587        DecodePercentEncoding(thread, n, k, str, bb, oct);
588        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
589        UTF16EncodeCodePoint(thread, IsInURISet, oct, str, start, k, sStr);
590        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
591    }
592    return JSTaggedValue::True();
593}
594
595JSTaggedValue BuiltinsGlobal::DecodePercentEncoding(JSThread *thread, int32_t &n,
596                                                    int32_t &k, const JSHandle<EcmaString> &str,
597                                                    uint8_t &bb, std::vector<uint8_t> &oct)
598{
599    CString errorMsg;
600    int32_t j = 1;
601    while (j < n) {
602        k++;
603        uint16_t codeUnit = EcmaStringAccessor(str).Get(k);
604        // b. If the code unit at index k within string is not "%", throw a URIError exception.
605        // c. If the code units at index (k +1) and (k + 2) within string do not represent hexadecimal
606        //    digits, throw a URIError exception.
607        if (!(codeUnit == '%')) {
608            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
609            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
610        }
611        if (!(IsHexDigits(EcmaStringAccessor(str).Get(k + 1)) &&
612                IsHexDigits(EcmaStringAccessor(str).Get(k + 2)))) {  // 2: means plus 2
613            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
614            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
615        }
616        uint16_t frontChart = EcmaStringAccessor(str).Get(k + 1);
617        uint16_t behindChart = EcmaStringAccessor(str).Get(k + 2);  // 2: means plus 2
618        bb = GetValueFromTwoHex(frontChart, behindChart);
619        // e. If the two most significant bits in B are not 10, throw a URIError exception.
620        if (!((bb & BIT_MASK_TWO) == BIT_MASK_ONE)) {
621            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
622            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
623        }
624        k += 2;  // 2: means plus 2
625        oct.push_back(bb);
626        j++;
627    }
628    return JSTaggedValue::True();
629}
630
631JSTaggedValue BuiltinsGlobal::UTF16EncodeCodePoint(JSThread *thread, judgURIFunc IsInURISet,
632                                                   const std::vector<uint8_t> &oct, const JSHandle<EcmaString> &str,
633                                                   uint32_t &start, int32_t &k, std::u16string &sStr)
634{
635    if (!base::utf_helper::IsValidUTF8(oct)) {
636        CString errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
637        THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
638    }
639    uint32_t vv = StringHelper::Utf8ToU32String(oct);
640    if (vv < base::utf_helper::DECODE_SECOND_FACTOR) {
641        if (!IsInURISet(vv)) {
642            sStr = StringHelper::Utf16ToU16String(reinterpret_cast<uint16_t *>(&vv), 1);
643        } else {
644            auto substr = EcmaStringAccessor::FastSubString(
645                thread->GetEcmaVM(), str, start, static_cast<uint32_t>(k) - start + 1U);
646            sStr = StringHelper::StringToU16string(
647                EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION));
648        }
649    } else {
650        uint16_t lv = (((vv - base::utf_helper::DECODE_SECOND_FACTOR) & BIT16_MASK) +
651            base::utf_helper::DECODE_TRAIL_LOW);
652        uint16_t hv = ((((vv - base::utf_helper::DECODE_SECOND_FACTOR) >> 10U) & BIT16_MASK) +  // NOLINT
653            base::utf_helper::DECODE_LEAD_LOW);  // 10: means shift left by 10 digits
654        sStr = StringHelper::Append(StringHelper::Utf16ToU16String(&hv, 1),
655                                    StringHelper::Utf16ToU16String(&lv, 1));
656    }
657    return JSTaggedValue::True();
658}
659
660void BuiltinsGlobal::PrintString([[maybe_unused]] JSThread *thread, EcmaString *string)
661{
662    if (string == nullptr) {
663        return;
664    }
665    BUILTINS_API_TRACE(thread, Global, PrintString);
666    CString buffer = ConvertToString(string);
667    std::cout << buffer;
668}
669
670JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg)
671{
672    if (msg == nullptr) {
673        return JSTaggedValue::Undefined();
674    }
675    JSThread *thread = msg->GetThread();
676    [[maybe_unused]] EcmaHandleScope handleScope(thread);
677    BUILTINS_API_TRACE(thread, Global, PrintEntryPoint);
678
679    uint32_t numArgs = msg->GetArgsNumber();
680    for (uint32_t i = 0; i < numArgs; i++) {
681        JSHandle<EcmaString> stringContent = JSTaggedValue::ToString(thread, GetCallArg(msg, i));
682        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
683        PrintString(thread, *stringContent);
684
685        if (i != numArgs - 1) {
686            std::cout << " ";
687        }
688    }
689    std::cout << std::endl;
690    return JSTaggedValue::Undefined();
691}
692
693JSTaggedValue BuiltinsGlobal::MarkModuleCollectable(EcmaRuntimeCallInfo *msg)
694{
695    ASSERT(msg);
696    JSThread *thread = msg->GetThread();
697    [[maybe_unused]] EcmaHandleScope handleScope(thread);
698
699    uint32_t numArgs = msg->GetArgsNumber();
700    if (numArgs != 1) {
701        LOG_FULL(ERROR) << "The number of parameters received by markModuleCollectable is incorrect.";
702        return JSTaggedValue::False();
703    }
704    JSHandle<JSTaggedValue> module = GetCallArg(msg, 0);
705    if (!module->IsModuleNamespace()) {
706        return JSTaggedValue::False();
707    }
708
709    ModuleDeregister::ProcessModuleReference(thread, module);
710    return JSTaggedValue::True();
711}
712
713JSTaggedValue BuiltinsGlobal::LoadNativeModule(EcmaRuntimeCallInfo *msg)
714{
715    ASSERT(msg);
716    JSThread *thread = msg->GetThread();
717    [[maybe_unused]] EcmaHandleScope handleScope(thread);
718    CString errorMsg;
719    uint32_t numArgs = msg->GetArgsNumber();
720    if (numArgs != 1) {
721        errorMsg = "The number of parameters received by loadNativeModule is incorrect.";
722        auto error = GlobalError::ParamError(thread, errorMsg.c_str());
723        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
724    }
725    JSHandle<JSTaggedValue> input = GetCallArg(msg, 0);
726    if (!input->IsString()) {
727        errorMsg = "The number of parameters received by loadNativeModule is incorrect.";
728        auto error = GlobalError::ParamError(thread, errorMsg.c_str());
729        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
730    }
731
732    EcmaVM *vm  = thread->GetEcmaVM();
733    auto [moduleName, fileName] = vm->GetCurrentModuleInfo(false);
734    std::shared_ptr<JSPandaFile> curJsPandaFile;
735    CString requestPath = ModulePathHelper::Utf8ConvertToString(input.GetTaggedValue());
736    CString abcFilePath = fileName.c_str();
737    if (moduleName.size() != 0) {
738        curJsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, abcFilePath, requestPath);
739        if (curJsPandaFile == nullptr) {
740            errorMsg = "Load native module failed, filename '" + abcFilePath +
741                ", module name '" + requestPath;
742            auto error = GlobalError::ReferenceError(thread, errorMsg.c_str());
743            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
744        }
745        if (vm->IsNormalizedOhmUrlPack()) {
746            ModulePathHelper::TranslateExpressionToNormalized(thread, curJsPandaFile.get(), abcFilePath, "",
747                requestPath);
748        } else if (ModulePathHelper::NeedTranstale(requestPath)) {
749            ModulePathHelper::TranstaleExpressionInput(curJsPandaFile.get(), requestPath);
750        }
751
752        size_t pos = requestPath.find(PathHelper::COLON_TAG);
753        if (pos == CString::npos) {
754            errorMsg = "The module name '"+ requestPath +
755                "' of parameters received by loadNativeModule is incorrect.";
756            auto error = GlobalError::ParamError(thread, errorMsg.c_str());
757            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
758        }
759    }
760
761    ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
762    auto exportObject = moduleManager->ExecuteNativeModuleMayThrowError(thread, requestPath);
763    return exportObject.GetTaggedValue();
764}
765
766JSTaggedValue BuiltinsGlobal::CallJsBoundFunction(EcmaRuntimeCallInfo *msg)
767{
768    JSThread *thread = msg->GetThread();
769    BUILTINS_API_TRACE(thread, Global, CallJsBoundFunction);
770    [[maybe_unused]] EcmaHandleScope handleScope(thread);
771    // msg contains jsfunc, this, arg1,...
772
773    JSHandle<JSBoundFunction> boundFunc(GetConstructor(msg));
774    JSHandle<JSTaggedValue> thisObj(thread, boundFunc->GetBoundThis());
775    msg->SetThis(thisObj.GetTaggedValue());
776    return RuntimeStubs::CallBoundFunction(msg);
777}
778
779JSTaggedValue BuiltinsGlobal::CallJsProxy(EcmaRuntimeCallInfo *msg)
780{
781    JSThread *thread = msg->GetThread();
782    BUILTINS_API_TRACE(thread, Global, CallJsProxy);
783    [[maybe_unused]] EcmaHandleScope handleScope(thread);
784    // msg contains js_proxy, this, arg1,...
785    JSHandle<JSProxy> proxy(GetConstructor(msg));
786    if (!proxy->IsCallable()) {
787        THROW_TYPE_ERROR_AND_RETURN(thread, "Proxy target is not callable", JSTaggedValue::Undefined());
788    }
789
790    // Calling proxy directly should transfer 'undefined' as this
791    return JSProxy::CallInternal(msg);
792}
793
794#if ECMASCRIPT_ENABLE_RUNTIME_STAT
795JSTaggedValue BuiltinsGlobal::StartRuntimeStat(EcmaRuntimeCallInfo *msg)
796{
797    JSThread *thread = msg->GetThread();
798    BUILTINS_API_TRACE(thread, Global, StartRuntimeStat);
799    [[maybe_unused]] EcmaHandleScope handleScope(thread);
800    // start vm runtime stat statistic
801    thread->GetCurrentEcmaContext()->SetRuntimeStatEnable(true);
802    return JSTaggedValue::Undefined();
803}
804
805JSTaggedValue BuiltinsGlobal::StopRuntimeStat(EcmaRuntimeCallInfo *msg)
806{
807    JSThread *thread = msg->GetThread();
808    BUILTINS_API_TRACE(thread, Global, StopRuntimeStat);
809    [[maybe_unused]] EcmaHandleScope handleScope(thread);
810    // start vm runtime stat statistic
811    thread->GetCurrentEcmaContext()->SetRuntimeStatEnable(false);
812    return JSTaggedValue::Undefined();
813}
814#endif
815
816#if ECMASCRIPT_ENABLE_OPT_CODE_PROFILER
817JSTaggedValue BuiltinsGlobal::PrintOptStat(EcmaRuntimeCallInfo *msg)
818{
819    JSThread *thread = msg->GetThread();
820    BUILTINS_API_TRACE(thread, Global, PrintOptStat);
821    [[maybe_unused]] EcmaHandleScope handleScope(thread);
822    // start vm runtime stat statistic
823    thread->GetCurrentEcmaContext()->PrintOptStat();
824    return JSTaggedValue::Undefined();
825}
826#endif
827
828#if ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER
829JSTaggedValue BuiltinsGlobal::PrintFunctionCallStat(EcmaRuntimeCallInfo *msg)
830{
831    JSThread *thread = msg->GetThread();
832    BUILTINS_API_TRACE(thread, Global, PrintFunctionCallStat);
833    [[maybe_unused]] EcmaHandleScope handleScope(thread);
834    // start vm runtime stat statistic
835    thread->GetEcmaVM()->DumpCallTimeInfo();
836    return JSTaggedValue::Undefined();
837}
838#endif
839
840// B.2.1.1 escape ( string )
841JSTaggedValue BuiltinsGlobal::Escape(EcmaRuntimeCallInfo *msg)
842{
843    ASSERT(msg);
844    JSThread *thread = msg->GetThread();
845    BUILTINS_API_TRACE(thread, Global, Escape);
846    [[maybe_unused]] EcmaHandleScope handleScope(thread);
847    // 1. Set string to ? ToString(string).
848    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
849    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
850    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
851    // 2. Let len be the length of string.
852    uint32_t len = EcmaStringAccessor(string).GetLength();
853    // 3. Let R be the empty String.
854    std::u16string r;
855    // 4. Let unescapedSet be the string-concatenation of the ASCII word characters and "@*+-./".
856    // 5. Let k be 0.
857    uint32_t k = 0;
858    // 6. Repeat, while k < len,
859    //   a. Let C be the code unit at index k within string.
860    //   b. If unescapedSet contains C, then
861    //        i. Let S be C.
862    //   c. Else,
863    //        i. Let n be the numeric value of C.
864    //        ii. If n < 256, then
865    //          1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number.
866    //          2. Let S be the string-concatenation of "%" and StringPad(hex, 2, "0", start).
867    //        iii. Else,
868    //          1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number.
869    //          2. Let S be the string-concatenation of "%u" and StringPad(hex, 4, "0", start).
870    //    d. Set R to the string-concatenation of R and S.
871    //    e. Set k to k + 1.
872    while (k < len) {
873        uint16_t c = EcmaStringAccessor(string).Get(k);
874        if (c < std::numeric_limits<int8_t>::max() && ESCAPE_BIT_MAP[c] == 1) {
875            r.push_back(c);
876        } else {
877            r.push_back('%');
878            if (c <= std::numeric_limits<uint8_t>::max()) {
879                r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT4) & ESCAPE_HEX_MASK]);
880                r.push_back(ESCAPE_HEX_TO_CHAR[c & ESCAPE_HEX_MASK]);
881            } else {
882                r.push_back('u');
883                r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT12) & ESCAPE_HEX_MASK]);
884                r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT8) & ESCAPE_HEX_MASK]);
885                r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT4) & ESCAPE_HEX_MASK]);
886                r.push_back(ESCAPE_HEX_TO_CHAR[c & ESCAPE_HEX_MASK]);
887            }
888        }
889        ++k;
890    }
891    // 7. Return R.
892    auto *returnData = reinterpret_cast<uint16_t *>(r.data());
893    uint32_t retSize = r.size();
894    return factory->NewFromUtf16Literal(returnData, retSize).GetTaggedValue();
895}
896
897// B.2.1.2 unescape ( string )
898JSTaggedValue BuiltinsGlobal::Unescape(EcmaRuntimeCallInfo *msg)
899{
900    ASSERT(msg);
901    JSThread *thread = msg->GetThread();
902    BUILTINS_API_TRACE(thread, Global, Unescape);
903    [[maybe_unused]] EcmaHandleScope handleScope(thread);
904    // 1. Set string to ? ToString(string).
905    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
906    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
907    // 2. Let len be the length of string.
908    uint32_t len = EcmaStringAccessor(string).GetLength();
909    // 3. Let R be the empty String.
910    EcmaVM *vm = thread->GetEcmaVM();
911    ObjectFactory *factory = vm->GetFactory();
912    std::u16string r;
913    // 4. Let k be 0.
914    uint32_t k = 0;
915    // 5. Repeat, while k < len,
916    //   a. Let C be the code unit at index k within string.
917    //   b. If C is the code unit 0x0025 (PERCENT SIGN), then
918    //     i. Let hexDigits be the empty String.
919    //     ii. Let optionalAdvance be 0.
920    //     iii. If k + 5 < len and the code unit at index k + 1 within string is the code unit
921    //          0x0075 (LATIN SMALL LETTER U), then
922    //       1. Set hexDigits to the substring of string from k + 2 to k + 6.
923    //       2. Set optionalAdvance to 5.
924    //     iv. Else if k + 3 ≤ len, then
925    //       1. Set hexDigits to the substring of string from k + 1 to k + 3.
926    //       2. Set optionalAdvance to 2.
927    //     v. Let parseResult be ParseText(StringToCodePoints(hexDigits), HexDigits[~Sep]).
928    //     vi. If parseResult is a Parse Node, then
929    //       1. Let n be the MV of parseResult.
930    //       2. Set C to the code unit whose numeric value is n.
931    //       3. Set k to k + optionalAdvance.
932    //   c. Set R to the string-concatenation of R and C.
933    //   d. Set k to k + 1.
934    while (k < len) {
935        uint16_t c = EcmaStringAccessor(string).Get(k);
936        if (c == '%') {
937            uint16_t c1 = EcmaStringAccessor(string).Get(k + 1);
938            if (k + ESCAPE_CHAR_OFFSET5 < len && c1 == 'u') {
939                uint16_t c2 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET2);
940                uint16_t c3 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET3);
941                uint16_t c4 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET4);
942                uint16_t c5 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET5);
943                bool c2IsHexDigits = IsHexDigits(c2);
944                bool c3IsHexDigits = IsHexDigits(c3);
945                bool c4IsHexDigits = IsHexDigits(c4);
946                bool c5IsHexDigits = IsHexDigits(c5);
947                bool isHexDigits = c2IsHexDigits && c3IsHexDigits && c4IsHexDigits && c5IsHexDigits;
948                if (isHexDigits) {
949                    c = ESCAPE_CHAR_TO_HEX[c2];
950                    c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c3];
951                    c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c4];
952                    c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c5];
953                    k = k + ESCAPE_CHAR_OFFSET5;
954                }
955            } else if (k + ESCAPE_CHAR_OFFSET3 <= len) {
956                uint16_t c2 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET2);
957                bool c1IsHexDigits = IsHexDigits(c1);
958                bool c2IsHexDigits = IsHexDigits(c2);
959                bool isHexDigits = c1IsHexDigits && c2IsHexDigits;
960                if (isHexDigits) {
961                    c = ESCAPE_CHAR_TO_HEX[c1];
962                    c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c2];
963                    k = k + ESCAPE_CHAR_OFFSET2;
964                }
965            }
966        }
967        r.push_back(c);
968        ++k;
969    }
970    // 7. Return R.
971    auto *returnData = reinterpret_cast<uint16_t *>(r.data());
972    uint32_t retSize = r.size();
973    return factory->NewFromUtf16Literal(returnData, retSize).GetTaggedValue();
974}
975
976JSTaggedValue BuiltinsGlobal::GetCurrentModuleName(EcmaRuntimeCallInfo *msg)
977{
978    ASSERT(msg);
979    JSThread *thread = msg->GetThread();
980    BUILTINS_API_TRACE(thread, Global, GetCurrentModuleName);
981    [[maybe_unused]] EcmaHandleScope handleScope(thread);
982    std::pair<CString, CString> moduleInfo = EcmaInterpreter::GetCurrentEntryPoint(thread);
983    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
984    CString fileName = moduleInfo.second;
985    CString moduleName = ModulePathHelper::GetModuleNameWithBaseFile(fileName);
986    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
987    JSHandle<EcmaString> result = factory->NewFromUtf8(moduleName.c_str());
988    return result.GetTaggedValue();
989}
990
991JSTaggedValue BuiltinsGlobal::GetCurrentBundleName(EcmaRuntimeCallInfo *msg)
992{
993    ASSERT(msg);
994    JSThread *thread = msg->GetThread();
995    BUILTINS_API_TRACE(thread, Global, GetCurrentBundleName);
996    [[maybe_unused]] EcmaHandleScope handleScope(thread);
997    std::pair<CString, CString> moduleInfo = EcmaInterpreter::GetCurrentEntryPoint(thread);
998    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
999    EcmaVM *vm = thread->GetEcmaVM();
1000    CString recordName = moduleInfo.first;
1001    CString bundleName = ModulePathHelper::GetBundleNameWithRecordName(vm, recordName);
1002    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1003    JSHandle<EcmaString> result = factory->NewFromUtf8(bundleName.c_str());
1004    return result.GetTaggedValue();
1005}
1006
1007JSTaggedValue BuiltinsGlobal::IsSendable(EcmaRuntimeCallInfo *msg)
1008{
1009    ASSERT(msg);
1010    JSThread *thread = msg->GetThread();
1011    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1012
1013    uint32_t numArgs = msg->GetArgsNumber();
1014    if (numArgs != 1) {
1015        LOG_FULL(ERROR) << "The number of parameters received by IsSendable is incorrect.";
1016        return JSTaggedValue::False();
1017    }
1018    JSHandle<JSTaggedValue> obj = GetCallArg(msg, 0);
1019    if ((obj->IsECMAObject() && obj->IsJSShared()) ||
1020        obj->IsString() || obj->IsNumber() || obj->IsBoolean() ||
1021        obj->IsUndefined() || obj->IsNull() || obj->IsBigInt()) {
1022        return JSTaggedValue::True();
1023    }
1024
1025    return JSTaggedValue::False();
1026}
1027}  // namespace panda::ecmascript::builtins
1028