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