1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/builtins/builtins_global.h"
17
18 #include <random>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22
23 #include "ecmascript/base/number_helper.h"
24 #include "ecmascript/base/string_helper.h"
25 #include "ecmascript/ecma_macros.h"
26 #include "ecmascript/js_function.h"
27 #include "ecmascript/mem/c_containers.h"
28 #include "ecmascript/module/js_module_deregister.h"
29 #include "ecmascript/stubs/runtime_stubs.h"
30 #include "ecmascript/tagged_array-inl.h"
31
32 namespace panda::ecmascript::builtins {
33 using NumberHelper = base::NumberHelper;
34 using StringHelper = base::StringHelper;
35 // bitmap for "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" + "@*+-./"
36 constexpr std::uint8_t ESCAPE_BIT_MAP[128] = {
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, 0, 0, 0, 0, 0, 0,
39 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
41 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
42 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
43 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
44 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
45 constexpr std::uint8_t ESCAPE_HEX_TO_CHAR[16] = {
46 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
47 };
48 constexpr std::uint8_t ESCAPE_CHAR_TO_HEX[128] = {
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
53 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
57 };
58 constexpr std::uint8_t ESCAPE_HEX_MASK = 0xf;
59 constexpr std::uint8_t ESCAPE_HEX_BIT4 = 4;
60 constexpr std::uint8_t ESCAPE_HEX_BIT8 = 8;
61 constexpr std::uint8_t ESCAPE_HEX_BIT12 = 12;
62 constexpr std::uint8_t ESCAPE_CHAR_OFFSET2 = 2;
63 constexpr std::uint8_t ESCAPE_CHAR_OFFSET3 = 3;
64 constexpr std::uint8_t ESCAPE_CHAR_OFFSET4 = 4;
65 constexpr std::uint8_t ESCAPE_CHAR_OFFSET5 = 5;
66 constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0';
67
68 // 18.2.1
NotSupportEval(EcmaRuntimeCallInfo * msg)69 JSTaggedValue BuiltinsGlobal::NotSupportEval(EcmaRuntimeCallInfo *msg)
70 {
71 JSThread *thread = msg->GetThread();
72 BUILTINS_API_TRACE(thread, Global, NotSupportEval);
73 [[maybe_unused]] EcmaHandleScope handleScope(thread);
74 THROW_TYPE_ERROR_AND_RETURN(thread, "not support eval()", JSTaggedValue::Exception());
75 }
76
77 // 18.2.2
IsFinite(EcmaRuntimeCallInfo * msg)78 JSTaggedValue BuiltinsGlobal::IsFinite(EcmaRuntimeCallInfo *msg)
79 {
80 ASSERT(msg);
81 JSThread *thread = msg->GetThread();
82 BUILTINS_API_TRACE(thread, Global, IsFinite);
83 [[maybe_unused]] EcmaHandleScope handleScope(thread);
84 JSHandle<JSTaggedValue> numberInput = GetCallArg(msg, 0);
85 // 1. Let num be ToNumber(number).
86 JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput);
87 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
88 // 3. If num is NaN, +Infinite, or -Infinite, return false.
89 // 4. Otherwise, return true.
90 if (std::isfinite(number.GetNumber())) {
91 return GetTaggedBoolean(true);
92 }
93 return GetTaggedBoolean(false);
94 }
95
96 // 18.2.3
IsNaN(EcmaRuntimeCallInfo * msg)97 JSTaggedValue BuiltinsGlobal::IsNaN(EcmaRuntimeCallInfo *msg)
98 {
99 ASSERT(msg);
100 JSThread *thread = msg->GetThread();
101 BUILTINS_API_TRACE(thread, Global, IsNaN);
102 [[maybe_unused]] EcmaHandleScope handleScope(thread);
103 JSHandle<JSTaggedValue> numberInput = GetCallArg(msg, 0);
104 // 1. Let num be ToNumber(number).
105 JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput);
106 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
107
108 // 3. If num is NaN, return true.
109 if (std::isnan(number.GetNumber())) {
110 return GetTaggedBoolean(true);
111 }
112 // 4. Otherwise, return false.
113 return GetTaggedBoolean(false);
114 }
115
IsUnescapedURI(uint16_t ch)116 bool BuiltinsGlobal::IsUnescapedURI(uint16_t ch)
117 {
118 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
119 return true;
120 }
121 return IsInMarkURISet(ch);
122 }
123
IsInUnescapedURISet(uint16_t ch)124 bool BuiltinsGlobal::IsInUnescapedURISet(uint16_t ch)
125 {
126 if (ch == '#') {
127 return true;
128 }
129 return IsUnescapedURI(ch) || IsReservedURI(ch);
130 }
131
IsInReservedURISet(uint16_t ch)132 bool BuiltinsGlobal::IsInReservedURISet(uint16_t ch)
133 {
134 if (ch == '#') {
135 return true;
136 }
137 return IsReservedURI(ch);
138 }
139
IsReservedURI(uint16_t ch)140 bool BuiltinsGlobal::IsReservedURI(uint16_t ch)
141 {
142 std::u16string str(u";/?:@&=+$,");
143 std::u16string::size_type index = str.find(ch);
144 return (index != std::u16string::npos);
145 }
146
IsInMarkURISet(uint16_t ch)147 bool BuiltinsGlobal::IsInMarkURISet(uint16_t ch)
148 {
149 std::u16string str(u"-_.!~*'()");
150 std::u16string::size_type index = str.find(ch);
151 return (index != std::u16string::npos);
152 }
153
IsHexDigits(uint16_t ch)154 bool BuiltinsGlobal::IsHexDigits(uint16_t ch)
155 {
156 return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f');
157 }
158
159 // 18.2.6
DecodeURI(EcmaRuntimeCallInfo * msg)160 JSTaggedValue BuiltinsGlobal::DecodeURI(EcmaRuntimeCallInfo *msg)
161 {
162 ASSERT(msg);
163 JSThread *thread = msg->GetThread();
164 BUILTINS_API_TRACE(thread, Global, DecodeURI);
165 [[maybe_unused]] EcmaHandleScope handleScope(thread);
166 // 1. Let uriString be ToString(encodedURI).
167 // 2. ReturnIfAbrupt(uriString).
168 JSHandle<EcmaString> uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
169 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
170
171 // 3. Let reservedURISet be a String containing one instance of each code unit valid in uriReserved plus "#".
172 // 4. Return Decode(uriString, reservedURISet).
173 return Decode(thread, uriString, IsInReservedURISet);
174 }
175
EncodeURI(EcmaRuntimeCallInfo * msg)176 JSTaggedValue BuiltinsGlobal::EncodeURI(EcmaRuntimeCallInfo *msg)
177 {
178 ASSERT(msg);
179 JSThread *thread = msg->GetThread();
180 BUILTINS_API_TRACE(thread, Global, EncodeURI);
181 [[maybe_unused]] EcmaHandleScope handleScope(thread);
182 // 1. Let uriString be ToString(uri).
183 // 2. ReturnIfAbrupt(uriString).
184 JSHandle<EcmaString> uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
185 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
186
187 // 3. Let unescapedURISet be a String containing one instance of
188 // each code unit valid in uriReserved and uriUnescaped plus "#".
189 // 4. Return Encode(uriString, unescapedURISet).
190 return Encode(thread, uriString, IsInUnescapedURISet);
191 }
192
DecodeURIComponent(EcmaRuntimeCallInfo * msg)193 JSTaggedValue BuiltinsGlobal::DecodeURIComponent(EcmaRuntimeCallInfo *msg)
194 {
195 ASSERT(msg);
196 JSThread *thread = msg->GetThread();
197 BUILTINS_API_TRACE(thread, Global, DecodeURIComponent);
198 [[maybe_unused]] EcmaHandleScope handleScope(thread);
199 // 1. Let componentString be ToString(encodedURIComponent).
200 // 2. ReturnIfAbrupt(componentString).
201 JSHandle<EcmaString> componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
202 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
203
204 // 3. Let reservedURIComponentSet be the empty String.
205 // 4. Return Decode(componentString, reservedURIComponentSet).
206 return Decode(thread, componentString, []([[maybe_unused]] uint16_t unused) { return false; });
207 }
208
EncodeURIComponent(EcmaRuntimeCallInfo * msg)209 JSTaggedValue BuiltinsGlobal::EncodeURIComponent(EcmaRuntimeCallInfo *msg)
210 {
211 ASSERT(msg);
212 JSThread *thread = msg->GetThread();
213 BUILTINS_API_TRACE(thread, Global, EncodeURIComponent);
214 [[maybe_unused]] EcmaHandleScope handleScope(thread);
215 // 1. Let componentString be ToString(uriComponent).
216 // 2. ReturnIfAbrupt(componentString).
217 JSHandle<EcmaString> componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
218 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
219
220 // 3. Let unescapedURIComponentSet be a String containing one instance of each code unit valid in uriUnescaped.
221 // 4. Return Encode(componentString, unescapedURIComponentSet).
222 return Encode(thread, componentString, IsUnescapedURI);
223 }
224
225 // Runtime Semantics
Encode(JSThread * thread,const JSHandle<EcmaString> & str,judgURIFunc IsInURISet)226 JSTaggedValue BuiltinsGlobal::Encode(JSThread *thread, const JSHandle<EcmaString> &str, judgURIFunc IsInURISet)
227 {
228 BUILTINS_API_TRACE(thread, Global, Encode);
229 // 1. Let strLen be the number of code units in string.
230 uint32_t strLen = EcmaStringAccessor(str).GetLength();
231 // 2. Let R be the empty String.
232 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
233 std::u16string resStr;
234
235 // 3. Let k be 0.
236 // 4. Repeat
237 uint32_t k = 0;
238 while (true) {
239 // a. If k equals strLen, return R.
240 if (k == strLen) {
241 auto *uint16tData = reinterpret_cast<uint16_t *>(resStr.data());
242 uint32_t resSize = resStr.size();
243 return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue();
244 }
245
246 // b. Let C be the code unit at index k within string.
247 // c. If C is in unescapedSet, then
248 // i. Let S be a String containing only the code unit C.
249 // ii. Let R be a new String value computed by concatenating the previous value of R and S.
250 // d. Else C is not in unescapedSet,
251 uint16_t cc = EcmaStringAccessor(str).Get(k);
252 if (IsInURISet(cc)) {
253 std::u16string sStr = StringHelper::Utf16ToU16String(&cc, 1);
254 resStr.append(sStr);
255 } else {
256 // i. If the code unit value of C is not less than 0xDC00 and not greater than 0xDFFF,
257 // throw a URIError exception.
258 if (cc >= base::utf_helper::DECODE_TRAIL_LOW && cc <= base::utf_helper::DECODE_TRAIL_HIGH) {
259 THROW_URI_ERROR_AND_RETURN(thread, "EncodeURI: The format of the URI to be parsed is incorrect",
260 JSTaggedValue::Exception());
261 }
262
263 // ii. If the code unit value of C is less than 0xD800 or greater than 0xDBFF, then
264 // 1. Let V be the code unit value of C.
265 // iii. Else,
266 // 1. Increase k by 1.
267 // 2. If k equals strLen, throw a URIError exception.
268 // 3. Let kChar be the code unit value of the code unit at index k within string.
269 // 4. If kChar is less than 0xDC00 or greater than 0xDFFF, throw a URIError exception.
270 // 5. Let V be UTF16Decode(C, kChar).
271 uint32_t vv;
272 if (cc < base::utf_helper::DECODE_LEAD_LOW || cc > base::utf_helper::DECODE_LEAD_HIGH) {
273 vv = cc;
274 } else {
275 k++;
276 if (k == strLen) {
277 THROW_URI_ERROR_AND_RETURN(thread, "k is invalid", JSTaggedValue::Exception());
278 }
279 uint16_t kc = EcmaStringAccessor(str).Get(k);
280 if (kc < base::utf_helper::DECODE_TRAIL_LOW || kc > base::utf_helper::DECODE_TRAIL_HIGH) {
281 THROW_URI_ERROR_AND_RETURN(thread, "EncodeURI: The format of the URI to be parsed is incorrect",
282 JSTaggedValue::Exception());
283 }
284 vv = base::utf_helper::UTF16Decode(cc, kc);
285 }
286
287 // iv. Let Octets be the array of octets resulting by applying the UTF-8 transformation to V,
288 // and let L be the array size.
289 // v. Let j be 0.
290 // vi. Repeat, while j < L
291 // 1. Let jOctet be the value at index j within Octets.
292 // 2. Let S be a String containing three code units "%XY" where XY are two uppercase hexadecimal
293 // digits encoding the value of jOctet.
294 // 3. Let R be a new String value computed by concatenating the previous value of R and S.
295 // 4. Increase j by 1.
296 std::string oct = StringHelper::Utf32ToString(vv);
297 std::string hexStr("0123456789ABCDEF");
298
299 uint32_t length = oct.length();
300 std::stringstream tmpStr;
301 for (uint32_t j = 0; j < length; j++) {
302 uint8_t joct = static_cast<uint8_t>(oct.at(j));
303 tmpStr << '%' << hexStr.at((joct >> 4U) & BIT_MASK) // NOLINT
304 << hexStr.at(joct & BIT_MASK); // 4: means shift right by 4 digits
305 }
306 resStr.append(StringHelper::StringToU16string(tmpStr.str()));
307 }
308
309 // e. Increase k by 1.
310 k++;
311 }
312 }
313
GetValueFromTwoHex(uint16_t front,uint16_t behind)314 uint8_t BuiltinsGlobal::GetValueFromTwoHex(uint16_t front, uint16_t behind)
315 {
316 ASSERT(IsHexDigits(front) && IsHexDigits(behind));
317 std::u16string hexString(u"0123456789ABCDEF");
318
319 size_t idxf = StringHelper::FindFromU16ToUpper(hexString, &front);
320 size_t idxb = StringHelper::FindFromU16ToUpper(hexString, &behind);
321 uint8_t res = ((idxf << 4U) | idxb) & BIT_MASK_FF; // NOLINT 4: means shift left by 4 digits
322 return res;
323 }
324
GetValueFromHexString(const JSHandle<EcmaString> & string)325 uint16_t BuiltinsGlobal::GetValueFromHexString(const JSHandle<EcmaString> &string)
326 {
327 uint32_t size = EcmaStringAccessor(string).GetLength();
328 ASSERT(size > 0 && size <= 4); // NOLINT 4: means 4 hex digits
329 std::u16string hexString(u"0123456789ABCDEF");
330
331 uint16_t ret = 0;
332 for (uint32_t i = 0; i < size; ++i) {
333 uint16_t ch = EcmaStringAccessor(string).Get(i);
334 size_t idx = StringHelper::FindFromU16ToUpper(hexString, &ch);
335 ret = ((ret << 4U) | idx) & BIT_MASK_4F; // NOLINT 4: means shift left by 4
336 }
337 return ret;
338 }
339
340 // 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)341 EcmaString *BuiltinsGlobal::StringPad(JSThread *thread, const JSHandle<EcmaString> &source,
342 uint32_t maxLength, const JSHandle<EcmaString> &fillString,
343 Placement placement)
344 {
345 // 1. Let stringLength be the length of S.
346 uint32_t stringLength = EcmaStringAccessor(source).GetLength();
347 // 2. If maxLength ≤ stringLength, return S.
348 if (maxLength <= stringLength) {
349 return *source;
350 }
351 // 3. If fillString is the empty String, return S.
352 uint32_t targetStrLen = EcmaStringAccessor(fillString).GetLength();
353 if (targetStrLen == 0) {
354 return *source;
355 }
356 // 4. Let fillLen be maxLength - stringLength.
357 uint32_t fillLen = maxLength - stringLength;
358 EcmaVM *vm = thread->GetEcmaVM();
359 //5. Let truncatedStringFiller be the String value consisting of repeated concatenations
360 // of fillString truncated to length fillLen.
361 uint32_t repeatTimes = std::ceil(fillLen / targetStrLen);
362 EcmaString *p = nullptr;
363 JSHandle<EcmaString> stringFiller = vm->GetFactory()->NewFromStdString(std::string("\0"));
364 for (uint32_t k = 0; k < repeatTimes; ++k) {
365 p = EcmaStringAccessor::Concat(vm, stringFiller, fillString);
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(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(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 // Runtime Semantics
Decode(JSThread * thread,const JSHandle<EcmaString> & str,judgURIFunc IsInURISet)446 JSTaggedValue BuiltinsGlobal::Decode(JSThread *thread, const JSHandle<EcmaString> &str, judgURIFunc IsInURISet)
447 {
448 BUILTINS_API_TRACE(thread, Global, Decode);
449 // 1. Let strLen be the number of code units in string.
450 int32_t strLen = static_cast<int32_t>(EcmaStringAccessor(str).GetLength());
451 // 2. Let R be the empty String.
452 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
453 std::u16string resStr;
454
455 // 3. Let k be 0.
456 // 4. Repeat
457 int32_t k = 0;
458 while (true) {
459 // a. If k equals strLen, return R.
460 if (k == strLen) {
461 auto *uint16tData = reinterpret_cast<uint16_t *>(resStr.data());
462 uint32_t resSize = resStr.size();
463 return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue();
464 }
465
466 // b. Let C be the code unit at index k within string.
467 // c. If C is not "%", then
468 // i. Let S be the String containing only the code unit C.
469 // d. Else C is "%",
470 // i. Let start be k.
471 // iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2).
472 // v. Increase k by 2.
473 // vi. If the most significant bit in B is 0, then
474 // 1. Let C be the code unit with code unit value B.
475 // 2. If C is not in reservedSet, then
476 // a. Let S be the String containing only the code unit C.
477 // 3. Else C is in reservedSet,
478 // a. Let S be the substring of string from index start to index k inclusive.
479 uint16_t cc = EcmaStringAccessor(str).Get(k);
480 std::u16string sStr;
481 if (cc != '%') {
482 if (cc == 0 && strLen == 1) {
483 JSHandle<EcmaString> tmpEcmaString = factory->NewFromUtf16Literal(&cc, 1);
484 return tmpEcmaString.GetTaggedValue();
485 }
486 sStr = StringHelper::Utf16ToU16String(&cc, 1);
487 } else {
488 [[maybe_unused]] uint32_t start = static_cast<uint32_t>(k);
489
490 // ii. If k + 2 is greater than or equal to strLen, throw a URIError exception.
491 // iii. If the code units at index (k+1) and (k + 2) within string do not represent hexadecimal digits,
492 // throw a URIError exception.
493 if ((k + 2) >= strLen) { // 2: means plus 2
494 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
495 JSTaggedValue::Exception());
496 }
497 if (!(IsHexDigits(EcmaStringAccessor(str).Get(k + 1)) &&
498 IsHexDigits(EcmaStringAccessor(str).Get(k + 2)))) { // 2: means plus 2
499 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
500 JSTaggedValue::Exception());
501 }
502
503 uint16_t frontChar = EcmaStringAccessor(str).Get(k + 1);
504 uint16_t behindChar = EcmaStringAccessor(str).Get(k + 2); // 2: means plus 2
505 uint8_t bb = GetValueFromTwoHex(frontChar, behindChar);
506 k += 2; // 2: means plus 2
507 if ((bb & BIT_MASK_ONE) == 0) {
508 if (!IsInURISet(bb)) {
509 sStr = StringHelper::Utf8ToU16String(&bb, 1);
510 } else {
511 auto substr = EcmaStringAccessor::FastSubString(
512 thread->GetEcmaVM(), str, start, static_cast<uint32_t>(k) - start + 1U);
513 sStr = StringHelper::StringToU16string(
514 EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION));
515 }
516 } else {
517 // vii. Else the most significant bit in B is 1,
518 // 1. Let n be the smallest nonnegative integer such that (B << n) & 0x80 is equal to 0.
519 // 3. Let Octets be an array of 8-bit integers of size n.
520 // 4. Put B into Octets at index 0.
521 // 6. Let j be 1.
522 // 7. Repeat, while j < n
523 // a. Increase k by 1.
524 // d. Let B be the 8-bit value represented by the two hexadecimal digits at
525 // index (k + 1) and (k + 2).
526 // f. Increase k by 2.
527 // g. Put B into Octets at index j.
528 // h. Increase j by 1.
529 // 9. If V < 0x10000, then
530 // a. Let C be the code unit V.
531 // b. If C is not in reservedSet, then
532 // i. Let S be the String containing only the code unit C.
533 // c. Else C is in reservedSet,
534 // i. Let S be the substring of string from index start to index k inclusive.
535 // 10. Else V ≥ 0x10000,
536 // a. Let L be (((V – 0x10000) & 0x3FF) + 0xDC00).
537 // b. Let H be ((((V – 0x10000) >> 10) & 0x3FF) + 0xD800).
538 // c. Let S be the String containing the two code units H and L.
539 int32_t n = 0;
540 while ((((static_cast<uint32_t>(bb) << static_cast<uint32_t>(n)) & BIT_MASK_ONE) != 0)) {
541 n++;
542 if (n > 4) { // 4 : 4 means less than 4
543 break;
544 }
545 }
546 // 2. If n equals 1 or n is greater than 4, throw a URIError exception.
547 if ((n == 1) || (n > 4)) { // 4: means greater than 4
548 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
549 JSTaggedValue::Exception());
550 }
551
552 std::vector<uint8_t> oct = {bb};
553
554 // 5. If k + (3 × (n – 1)) is greater than or equal to strLen, throw a URIError exception.
555 if (k + (3 * (n - 1)) >= strLen) { // 3: means multiply by 3
556 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
557 JSTaggedValue::Exception());
558 }
559 int32_t j = 1;
560 while (j < n) {
561 k++;
562 uint16_t codeUnit = EcmaStringAccessor(str).Get(k);
563 // b. If the code unit at index k within string is not "%", throw a URIError exception.
564 // c. If the code units at index (k +1) and (k + 2) within string do not represent hexadecimal
565 // digits, throw a URIError exception.
566 if (!(codeUnit == '%')) {
567 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
568 JSTaggedValue::Exception());
569 }
570 if (!(IsHexDigits(EcmaStringAccessor(str).Get(k + 1)) &&
571 IsHexDigits(EcmaStringAccessor(str).Get(k + 2)))) { // 2: means plus 2
572 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
573 JSTaggedValue::Exception());
574 }
575
576 uint16_t frontChart = EcmaStringAccessor(str).Get(k + 1);
577 uint16_t behindChart = EcmaStringAccessor(str).Get(k + 2); // 2: means plus 2
578 bb = GetValueFromTwoHex(frontChart, behindChart);
579 // e. If the two most significant bits in B are not 10, throw a URIError exception.
580 if (!((bb & BIT_MASK_TWO) == BIT_MASK_ONE)) {
581 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
582 JSTaggedValue::Exception());
583 }
584
585 k += 2; // 2: means plus 2
586 oct.push_back(bb);
587 j++;
588 }
589
590 // 8. Let V be the value obtained by applying the UTF-8 transformation to Octets, that is,
591 // from an array of octets into a 21-bit value. If Octets does not contain a valid UTF-8 encoding of
592 // a Unicode code point throw a URIError exception.
593 if (!base::utf_helper::IsValidUTF8(oct)) {
594 THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect",
595 JSTaggedValue::Exception());
596 }
597 uint32_t vv = StringHelper::Utf8ToU32String(oct);
598 if (vv < base::utf_helper::DECODE_SECOND_FACTOR) {
599 if (!IsInURISet(vv)) {
600 sStr = StringHelper::Utf16ToU16String(reinterpret_cast<uint16_t *>(&vv), 1);
601 } else {
602 auto substr = EcmaStringAccessor::FastSubString(
603 thread->GetEcmaVM(), str, start, static_cast<uint32_t>(k) - start + 1U);
604 sStr = StringHelper::StringToU16string(
605 EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION));
606 }
607 } else {
608 uint16_t lv = (((vv - base::utf_helper::DECODE_SECOND_FACTOR) & BIT16_MASK) +
609 base::utf_helper::DECODE_TRAIL_LOW);
610 uint16_t hv = ((((vv - base::utf_helper::DECODE_SECOND_FACTOR) >> 10U) & BIT16_MASK) + // NOLINT
611 base::utf_helper::DECODE_LEAD_LOW); // 10: means shift left by 10 digits
612 sStr = StringHelper::Append(StringHelper::Utf16ToU16String(&hv, 1),
613 StringHelper::Utf16ToU16String(&lv, 1));
614 }
615 }
616 }
617 // e. Let R be a new String value computed by concatenating the previous value of R and S.
618 // f. Increase k by 1.
619 resStr.append(sStr);
620 k++;
621 }
622 }
623
PrintString(JSThread * thread,EcmaString * string)624 void BuiltinsGlobal::PrintString([[maybe_unused]] JSThread *thread, EcmaString *string)
625 {
626 if (string == nullptr) {
627 return;
628 }
629 BUILTINS_API_TRACE(thread, Global, PrintString);
630 CString buffer = ConvertToString(string);
631 std::cout << buffer;
632 }
633
PrintEntrypoint(EcmaRuntimeCallInfo * msg)634 JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg)
635 {
636 if (msg == nullptr) {
637 return JSTaggedValue::Undefined();
638 }
639 JSThread *thread = msg->GetThread();
640 [[maybe_unused]] EcmaHandleScope handleScope(thread);
641 BUILTINS_API_TRACE(thread, Global, PrintEntryPoint);
642
643 uint32_t numArgs = msg->GetArgsNumber();
644 for (uint32_t i = 0; i < numArgs; i++) {
645 JSHandle<EcmaString> stringContent = JSTaggedValue::ToString(thread, GetCallArg(msg, i));
646 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
647 PrintString(thread, *stringContent);
648
649 if (i != numArgs - 1) {
650 std::cout << " ";
651 }
652 }
653 std::cout << std::endl;
654 return JSTaggedValue::Undefined();
655 }
656
MarkModuleCollectable(EcmaRuntimeCallInfo * msg)657 JSTaggedValue BuiltinsGlobal::MarkModuleCollectable(EcmaRuntimeCallInfo *msg)
658 {
659 ASSERT(msg);
660 JSThread *thread = msg->GetThread();
661 [[maybe_unused]] EcmaHandleScope handleScope(thread);
662
663 uint32_t numArgs = msg->GetArgsNumber();
664 if (numArgs != 1) {
665 LOG_FULL(ERROR) << "The number of parameters received by markModuleCollectable is incorrect.";
666 return JSTaggedValue::False();
667 }
668 JSHandle<JSTaggedValue> module = GetCallArg(msg, 0);
669 if (!module->IsModuleNamespace()) {
670 return JSTaggedValue::False();
671 }
672
673 ModuleDeregister::ProcessModuleReference(thread, module);
674 return JSTaggedValue::True();
675 }
676
CallJsBoundFunction(EcmaRuntimeCallInfo * msg)677 JSTaggedValue BuiltinsGlobal::CallJsBoundFunction(EcmaRuntimeCallInfo *msg)
678 {
679 JSThread *thread = msg->GetThread();
680 BUILTINS_API_TRACE(thread, Global, CallJsBoundFunction);
681 [[maybe_unused]] EcmaHandleScope handleScope(thread);
682 // msg contains jsfunc, this, arg1,...
683
684 JSHandle<JSBoundFunction> boundFunc(GetConstructor(msg));
685 JSHandle<JSTaggedValue> thisObj(thread, boundFunc->GetBoundThis());
686 msg->SetThis(thisObj.GetTaggedValue());
687 return RuntimeStubs::CallBoundFunction(msg);
688 }
689
CallJsProxy(EcmaRuntimeCallInfo * msg)690 JSTaggedValue BuiltinsGlobal::CallJsProxy(EcmaRuntimeCallInfo *msg)
691 {
692 JSThread *thread = msg->GetThread();
693 BUILTINS_API_TRACE(thread, Global, CallJsProxy);
694 [[maybe_unused]] EcmaHandleScope handleScope(thread);
695 // msg contains js_proxy, this, arg1,...
696 JSHandle<JSProxy> proxy(GetConstructor(msg));
697 if (!proxy->IsCallable()) {
698 THROW_TYPE_ERROR_AND_RETURN(thread, "Proxy target is not callable", JSTaggedValue::Undefined());
699 }
700
701 // Calling proxy directly should transfer 'undefined' as this
702 return JSProxy::CallInternal(msg);
703 }
704
705 #if ECMASCRIPT_ENABLE_RUNTIME_STAT
StartRuntimeStat(EcmaRuntimeCallInfo * msg)706 JSTaggedValue BuiltinsGlobal::StartRuntimeStat(EcmaRuntimeCallInfo *msg)
707 {
708 JSThread *thread = msg->GetThread();
709 BUILTINS_API_TRACE(thread, Global, StartRuntimeStat);
710 [[maybe_unused]] EcmaHandleScope handleScope(thread);
711 // start vm runtime stat statistic
712 thread->GetCurrentEcmaContext()->SetRuntimeStatEnable(true);
713 return JSTaggedValue::Undefined();
714 }
715
StopRuntimeStat(EcmaRuntimeCallInfo * msg)716 JSTaggedValue BuiltinsGlobal::StopRuntimeStat(EcmaRuntimeCallInfo *msg)
717 {
718 JSThread *thread = msg->GetThread();
719 BUILTINS_API_TRACE(thread, Global, StopRuntimeStat);
720 [[maybe_unused]] EcmaHandleScope handleScope(thread);
721 // start vm runtime stat statistic
722 thread->GetCurrentEcmaContext()->SetRuntimeStatEnable(false);
723 return JSTaggedValue::Undefined();
724 }
725 #endif
726
727 #if ECMASCRIPT_ENABLE_OPT_CODE_PROFILER
PrintOptStat(EcmaRuntimeCallInfo * msg)728 JSTaggedValue BuiltinsGlobal::PrintOptStat(EcmaRuntimeCallInfo *msg)
729 {
730 JSThread *thread = msg->GetThread();
731 BUILTINS_API_TRACE(thread, Global, PrintOptStat);
732 [[maybe_unused]] EcmaHandleScope handleScope(thread);
733 // start vm runtime stat statistic
734 thread->GetCurrentEcmaContext()->PrintOptStat();
735 return JSTaggedValue::Undefined();
736 }
737 #endif
738
739 #if ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER
PrintFunctionCallStat(EcmaRuntimeCallInfo * msg)740 JSTaggedValue BuiltinsGlobal::PrintFunctionCallStat(EcmaRuntimeCallInfo *msg)
741 {
742 JSThread *thread = msg->GetThread();
743 BUILTINS_API_TRACE(thread, Global, PrintFunctionCallStat);
744 [[maybe_unused]] EcmaHandleScope handleScope(thread);
745 // start vm runtime stat statistic
746 thread->GetEcmaVM()->DumpCallTimeInfo();
747 return JSTaggedValue::Undefined();
748 }
749 #endif
750
751 // B.2.1.1 escape ( string )
Escape(EcmaRuntimeCallInfo * msg)752 JSTaggedValue BuiltinsGlobal::Escape(EcmaRuntimeCallInfo *msg)
753 {
754 ASSERT(msg);
755 JSThread *thread = msg->GetThread();
756 BUILTINS_API_TRACE(thread, Global, Escape);
757 [[maybe_unused]] EcmaHandleScope handleScope(thread);
758 // 1. Set string to ? ToString(string).
759 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
760 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
761 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
762 // 2. Let len be the length of string.
763 uint32_t len = EcmaStringAccessor(string).GetLength();
764 // 3. Let R be the empty String.
765 std::u16string r;
766 // 4. Let unescapedSet be the string-concatenation of the ASCII word characters and "@*+-./".
767 // 5. Let k be 0.
768 uint32_t k = 0;
769 // 6. Repeat, while k < len,
770 // a. Let C be the code unit at index k within string.
771 // b. If unescapedSet contains C, then
772 // i. Let S be C.
773 // c. Else,
774 // i. Let n be the numeric value of C.
775 // ii. If n < 256, then
776 // 1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number.
777 // 2. Let S be the string-concatenation of "%" and StringPad(hex, 2, "0", start).
778 // iii. Else,
779 // 1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number.
780 // 2. Let S be the string-concatenation of "%u" and StringPad(hex, 4, "0", start).
781 // d. Set R to the string-concatenation of R and S.
782 // e. Set k to k + 1.
783 while (k < len) {
784 uint16_t c = EcmaStringAccessor(string).Get(k);
785 if (c < std::numeric_limits<int8_t>::max() && ESCAPE_BIT_MAP[c] == 1) {
786 r.push_back(c);
787 } else {
788 r.push_back('%');
789 if (c <= std::numeric_limits<uint8_t>::max()) {
790 r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT4) & ESCAPE_HEX_MASK]);
791 r.push_back(ESCAPE_HEX_TO_CHAR[c & ESCAPE_HEX_MASK]);
792 } else {
793 r.push_back('u');
794 r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT12) & ESCAPE_HEX_MASK]);
795 r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT8) & ESCAPE_HEX_MASK]);
796 r.push_back(ESCAPE_HEX_TO_CHAR[(c >> ESCAPE_HEX_BIT4) & ESCAPE_HEX_MASK]);
797 r.push_back(ESCAPE_HEX_TO_CHAR[c & ESCAPE_HEX_MASK]);
798 }
799 }
800 ++k;
801 }
802 // 7. Return R.
803 auto *returnData = reinterpret_cast<uint16_t *>(r.data());
804 uint32_t retSize = r.size();
805 return factory->NewFromUtf16Literal(returnData, retSize).GetTaggedValue();
806 }
807
808 // B.2.1.2 unescape ( string )
Unescape(EcmaRuntimeCallInfo * msg)809 JSTaggedValue BuiltinsGlobal::Unescape(EcmaRuntimeCallInfo *msg)
810 {
811 ASSERT(msg);
812 JSThread *thread = msg->GetThread();
813 BUILTINS_API_TRACE(thread, Global, Unescape);
814 [[maybe_unused]] EcmaHandleScope handleScope(thread);
815 // 1. Set string to ? ToString(string).
816 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, GetCallArg(msg, 0));
817 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
818 // 2. Let len be the length of string.
819 uint32_t len = EcmaStringAccessor(string).GetLength();
820 // 3. Let R be the empty String.
821 EcmaVM *vm = thread->GetEcmaVM();
822 ObjectFactory *factory = vm->GetFactory();
823 std::u16string r;
824 // 4. Let k be 0.
825 uint32_t k = 0;
826 // 5. Repeat, while k < len,
827 // a. Let C be the code unit at index k within string.
828 // b. If C is the code unit 0x0025 (PERCENT SIGN), then
829 // i. Let hexDigits be the empty String.
830 // ii. Let optionalAdvance be 0.
831 // iii. If k + 5 < len and the code unit at index k + 1 within string is the code unit
832 // 0x0075 (LATIN SMALL LETTER U), then
833 // 1. Set hexDigits to the substring of string from k + 2 to k + 6.
834 // 2. Set optionalAdvance to 5.
835 // iv. Else if k + 3 ≤ len, then
836 // 1. Set hexDigits to the substring of string from k + 1 to k + 3.
837 // 2. Set optionalAdvance to 2.
838 // v. Let parseResult be ParseText(StringToCodePoints(hexDigits), HexDigits[~Sep]).
839 // vi. If parseResult is a Parse Node, then
840 // 1. Let n be the MV of parseResult.
841 // 2. Set C to the code unit whose numeric value is n.
842 // 3. Set k to k + optionalAdvance.
843 // c. Set R to the string-concatenation of R and C.
844 // d. Set k to k + 1.
845 while (k < len) {
846 uint16_t c = EcmaStringAccessor(string).Get(k);
847 if (c == '%') {
848 uint16_t c1 = EcmaStringAccessor(string).Get(k + 1);
849 if (k + ESCAPE_CHAR_OFFSET5 < len && c1 == 'u') {
850 uint16_t c2 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET2);
851 uint16_t c3 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET3);
852 uint16_t c4 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET4);
853 uint16_t c5 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET5);
854 bool c2IsHexDigits = IsHexDigits(c2);
855 bool c3IsHexDigits = IsHexDigits(c3);
856 bool c4IsHexDigits = IsHexDigits(c4);
857 bool c5IsHexDigits = IsHexDigits(c5);
858 bool isHexDigits = c2IsHexDigits && c3IsHexDigits && c4IsHexDigits && c5IsHexDigits;
859 if (isHexDigits) {
860 c = ESCAPE_CHAR_TO_HEX[c2];
861 c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c3];
862 c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c4];
863 c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c5];
864 k = k + ESCAPE_CHAR_OFFSET5;
865 }
866 } else if (k + ESCAPE_CHAR_OFFSET3 <= len) {
867 uint16_t c2 = EcmaStringAccessor(string).Get(k + ESCAPE_CHAR_OFFSET2);
868 bool c1IsHexDigits = IsHexDigits(c1);
869 bool c2IsHexDigits = IsHexDigits(c2);
870 bool isHexDigits = c1IsHexDigits && c2IsHexDigits;
871 if (isHexDigits) {
872 c = ESCAPE_CHAR_TO_HEX[c1];
873 c = (c << ESCAPE_HEX_BIT4) | ESCAPE_CHAR_TO_HEX[c2];
874 k = k + ESCAPE_CHAR_OFFSET2;
875 }
876 }
877 }
878 r.push_back(c);
879 ++k;
880 }
881 // 7. Return R.
882 auto *returnData = reinterpret_cast<uint16_t *>(r.data());
883 uint32_t retSize = r.size();
884 return factory->NewFromUtf16Literal(returnData, retSize).GetTaggedValue();
885 }
886 } // namespace panda::ecmascript::builtins
887