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