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 
32 namespace panda::ecmascript::builtins {
33 using NumberHelper = base::NumberHelper;
34 using StringHelper = base::StringHelper;
35 using GlobalError = containers::ContainerError;
36 // bitmap for "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" + "@*+-./"
37 constexpr 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};
46 constexpr 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 };
49 constexpr 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 };
59 constexpr std::uint8_t ESCAPE_HEX_MASK = 0xf;
60 constexpr std::uint8_t ESCAPE_HEX_BIT4 = 4;
61 constexpr std::uint8_t ESCAPE_HEX_BIT8 = 8;
62 constexpr std::uint8_t ESCAPE_HEX_BIT12 = 12;
63 constexpr std::uint8_t ESCAPE_CHAR_OFFSET2 = 2;
64 constexpr std::uint8_t ESCAPE_CHAR_OFFSET3 = 3;
65 constexpr std::uint8_t ESCAPE_CHAR_OFFSET4 = 4;
66 constexpr std::uint8_t ESCAPE_CHAR_OFFSET5 = 5;
67 constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0';
68 
69 // 18.2.1
NotSupportEval(EcmaRuntimeCallInfo *msg)70 JSTaggedValue 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
IsFinite(EcmaRuntimeCallInfo *msg)79 JSTaggedValue 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
IsNaN(EcmaRuntimeCallInfo *msg)98 JSTaggedValue 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 
IsUnescapedURI(uint16_t ch)117 bool 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 
IsInUnescapedURISet(uint16_t ch)125 bool BuiltinsGlobal::IsInUnescapedURISet(uint16_t ch)
126 {
127     if (ch == '#') {
128         return true;
129     }
130     return IsUnescapedURI(ch) || IsReservedURI(ch);
131 }
132 
IsInReservedURISet(uint16_t ch)133 bool BuiltinsGlobal::IsInReservedURISet(uint16_t ch)
134 {
135     if (ch == '#') {
136         return true;
137     }
138     return IsReservedURI(ch);
139 }
140 
IsReservedURI(uint16_t ch)141 bool 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 
IsInMarkURISet(uint16_t ch)148 bool 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 
IsHexDigits(uint16_t ch)155 bool 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
DecodeURI(EcmaRuntimeCallInfo *msg)161 JSTaggedValue 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 
EncodeURI(EcmaRuntimeCallInfo *msg)177 JSTaggedValue 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 
DecodeURIComponent(EcmaRuntimeCallInfo *msg)194 JSTaggedValue 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 
EncodeURIComponent(EcmaRuntimeCallInfo *msg)210 JSTaggedValue 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
Encode(JSThread *thread, const JSHandle<EcmaString> &str, judgURIFunc IsInURISet)227 JSTaggedValue 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 
GetValueFromTwoHex(uint16_t front, uint16_t behind)320 uint8_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 
GetValueFromHexString(const JSHandle<EcmaString> &string)331 uint16_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 )
StringPad(JSThread *thread, const JSHandle<EcmaString> &source, uint32_t maxLength, const JSHandle<EcmaString> &fillString, Placement placement)347 EcmaString *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 )
UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)386 uint16_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 )
StringToCodePoints(JSThread *thread, const JSHandle<EcmaString> &string)397 EcmaString *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
Decode(JSThread *thread, const JSHandle<EcmaString> &str, judgURIFunc IsInURISet)452 JSTaggedValue 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 
HandleSingleByteCharacter(JSThread *thread, uint8_t &bb, const JSHandle<EcmaString> &str, uint32_t &start, int32_t &k, std::u16string &sStr, judgURIFunc IsInURISet)506 void 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 
DecodePercentEncoding(JSThread *thread, const JSHandle<EcmaString> &str, int32_t &k, judgURIFunc IsInURISet, int32_t strLen, std::u16string &sStr)522 JSTaggedValue 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 
DecodePercentEncoding(JSThread *thread, int32_t &n, int32_t &k, const JSHandle<EcmaString> &str, uint8_t &bb, std::vector<uint8_t> &oct)595 JSTaggedValue 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 
UTF16EncodeCodePoint(JSThread *thread, judgURIFunc IsInURISet, const std::vector<uint8_t> &oct, const JSHandle<EcmaString> &str, uint32_t &start, int32_t &k, std::u16string &sStr)631 JSTaggedValue 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 
PrintString([[maybe_unused]] JSThread *thread, EcmaString *string)660 void 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 
PrintEntrypoint(EcmaRuntimeCallInfo *msg)670 JSTaggedValue 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 
MarkModuleCollectable(EcmaRuntimeCallInfo *msg)693 JSTaggedValue 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 
LoadNativeModule(EcmaRuntimeCallInfo *msg)713 JSTaggedValue 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 
CallJsBoundFunction(EcmaRuntimeCallInfo *msg)766 JSTaggedValue 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 
CallJsProxy(EcmaRuntimeCallInfo *msg)779 JSTaggedValue 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
StartRuntimeStat(EcmaRuntimeCallInfo *msg)795 JSTaggedValue 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 
StopRuntimeStat(EcmaRuntimeCallInfo *msg)805 JSTaggedValue 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
PrintOptStat(EcmaRuntimeCallInfo *msg)817 JSTaggedValue 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
PrintFunctionCallStat(EcmaRuntimeCallInfo *msg)829 JSTaggedValue 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 )
Escape(EcmaRuntimeCallInfo *msg)841 JSTaggedValue 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 )
Unescape(EcmaRuntimeCallInfo *msg)898 JSTaggedValue 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 
GetCurrentModuleName(EcmaRuntimeCallInfo *msg)976 JSTaggedValue 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 
GetCurrentBundleName(EcmaRuntimeCallInfo *msg)991 JSTaggedValue 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 
IsSendable(EcmaRuntimeCallInfo *msg)1007 JSTaggedValue 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