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