1/*
2 * Copyright (C) 1999-2002 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003-2019 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich ([email protected])
6 * Copyright (C) 2007 Maks Orlovich
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "JSGlobalObjectFunctions.h"
27
28#include "CallFrame.h"
29#include "CatchScope.h"
30#include "EvalExecutable.h"
31#include "Exception.h"
32#include "IndirectEvalExecutable.h"
33#include "Interpreter.h"
34#include "IntlDateTimeFormat.h"
35#include "IntlObject.h"
36#include "JSCInlines.h"
37#include "JSFunction.h"
38#include "JSGlobalObject.h"
39#include "JSInternalPromise.h"
40#include "JSModuleLoader.h"
41#include "JSPromise.h"
42#include "JSString.h"
43#include "Lexer.h"
44#include "LiteralParser.h"
45#include "Nodes.h"
46#include "ObjectConstructor.h"
47#include "JSCInlines.h"
48#include "ParseInt.h"
49#include "Parser.h"
50#include "StackVisitor.h"
51#include <stdio.h>
52#include <stdlib.h>
53#include <unicode/utf8.h>
54#include <wtf/ASCIICType.h>
55#include <wtf/Assertions.h>
56#include <wtf/HexNumber.h>
57#include <wtf/MathExtras.h>
58#include <wtf/dtoa.h>
59#include <wtf/text/StringBuilder.h>
60
61namespace JSC {
62
63const ASCIILiteral ObjectProtoCalledOnNullOrUndefinedError { "Object.prototype.__proto__ called on null or undefined"_s };
64
65template<unsigned charactersCount>
66static Bitmap<256> makeCharacterBitmap(const char (&characters)[charactersCount])
67{
68 static_assert(charactersCount > 0, "Since string literal is null terminated, characterCount is always larger than 0");
69 Bitmap<256> bitmap;
70 for (unsigned i = 0; i < charactersCount - 1; ++i)
71 bitmap.set(characters[i]);
72 return bitmap;
73}
74
75template<typename CharacterType>
76static JSValue encode(JSGlobalObject* globalObject, const Bitmap<256>& doNotEscape, const CharacterType* characters, unsigned length)
77{
78 VM& vm = globalObject->vm();
79 auto scope = DECLARE_THROW_SCOPE(vm);
80
81 // 18.2.6.1.1 Runtime Semantics: Encode ( string, unescapedSet )
82 // https://tc39.github.io/ecma262/#sec-encode
83
84 auto throwException = [&scope, globalObject] {
85 return JSC::throwException(globalObject, scope, createURIError(globalObject, "String contained an illegal UTF-16 sequence."_s));
86 };
87
88 StringBuilder builder(StringBuilder::OverflowHandler::RecordOverflow);
89 builder.reserveCapacity(length);
90
91 // 4. Repeat
92 auto* end = characters + length;
93 for (auto* cursor = characters; cursor != end; ++cursor) {
94 auto character = *cursor;
95
96 // 4-c. If C is in unescapedSet, then
97 if (character < doNotEscape.size() && doNotEscape.get(character)) {
98 // 4-c-i. Let S be a String containing only the code unit C.
99 // 4-c-ii. Let R be a new String value computed by concatenating the previous value of R and S.
100 builder.append(static_cast<LChar>(character));
101 continue;
102 }
103
104 // 4-d-i. If the code unit value of C is not less than 0xDC00 and not greater than 0xDFFF, throw a URIError exception.
105 if (U16_IS_TRAIL(character))
106 return throwException();
107
108 // 4-d-ii. If the code unit value of C is less than 0xD800 or greater than 0xDBFF, then
109 // 4-d-ii-1. Let V be the code unit value of C.
110 UChar32 codePoint;
111 if (!U16_IS_LEAD(character))
112 codePoint = character;
113 else {
114 // 4-d-iii. Else,
115 // 4-d-iii-1. Increase k by 1.
116 ++cursor;
117
118 // 4-d-iii-2. If k equals strLen, throw a URIError exception.
119 if (cursor == end)
120 return throwException();
121
122 // 4-d-iii-3. Let kChar be the code unit value of the code unit at index k within string.
123 auto trail = *cursor;
124
125 // 4-d-iii-4. If kChar is less than 0xDC00 or greater than 0xDFFF, throw a URIError exception.
126 if (!U16_IS_TRAIL(trail))
127 return throwException();
128
129 // 4-d-iii-5. Let V be UTF16Decode(C, kChar).
130 codePoint = U16_GET_SUPPLEMENTARY(character, trail);
131 }
132
133 // 4-d-iv. Let Octets be the array of octets resulting by applying the UTF-8 transformation to V, and let L be the array size.
134 LChar utf8OctetsBuffer[U8_MAX_LENGTH];
135 unsigned utf8Length = 0;
136 // We can use U8_APPEND_UNSAFE here since codePoint is either
137 // 1. non surrogate one, correct code point.
138 // 2. correct code point generated from validated lead and trail surrogates.
139 U8_APPEND_UNSAFE(utf8OctetsBuffer, utf8Length, codePoint);
140
141 // 4-d-v. Let j be 0.
142 // 4-d-vi. Repeat, while j < L
143 for (unsigned index = 0; index < utf8Length; ++index) {
144 // 4-d-vi-1. Let jOctet be the value at index j within Octets.
145 // 4-d-vi-2. Let S be a String containing three code units "%XY" where XY are two uppercase hexadecimal digits encoding the value of jOctet.
146 // 4-d-vi-3. Let R be a new String value computed by concatenating the previous value of R and S.
147 builder.append('%');
148 appendByteAsHex(utf8OctetsBuffer[index], builder);
149 }
150 }
151
152 if (UNLIKELY(builder.hasOverflowed()))
153 return throwOutOfMemoryError(globalObject, scope);
154 return jsString(vm, builder.toString());
155}
156
157static JSValue encode(JSGlobalObject* globalObject, JSValue argument, const Bitmap<256>& doNotEscape)
158{
159 return toStringView(globalObject, argument, [&] (StringView view) {
160 if (view.is8Bit())
161 return encode(globalObject, doNotEscape, view.characters8(), view.length());
162 return encode(globalObject, doNotEscape, view.characters16(), view.length());
163 });
164}
165
166template <typename CharType>
167ALWAYS_INLINE
168static JSValue decode(JSGlobalObject* globalObject, const CharType* characters, int length, const Bitmap<256>& doNotUnescape, bool strict)
169{
170 VM& vm = globalObject->vm();
171 auto scope = DECLARE_THROW_SCOPE(vm);
172
173 StringBuilder builder(StringBuilder::OverflowHandler::RecordOverflow);
174 int k = 0;
175 UChar u = 0;
176 while (k < length) {
177 const CharType* p = characters + k;
178 CharType c = *p;
179 if (c == '%') {
180 int charLen = 0;
181 if (k <= length - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
182 const char b0 = Lexer<CharType>::convertHex(p[1], p[2]);
183 const int sequenceLen = 1 + U8_COUNT_TRAIL_BYTES(b0);
184 if (k <= length - sequenceLen * 3) {
185 charLen = sequenceLen * 3;
186#if U_ICU_VERSION_MAJOR_NUM >= 60
187 uint8_t sequence[U8_MAX_LENGTH];
188#else
189 // In pre-60 ICU, U8_COUNT_TRAIL_BYTES returns 0..5
190 uint8_t sequence[6];
191#endif
192 sequence[0] = b0;
193 for (int i = 1; i < sequenceLen; ++i) {
194 const CharType* q = p + i * 3;
195 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
196 sequence[i] = Lexer<CharType>::convertHex(q[1], q[2]);
197 else {
198 charLen = 0;
199 break;
200 }
201 }
202 if (charLen != 0) {
203 UChar32 character;
204 int32_t offset = 0;
205 U8_NEXT(sequence, offset, sequenceLen, character);
206 if (character < 0)
207 charLen = 0;
208 else if (!U_IS_BMP(character)) {
209 // Convert to surrogate pair.
210 ASSERT(U_IS_SUPPLEMENTARY(character));
211 builder.append(U16_LEAD(character));
212 u = U16_TRAIL(character);
213 } else {
214 ASSERT(!U_IS_SURROGATE(character));
215 u = static_cast<UChar>(character);
216 }
217 }
218 }
219 }
220 if (charLen == 0) {
221 if (strict)
222 return throwException(globalObject, scope, createURIError(globalObject, "URI error"_s));
223 // The only case where we don't use "strict" mode is the "unescape" function.
224 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
225 if (k <= length - 6 && p[1] == 'u'
226 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
227 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
228 charLen = 6;
229 u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]);
230 }
231 }
232 if (charLen && (u >= 128 || !doNotUnescape.get(static_cast<LChar>(u)))) {
233 builder.append(u);
234 k += charLen;
235 continue;
236 }
237 }
238 k++;
239 builder.append(c);
240 }
241 if (UNLIKELY(builder.hasOverflowed()))
242 return throwOutOfMemoryError(globalObject, scope);
243 RELEASE_AND_RETURN(scope, jsString(vm, builder.toString()));
244}
245
246static JSValue decode(JSGlobalObject* globalObject, JSValue argument, const Bitmap<256>& doNotUnescape, bool strict)
247{
248 return toStringView(globalObject, argument, [&] (StringView view) {
249 if (view.is8Bit())
250 return decode(globalObject, view.characters8(), view.length(), doNotUnescape, strict);
251 return decode(globalObject, view.characters16(), view.length(), doNotUnescape, strict);
252 });
253}
254
255static const int SizeOfInfinity = 8;
256
257template <typename CharType>
258static bool isInfinity(const CharType* data, const CharType* end)
259{
260 return (end - data) >= SizeOfInfinity
261 && data[0] == 'I'
262 && data[1] == 'n'
263 && data[2] == 'f'
264 && data[3] == 'i'
265 && data[4] == 'n'
266 && data[5] == 'i'
267 && data[6] == 't'
268 && data[7] == 'y';
269}
270
271// See ecma-262 6th 11.8.3
272template <typename CharType>
273static double jsBinaryIntegerLiteral(const CharType*& data, const CharType* end)
274{
275 // Binary number.
276 data += 2;
277 const CharType* firstDigitPosition = data;
278 double number = 0;
279 while (true) {
280 number = number * 2 + (*data - '0');
281 ++data;
282 if (data == end)
283 break;
284 if (!isASCIIBinaryDigit(*data))
285 break;
286 }
287 if (number >= mantissaOverflowLowerBound)
288 number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 2);
289
290 return number;
291}
292
293// See ecma-262 6th 11.8.3
294template <typename CharType>
295static double jsOctalIntegerLiteral(const CharType*& data, const CharType* end)
296{
297 // Octal number.
298 data += 2;
299 const CharType* firstDigitPosition = data;
300 double number = 0;
301 while (true) {
302 number = number * 8 + (*data - '0');
303 ++data;
304 if (data == end)
305 break;
306 if (!isASCIIOctalDigit(*data))
307 break;
308 }
309 if (number >= mantissaOverflowLowerBound)
310 number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 8);
311
312 return number;
313}
314
315// See ecma-262 6th 11.8.3
316template <typename CharType>
317static double jsHexIntegerLiteral(const CharType*& data, const CharType* end)
318{
319 // Hex number.
320 data += 2;
321 const CharType* firstDigitPosition = data;
322 double number = 0;
323 while (true) {
324 number = number * 16 + toASCIIHexValue(*data);
325 ++data;
326 if (data == end)
327 break;
328 if (!isASCIIHexDigit(*data))
329 break;
330 }
331 if (number >= mantissaOverflowLowerBound)
332 number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16);
333
334 return number;
335}
336
337// See ecma-262 6th 11.8.3
338template <typename CharType>
339static double jsStrDecimalLiteral(const CharType*& data, const CharType* end)
340{
341 RELEASE_ASSERT(data < end);
342
343 size_t parsedLength;
344 double number = parseDouble(data, end - data, parsedLength);
345 if (parsedLength) {
346 data += parsedLength;
347 return number;
348 }
349
350 // Check for [+-]?Infinity
351 switch (*data) {
352 case 'I':
353 if (isInfinity(data, end)) {
354 data += SizeOfInfinity;
355 return std::numeric_limits<double>::infinity();
356 }
357 break;
358
359 case '+':
360 if (isInfinity(data + 1, end)) {
361 data += SizeOfInfinity + 1;
362 return std::numeric_limits<double>::infinity();
363 }
364 break;
365
366 case '-':
367 if (isInfinity(data + 1, end)) {
368 data += SizeOfInfinity + 1;
369 return -std::numeric_limits<double>::infinity();
370 }
371 break;
372 }
373
374 // Not a number.
375 return PNaN;
376}
377
378template <typename CharType>
379static double toDouble(const CharType* characters, unsigned size)
380{
381 const CharType* endCharacters = characters + size;
382
383 // Skip leading white space.
384 for (; characters < endCharacters; ++characters) {
385 if (!isStrWhiteSpace(*characters))
386 break;
387 }
388
389 // Empty string.
390 if (characters == endCharacters)
391 return 0.0;
392
393 double number;
394 if (characters[0] == '0' && characters + 2 < endCharacters) {
395 if ((characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2]))
396 number = jsHexIntegerLiteral(characters, endCharacters);
397 else if ((characters[1] | 0x20) == 'o' && isASCIIOctalDigit(characters[2]))
398 number = jsOctalIntegerLiteral(characters, endCharacters);
399 else if ((characters[1] | 0x20) == 'b' && isASCIIBinaryDigit(characters[2]))
400 number = jsBinaryIntegerLiteral(characters, endCharacters);
401 else
402 number = jsStrDecimalLiteral(characters, endCharacters);
403 } else
404 number = jsStrDecimalLiteral(characters, endCharacters);
405
406 // Allow trailing white space.
407 for (; characters < endCharacters; ++characters) {
408 if (!isStrWhiteSpace(*characters))
409 break;
410 }
411 if (characters != endCharacters)
412 return PNaN;
413
414 return number;
415}
416
417// See ecma-262 6th 11.8.3
418double jsToNumber(StringView s)
419{
420 unsigned size = s.length();
421
422 if (size == 1) {
423 UChar c = s[0];
424 if (isASCIIDigit(c))
425 return c - '0';
426 if (isStrWhiteSpace(c))
427 return 0;
428 return PNaN;
429 }
430
431 if (s.is8Bit())
432 return toDouble(s.characters8(), size);
433 return toDouble(s.characters16(), size);
434}
435
436static double parseFloat(StringView s)
437{
438 unsigned size = s.length();
439
440 if (size == 1) {
441 UChar c = s[0];
442 if (isASCIIDigit(c))
443 return c - '0';
444 return PNaN;
445 }
446
447 if (s.is8Bit()) {
448 const LChar* data = s.characters8();
449 const LChar* end = data + size;
450
451 // Skip leading white space.
452 for (; data < end; ++data) {
453 if (!isStrWhiteSpace(*data))
454 break;
455 }
456
457 // Empty string.
458 if (data == end)
459 return PNaN;
460
461 return jsStrDecimalLiteral(data, end);
462 }
463
464 const UChar* data = s.characters16();
465 const UChar* end = data + size;
466
467 // Skip leading white space.
468 for (; data < end; ++data) {
469 if (!isStrWhiteSpace(*data))
470 break;
471 }
472
473 // Empty string.
474 if (data == end)
475 return PNaN;
476
477 return jsStrDecimalLiteral(data, end);
478}
479
480EncodedJSValue JSC_HOST_CALL globalFuncEval(JSGlobalObject* globalObject, CallFrame* callFrame)
481{
482 VM& vm = globalObject->vm();
483 auto scope = DECLARE_THROW_SCOPE(vm);
484
485 JSValue x = callFrame->argument(0);
486 if (!x.isString())
487 return JSValue::encode(x);
488
489 if (!globalObject->evalEnabled()) {
490 throwException(globalObject, scope, createEvalError(globalObject, globalObject->evalDisabledErrorMessage()));
491 return JSValue::encode(jsUndefined());
492 }
493
494 String s = asString(x)->value(globalObject);
495 RETURN_IF_EXCEPTION(scope, encodedJSValue());
496
497 JSValue parsedObject;
498 if (s.is8Bit()) {
499 LiteralParser<LChar> preparser(globalObject, s.characters8(), s.length(), NonStrictJSON, nullptr);
500 parsedObject = preparser.tryLiteralParse();
501 } else {
502 LiteralParser<UChar> preparser(globalObject, s.characters16(), s.length(), NonStrictJSON, nullptr);
503 parsedObject = preparser.tryLiteralParse();
504 }
505 RETURN_IF_EXCEPTION(scope, encodedJSValue());
506 if (parsedObject)
507 return JSValue::encode(parsedObject);
508
509 SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
510 EvalExecutable* eval = IndirectEvalExecutable::create(globalObject, makeSource(s, sourceOrigin), false, DerivedContextType::None, false, EvalContextType::None);
511 EXCEPTION_ASSERT(!!scope.exception() == !eval);
512 if (!eval)
513 return encodedJSValue();
514
515 RELEASE_AND_RETURN(scope, JSValue::encode(vm.interpreter->execute(eval, globalObject, globalObject->globalThis(), globalObject->globalScope())));
516}
517
518EncodedJSValue JSC_HOST_CALL globalFuncParseInt(JSGlobalObject* globalObject, CallFrame* callFrame)
519{
520 JSValue value = callFrame->argument(0);
521 JSValue radixValue = callFrame->argument(1);
522
523 // Optimized handling for numbers:
524 // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt
525 // results in a truncation to integer. In the case of -0, this is converted to 0.
526 //
527 // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21,
528 // however these values cannot be trivially truncated to int since 10^21 exceeds
529 // even the int64_t range. Negative numbers are a little trickier, the case for
530 // values in the range -10^21 < n <= -1 are similar to those for integer, but
531 // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0.
532 static const double tenToTheMinus6 = 0.000001;
533 static const double intMaxPlusOne = 2147483648.0;
534 if (value.isNumber()) {
535 double n = value.asNumber();
536 if (((n < intMaxPlusOne && n >= tenToTheMinus6) || !n) && radixValue.isUndefinedOrNull())
537 return JSValue::encode(jsNumber(static_cast<int32_t>(n)));
538 }
539
540 // If ToString throws, we shouldn't call ToInt32.
541 return toStringView(globalObject, value, [&] (StringView view) {
542 return JSValue::encode(jsNumber(parseInt(view, radixValue.toInt32(globalObject))));
543 });
544}
545
546EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(JSGlobalObject* globalObject, CallFrame* callFrame)
547{
548 auto viewWithString = callFrame->argument(0).toString(globalObject)->viewWithUnderlyingString(globalObject);
549 return JSValue::encode(jsNumber(parseFloat(viewWithString.view)));
550}
551
552EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(JSGlobalObject* globalObject, CallFrame* callFrame)
553{
554 static Bitmap<256> doNotUnescapeWhenDecodingURI = makeCharacterBitmap(
555 "#$&+,/:;=?@"
556 );
557
558 return JSValue::encode(decode(globalObject, callFrame->argument(0), doNotUnescapeWhenDecodingURI, true));
559}
560
561EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(JSGlobalObject* globalObject, CallFrame* callFrame)
562{
563 static Bitmap<256> emptyBitmap;
564 return JSValue::encode(decode(globalObject, callFrame->argument(0), emptyBitmap, true));
565}
566
567EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(JSGlobalObject* globalObject, CallFrame* callFrame)
568{
569 static Bitmap<256> doNotEscapeWhenEncodingURI = makeCharacterBitmap(
570 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
571 "abcdefghijklmnopqrstuvwxyz"
572 "0123456789"
573 "!#$&'()*+,-./:;=?@_~"
574 );
575
576 return JSValue::encode(encode(globalObject, callFrame->argument(0), doNotEscapeWhenEncodingURI));
577}
578
579EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(JSGlobalObject* globalObject, CallFrame* callFrame)
580{
581 static Bitmap<256> doNotEscapeWhenEncodingURIComponent = makeCharacterBitmap(
582 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
583 "abcdefghijklmnopqrstuvwxyz"
584 "0123456789"
585 "!'()*-._~"
586 );
587
588 return JSValue::encode(encode(globalObject, callFrame->argument(0), doNotEscapeWhenEncodingURIComponent));
589}
590
591EncodedJSValue JSC_HOST_CALL globalFuncEscape(JSGlobalObject* globalObject, CallFrame* callFrame)
592{
593 static Bitmap<256> doNotEscape = makeCharacterBitmap(
594 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
595 "abcdefghijklmnopqrstuvwxyz"
596 "0123456789"
597 "*+-./@_"
598 );
599
600 return JSValue::encode(toStringView(globalObject, callFrame->argument(0), [&] (StringView view) {
601 VM& vm = globalObject->vm();
602 StringBuilder builder;
603 if (view.is8Bit()) {
604 const LChar* c = view.characters8();
605 for (unsigned k = 0; k < view.length(); k++, c++) {
606 int u = c[0];
607 if (doNotEscape.get(static_cast<LChar>(u)))
608 builder.append(*c);
609 else {
610 builder.append('%');
611 appendByteAsHex(u, builder);
612 }
613 }
614 return jsString(vm, builder.toString());
615 }
616
617 const UChar* c = view.characters16();
618 for (unsigned k = 0; k < view.length(); k++, c++) {
619 UChar u = c[0];
620 if (u >= doNotEscape.size()) {
621 builder.appendLiteral("%u");
622 appendByteAsHex(u >> 8, builder);
623 appendByteAsHex(u & 0xFF, builder);
624 } else if (doNotEscape.get(static_cast<LChar>(u)))
625 builder.append(*c);
626 else {
627 builder.append('%');
628 appendByteAsHex(u, builder);
629 }
630 }
631
632 return jsString(vm, builder.toString());
633 }));
634}
635
636EncodedJSValue JSC_HOST_CALL globalFuncUnescape(JSGlobalObject* globalObject, CallFrame* callFrame)
637{
638 return JSValue::encode(toStringView(globalObject, callFrame->argument(0), [&] (StringView view) {
639 // We use int for k and length intentionally since we would like to evaluate
640 // the condition `k <= length -6` even if length is less than 6.
641 int k = 0;
642 int length = view.length();
643
644 StringBuilder builder;
645 builder.reserveCapacity(length);
646
647 if (view.is8Bit()) {
648 const LChar* characters = view.characters8();
649 LChar convertedLChar;
650 while (k < length) {
651 const LChar* c = characters + k;
652 if (c[0] == '%' && k <= length - 6 && c[1] == 'u') {
653 if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
654 builder.append(Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]));
655 k += 6;
656 continue;
657 }
658 } else if (c[0] == '%' && k <= length - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
659 convertedLChar = LChar(Lexer<LChar>::convertHex(c[1], c[2]));
660 c = &convertedLChar;
661 k += 2;
662 }
663 builder.append(*c);
664 k++;
665 }
666 } else {
667 const UChar* characters = view.characters16();
668
669 while (k < length) {
670 const UChar* c = characters + k;
671 UChar convertedUChar;
672 if (c[0] == '%' && k <= length - 6 && c[1] == 'u') {
673 if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
674 convertedUChar = Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]);
675 c = &convertedUChar;
676 k += 5;
677 }
678 } else if (c[0] == '%' && k <= length - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
679 convertedUChar = UChar(Lexer<UChar>::convertHex(c[1], c[2]));
680 c = &convertedUChar;
681 k += 2;
682 }
683 k++;
684 builder.append(*c);
685 }
686 }
687
688 return jsString(globalObject->vm(), builder.toString());
689 }));
690}
691
692EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(JSGlobalObject* globalObject, CallFrame*)
693{
694 VM& vm = globalObject->vm();
695 auto scope = DECLARE_THROW_SCOPE(vm);
696 return throwVMTypeError(globalObject, scope);
697}
698
699EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeErrorArgumentsCalleeAndCaller(JSGlobalObject* globalObject, CallFrame*)
700{
701 VM& vm = globalObject->vm();
702 auto scope = DECLARE_THROW_SCOPE(vm);
703 return throwVMTypeError(globalObject, scope, "'arguments', 'callee', and 'caller' cannot be accessed in this context.");
704}
705
706EncodedJSValue JSC_HOST_CALL globalFuncMakeTypeError(JSGlobalObject* globalObject, CallFrame* callFrame)
707{
708 Structure* errorStructure = globalObject->errorStructure(ErrorType::TypeError);
709 return JSValue::encode(ErrorInstance::create(globalObject, errorStructure, callFrame->argument(0), nullptr, TypeNothing, false));
710}
711
712EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(JSGlobalObject* globalObject, CallFrame* callFrame)
713{
714 VM& vm = globalObject->vm();
715 auto scope = DECLARE_THROW_SCOPE(vm);
716
717 JSValue thisValue = callFrame->thisValue().toThis(globalObject, StrictMode);
718 if (thisValue.isUndefinedOrNull())
719 return throwVMError(globalObject, scope, createNotAnObjectError(globalObject, thisValue));
720
721 JSObject* thisObject = jsDynamicCast<JSObject*>(vm, thisValue);
722 if (!thisObject) {
723 JSObject* prototype = thisValue.synthesizePrototype(globalObject);
724 EXCEPTION_ASSERT(!!scope.exception() == !prototype);
725 if (UNLIKELY(!prototype))
726 return JSValue::encode(JSValue());
727 return JSValue::encode(prototype);
728 }
729
730 RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->getPrototype(vm, globalObject)));
731}
732
733EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(JSGlobalObject* globalObject, CallFrame* callFrame)
734{
735 VM& vm = globalObject->vm();
736 auto scope = DECLARE_THROW_SCOPE(vm);
737
738 JSValue thisValue = callFrame->thisValue().toThis(globalObject, StrictMode);
739 if (thisValue.isUndefinedOrNull())
740 return throwVMTypeError(globalObject, scope, ObjectProtoCalledOnNullOrUndefinedError);
741
742 JSValue value = callFrame->argument(0);
743
744 JSObject* thisObject = jsDynamicCast<JSObject*>(vm, thisValue);
745
746 // Setting __proto__ of a primitive should have no effect.
747 if (!thisObject)
748 return JSValue::encode(jsUndefined());
749
750 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
751 if (!value.isObject() && !value.isNull())
752 return JSValue::encode(jsUndefined());
753
754 scope.release();
755 bool shouldThrowIfCantSet = true;
756 thisObject->setPrototype(vm, globalObject, value, shouldThrowIfCantSet);
757 return JSValue::encode(jsUndefined());
758}
759
760EncodedJSValue JSC_HOST_CALL globalFuncHostPromiseRejectionTracker(JSGlobalObject* globalObject, CallFrame* callFrame)
761{
762 VM& vm = globalObject->vm();
763 auto scope = DECLARE_THROW_SCOPE(vm);
764
765 JSPromise* promise = jsCast<JSPromise*>(callFrame->argument(0));
766
767 // InternalPromises should not be exposed to user scripts.
768 if (jsDynamicCast<JSInternalPromise*>(vm, promise))
769 return JSValue::encode(jsUndefined());
770
771 JSValue operationValue = callFrame->argument(1);
772
773 ASSERT(operationValue.isNumber());
774 auto operation = static_cast<JSPromiseRejectionOperation>(operationValue.toUInt32(globalObject));
775 ASSERT(operation == JSPromiseRejectionOperation::Reject || operation == JSPromiseRejectionOperation::Handle);
776 scope.assertNoException();
777
778 if (globalObject->globalObjectMethodTable()->promiseRejectionTracker)
779 globalObject->globalObjectMethodTable()->promiseRejectionTracker(globalObject, promise, operation);
780 else {
781 switch (operation) {
782 case JSPromiseRejectionOperation::Reject:
783 vm.promiseRejected(promise);
784 break;
785 case JSPromiseRejectionOperation::Handle:
786 // do nothing
787 break;
788 }
789 }
790 RETURN_IF_EXCEPTION(scope, { });
791
792 return JSValue::encode(jsUndefined());
793}
794
795EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(JSGlobalObject* globalObject, CallFrame* callFrame)
796{
797 dataLog(callFrame->argument(0).toWTFString(globalObject), "\n");
798 return JSValue::encode(jsUndefined());
799}
800
801EncodedJSValue JSC_HOST_CALL globalFuncBuiltinDescribe(JSGlobalObject* globalObject, CallFrame* callFrame)
802{
803 return JSValue::encode(jsString(globalObject->vm(), toString(callFrame->argument(0))));
804}
805
806EncodedJSValue JSC_HOST_CALL globalFuncImportModule(JSGlobalObject* globalObject, CallFrame* callFrame)
807{
808 VM& vm = globalObject->vm();
809
810 auto* promise = JSPromise::create(vm, globalObject->promiseStructure());
811
812 auto catchScope = DECLARE_CATCH_SCOPE(vm);
813 auto reject = [&] (JSValue rejectionReason) {
814 catchScope.clearException();
815 promise->reject(globalObject, rejectionReason);
816 catchScope.clearException();
817 return JSValue::encode(promise);
818 };
819
820 auto sourceOrigin = callFrame->callerSourceOrigin(vm);
821 RELEASE_ASSERT(callFrame->argumentCount() == 1);
822 auto* specifier = callFrame->uncheckedArgument(0).toString(globalObject);
823 if (Exception* exception = catchScope.exception())
824 return reject(exception->value());
825
826 // We always specify parameters as undefined. Once dynamic import() starts accepting fetching parameters,
827 // we should retrieve this from the arguments.
828 JSValue parameters = jsUndefined();
829 auto* internalPromise = globalObject->moduleLoader()->importModule(globalObject, specifier, parameters, sourceOrigin);
830 if (Exception* exception = catchScope.exception())
831 return reject(exception->value());
832 promise->resolve(globalObject, internalPromise);
833
834 catchScope.clearException();
835 return JSValue::encode(promise);
836}
837
838EncodedJSValue JSC_HOST_CALL globalFuncPropertyIsEnumerable(JSGlobalObject* globalObject, CallFrame* callFrame)
839{
840 VM& vm = globalObject->vm();
841 auto scope = DECLARE_THROW_SCOPE(vm);
842
843 RELEASE_ASSERT(callFrame->argumentCount() == 2);
844 JSObject* object = jsCast<JSObject*>(callFrame->uncheckedArgument(0));
845 auto propertyName = callFrame->uncheckedArgument(1).toPropertyKey(globalObject);
846 RETURN_IF_EXCEPTION(scope, encodedJSValue());
847
848 scope.release();
849 PropertyDescriptor descriptor;
850 bool enumerable = object->getOwnPropertyDescriptor(globalObject, propertyName, descriptor) && descriptor.enumerable();
851 return JSValue::encode(jsBoolean(enumerable));
852}
853
854EncodedJSValue JSC_HOST_CALL globalFuncOwnKeys(JSGlobalObject* globalObject, CallFrame* callFrame)
855{
856 VM& vm = globalObject->vm();
857 auto scope = DECLARE_THROW_SCOPE(vm);
858 JSObject* object = callFrame->argument(0).toObject(globalObject);
859 RETURN_IF_EXCEPTION(scope, encodedJSValue());
860 RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include)));
861}
862
863#if ENABLE(INTL)
864EncodedJSValue JSC_HOST_CALL globalFuncDateTimeFormat(JSGlobalObject* globalObject, CallFrame* callFrame)
865{
866 VM& vm = globalObject->vm();
867 auto scope = DECLARE_THROW_SCOPE(vm);
868
869 IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(vm, globalObject->dateTimeFormatStructure());
870 dateTimeFormat->initializeDateTimeFormat(globalObject, callFrame->argument(0), callFrame->argument(1));
871 RETURN_IF_EXCEPTION(scope, encodedJSValue());
872 double value = callFrame->argument(2).toNumber(globalObject);
873 RETURN_IF_EXCEPTION(scope, encodedJSValue());
874 RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->format(globalObject, value)));
875}
876#endif
877
878} // namespace JSC
879