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