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