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