]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSGlobalObjectFunctions.cpp
JavaScriptCore-7600.1.4.11.8.tar.gz
[apple/javascriptcore.git] / runtime / 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, 2004, 2005, 2006, 2007, 2008, 2009, 2012 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 "Interpreter.h"
30 #include "JSFunction.h"
31 #include "JSGlobalObject.h"
32 #include "JSString.h"
33 #include "JSStringBuilder.h"
34 #include "Lexer.h"
35 #include "LiteralParser.h"
36 #include "Nodes.h"
37 #include "JSCInlines.h"
38 #include "Parser.h"
39 #include "StackVisitor.h"
40 #include <wtf/dtoa.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <wtf/ASCIICType.h>
44 #include <wtf/Assertions.h>
45 #include <wtf/MathExtras.h>
46 #include <wtf/StringExtras.h>
47 #include <wtf/text/StringBuilder.h>
48 #include <wtf/unicode/UTF8.h>
49
50 using namespace WTF;
51 using namespace Unicode;
52
53 namespace JSC {
54
55 static JSValue encode(ExecState* exec, const char* doNotEscape)
56 {
57 CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(StrictConversion);
58 if (!cstr.data())
59 return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("String contained an illegal UTF-16 sequence.")));
60
61 JSStringBuilder builder;
62 const char* p = cstr.data();
63 for (size_t k = 0; k < cstr.length(); k++, p++) {
64 char c = *p;
65 if (c && strchr(doNotEscape, c))
66 builder.append(static_cast<LChar>(c));
67 else {
68 char tmp[4];
69 snprintf(tmp, sizeof(tmp), "%%%02X", static_cast<unsigned char>(c));
70 builder.append(tmp);
71 }
72 }
73 return builder.build(exec);
74 }
75
76 template <typename CharType>
77 ALWAYS_INLINE
78 static JSValue decode(ExecState* exec, const CharType* characters, int length, const char* doNotUnescape, bool strict)
79 {
80 JSStringBuilder builder;
81 int k = 0;
82 UChar u = 0;
83 while (k < length) {
84 const CharType* p = characters + k;
85 CharType c = *p;
86 if (c == '%') {
87 int charLen = 0;
88 if (k <= length - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
89 const char b0 = Lexer<CharType>::convertHex(p[1], p[2]);
90 const int sequenceLen = UTF8SequenceLength(b0);
91 if (sequenceLen && k <= length - sequenceLen * 3) {
92 charLen = sequenceLen * 3;
93 char sequence[5];
94 sequence[0] = b0;
95 for (int i = 1; i < sequenceLen; ++i) {
96 const CharType* q = p + i * 3;
97 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
98 sequence[i] = Lexer<CharType>::convertHex(q[1], q[2]);
99 else {
100 charLen = 0;
101 break;
102 }
103 }
104 if (charLen != 0) {
105 sequence[sequenceLen] = 0;
106 const int character = decodeUTF8Sequence(sequence);
107 if (character < 0 || character >= 0x110000)
108 charLen = 0;
109 else if (character >= 0x10000) {
110 // Convert to surrogate pair.
111 builder.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
112 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
113 } else
114 u = static_cast<UChar>(character);
115 }
116 }
117 }
118 if (charLen == 0) {
119 if (strict)
120 return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("URI error")));
121 // The only case where we don't use "strict" mode is the "unescape" function.
122 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
123 if (k <= length - 6 && p[1] == 'u'
124 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
125 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
126 charLen = 6;
127 u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]);
128 }
129 }
130 if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
131 builder.append(u);
132 k += charLen;
133 continue;
134 }
135 }
136 k++;
137 builder.append(c);
138 }
139 return builder.build(exec);
140 }
141
142 static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict)
143 {
144 String str = exec->argument(0).toString(exec)->value(exec);
145
146 if (str.is8Bit())
147 return decode(exec, str.characters8(), str.length(), doNotUnescape, strict);
148 return decode(exec, str.characters16(), str.length(), doNotUnescape, strict);
149 }
150
151 bool isStrWhiteSpace(UChar c)
152 {
153 switch (c) {
154 // ECMA-262-5th 7.2 & 7.3
155 case 0x0009:
156 case 0x000A:
157 case 0x000B:
158 case 0x000C:
159 case 0x000D:
160 case 0x0020:
161 case 0x00A0:
162 case 0x180E: // This character used to be in Zs category before Unicode 6.3, and EcmaScript says that we should keep treating it as such.
163 case 0x2028:
164 case 0x2029:
165 case 0xFEFF:
166 return true;
167 default:
168 return c > 0xFF && u_charType(c) == U_SPACE_SEPARATOR;
169 }
170 }
171
172 static int parseDigit(unsigned short c, int radix)
173 {
174 int digit = -1;
175
176 if (c >= '0' && c <= '9')
177 digit = c - '0';
178 else if (c >= 'A' && c <= 'Z')
179 digit = c - 'A' + 10;
180 else if (c >= 'a' && c <= 'z')
181 digit = c - 'a' + 10;
182
183 if (digit >= radix)
184 return -1;
185 return digit;
186 }
187
188 double parseIntOverflow(const LChar* s, unsigned length, int radix)
189 {
190 double number = 0.0;
191 double radixMultiplier = 1.0;
192
193 for (const LChar* p = s + length - 1; p >= s; p--) {
194 if (radixMultiplier == std::numeric_limits<double>::infinity()) {
195 if (*p != '0') {
196 number = std::numeric_limits<double>::infinity();
197 break;
198 }
199 } else {
200 int digit = parseDigit(*p, radix);
201 number += digit * radixMultiplier;
202 }
203
204 radixMultiplier *= radix;
205 }
206
207 return number;
208 }
209
210 static double parseIntOverflow(const UChar* s, unsigned length, int radix)
211 {
212 double number = 0.0;
213 double radixMultiplier = 1.0;
214
215 for (const UChar* p = s + length - 1; p >= s; p--) {
216 if (radixMultiplier == std::numeric_limits<double>::infinity()) {
217 if (*p != '0') {
218 number = std::numeric_limits<double>::infinity();
219 break;
220 }
221 } else {
222 int digit = parseDigit(*p, radix);
223 number += digit * radixMultiplier;
224 }
225
226 radixMultiplier *= radix;
227 }
228
229 return number;
230 }
231
232 static double parseIntOverflow(StringView string, int radix)
233 {
234 if (string.is8Bit())
235 return parseIntOverflow(string.characters8(), string.length(), radix);
236 return parseIntOverflow(string.characters16(), string.length(), radix);
237 }
238
239 // ES5.1 15.1.2.2
240 template <typename CharType>
241 ALWAYS_INLINE
242 static double parseInt(const String& s, const CharType* data, int radix)
243 {
244 // 1. Let inputString be ToString(string).
245 // 2. Let S be a newly created substring of inputString consisting of the first character that is not a
246 // StrWhiteSpaceChar and all characters following that character. (In other words, remove leading white
247 // space.) If inputString does not contain any such characters, let S be the empty string.
248 int length = s.length();
249 int p = 0;
250 while (p < length && isStrWhiteSpace(data[p]))
251 ++p;
252
253 // 3. Let sign be 1.
254 // 4. If S is not empty and the first character of S is a minus sign -, let sign be -1.
255 // 5. If S is not empty and the first character of S is a plus sign + or a minus sign -, then remove the first character from S.
256 double sign = 1;
257 if (p < length) {
258 if (data[p] == '+')
259 ++p;
260 else if (data[p] == '-') {
261 sign = -1;
262 ++p;
263 }
264 }
265
266 // 6. Let R = ToInt32(radix).
267 // 7. Let stripPrefix be true.
268 // 8. If R != 0,then
269 // b. If R != 16, let stripPrefix be false.
270 // 9. Else, R == 0
271 // a. LetR = 10.
272 // 10. If stripPrefix is true, then
273 // a. If the length of S is at least 2 and the first two characters of S are either ―0x or ―0X,
274 // then remove the first two characters from S and let R = 16.
275 // 11. If S contains any character that is not a radix-R digit, then let Z be the substring of S
276 // consisting of all characters before the first such character; otherwise, let Z be S.
277 if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) {
278 radix = 16;
279 p += 2;
280 } else if (radix == 0)
281 radix = 10;
282
283 // 8.a If R < 2 or R > 36, then return NaN.
284 if (radix < 2 || radix > 36)
285 return PNaN;
286
287 // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters
288 // A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant
289 // digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation;
290 // and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the
291 // mathematical integer value that is represented by Z in radix-R notation.)
292 // 14. Let number be the Number value for mathInt.
293 int firstDigitPosition = p;
294 bool sawDigit = false;
295 double number = 0;
296 while (p < length) {
297 int digit = parseDigit(data[p], radix);
298 if (digit == -1)
299 break;
300 sawDigit = true;
301 number *= radix;
302 number += digit;
303 ++p;
304 }
305
306 // 12. If Z is empty, return NaN.
307 if (!sawDigit)
308 return PNaN;
309
310 // Alternate code path for certain large numbers.
311 if (number >= mantissaOverflowLowerBound) {
312 if (radix == 10) {
313 size_t parsedLength;
314 number = parseDouble(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), parsedLength);
315 } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
316 number = parseIntOverflow(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), radix);
317 }
318
319 // 15. Return sign x number.
320 return sign * number;
321 }
322
323 static double parseInt(const String& s, int radix)
324 {
325 if (s.is8Bit())
326 return parseInt(s, s.characters8(), radix);
327 return parseInt(s, s.characters16(), radix);
328 }
329
330 static const int SizeOfInfinity = 8;
331
332 template <typename CharType>
333 static bool isInfinity(const CharType* data, const CharType* end)
334 {
335 return (end - data) >= SizeOfInfinity
336 && data[0] == 'I'
337 && data[1] == 'n'
338 && data[2] == 'f'
339 && data[3] == 'i'
340 && data[4] == 'n'
341 && data[5] == 'i'
342 && data[6] == 't'
343 && data[7] == 'y';
344 }
345
346 // See ecma-262 9.3.1
347 template <typename CharType>
348 static double jsHexIntegerLiteral(const CharType*& data, const CharType* end)
349 {
350 // Hex number.
351 data += 2;
352 const CharType* firstDigitPosition = data;
353 double number = 0;
354 while (true) {
355 number = number * 16 + toASCIIHexValue(*data);
356 ++data;
357 if (data == end)
358 break;
359 if (!isASCIIHexDigit(*data))
360 break;
361 }
362 if (number >= mantissaOverflowLowerBound)
363 number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16);
364
365 return number;
366 }
367
368 // See ecma-262 9.3.1
369 template <typename CharType>
370 static double jsStrDecimalLiteral(const CharType*& data, const CharType* end)
371 {
372 RELEASE_ASSERT(data < end);
373
374 size_t parsedLength;
375 double number = parseDouble(data, end - data, parsedLength);
376 if (parsedLength) {
377 data += parsedLength;
378 return number;
379 }
380
381 // Check for [+-]?Infinity
382 switch (*data) {
383 case 'I':
384 if (isInfinity(data, end)) {
385 data += SizeOfInfinity;
386 return std::numeric_limits<double>::infinity();
387 }
388 break;
389
390 case '+':
391 if (isInfinity(data + 1, end)) {
392 data += SizeOfInfinity + 1;
393 return std::numeric_limits<double>::infinity();
394 }
395 break;
396
397 case '-':
398 if (isInfinity(data + 1, end)) {
399 data += SizeOfInfinity + 1;
400 return -std::numeric_limits<double>::infinity();
401 }
402 break;
403 }
404
405 // Not a number.
406 return PNaN;
407 }
408
409 template <typename CharType>
410 static double toDouble(const CharType* characters, unsigned size)
411 {
412 const CharType* endCharacters = characters + size;
413
414 // Skip leading white space.
415 for (; characters < endCharacters; ++characters) {
416 if (!isStrWhiteSpace(*characters))
417 break;
418 }
419
420 // Empty string.
421 if (characters == endCharacters)
422 return 0.0;
423
424 double number;
425 if (characters[0] == '0' && characters + 2 < endCharacters && (characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2]))
426 number = jsHexIntegerLiteral(characters, endCharacters);
427 else
428 number = jsStrDecimalLiteral(characters, endCharacters);
429
430 // Allow trailing white space.
431 for (; characters < endCharacters; ++characters) {
432 if (!isStrWhiteSpace(*characters))
433 break;
434 }
435 if (characters != endCharacters)
436 return PNaN;
437
438 return number;
439 }
440
441 // See ecma-262 9.3.1
442 double jsToNumber(const String& s)
443 {
444 unsigned size = s.length();
445
446 if (size == 1) {
447 UChar c = s[0];
448 if (isASCIIDigit(c))
449 return c - '0';
450 if (isStrWhiteSpace(c))
451 return 0;
452 return PNaN;
453 }
454
455 if (s.is8Bit())
456 return toDouble(s.characters8(), size);
457 return toDouble(s.characters16(), size);
458 }
459
460 static double parseFloat(const String& s)
461 {
462 unsigned size = s.length();
463
464 if (size == 1) {
465 UChar c = s[0];
466 if (isASCIIDigit(c))
467 return c - '0';
468 return PNaN;
469 }
470
471 if (s.is8Bit()) {
472 const LChar* data = s.characters8();
473 const LChar* end = data + size;
474
475 // Skip leading white space.
476 for (; data < end; ++data) {
477 if (!isStrWhiteSpace(*data))
478 break;
479 }
480
481 // Empty string.
482 if (data == end)
483 return PNaN;
484
485 return jsStrDecimalLiteral(data, end);
486 }
487
488 const UChar* data = s.characters16();
489 const UChar* end = data + size;
490
491 // Skip leading white space.
492 for (; data < end; ++data) {
493 if (!isStrWhiteSpace(*data))
494 break;
495 }
496
497 // Empty string.
498 if (data == end)
499 return PNaN;
500
501 return jsStrDecimalLiteral(data, end);
502 }
503
504 EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec)
505 {
506 JSValue x = exec->argument(0);
507 if (!x.isString())
508 return JSValue::encode(x);
509
510 String s = x.toString(exec)->value(exec);
511
512 if (s.is8Bit()) {
513 LiteralParser<LChar> preparser(exec, s.characters8(), s.length(), NonStrictJSON);
514 if (JSValue parsedObject = preparser.tryLiteralParse())
515 return JSValue::encode(parsedObject);
516 } else {
517 LiteralParser<UChar> preparser(exec, s.characters16(), s.length(), NonStrictJSON);
518 if (JSValue parsedObject = preparser.tryLiteralParse())
519 return JSValue::encode(parsedObject);
520 }
521
522 JSGlobalObject* calleeGlobalObject = exec->callee()->globalObject();
523 EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false);
524 if (!eval)
525 return JSValue::encode(jsUndefined());
526
527 return JSValue::encode(exec->interpreter()->execute(eval, exec, calleeGlobalObject->globalThis(), calleeGlobalObject));
528 }
529
530 EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec)
531 {
532 JSValue value = exec->argument(0);
533 JSValue radixValue = exec->argument(1);
534
535 // Optimized handling for numbers:
536 // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt
537 // results in a truncation to integer. In the case of -0, this is converted to 0.
538 //
539 // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21,
540 // however these values cannot be trivially truncated to int since 10^21 exceeds
541 // even the int64_t range. Negative numbers are a little trickier, the case for
542 // values in the range -10^21 < n <= -1 are similar to those for integer, but
543 // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0.
544 static const double tenToTheMinus6 = 0.000001;
545 static const double intMaxPlusOne = 2147483648.0;
546 if (value.isNumber()) {
547 double n = value.asNumber();
548 if (((n < intMaxPlusOne && n >= tenToTheMinus6) || !n) && radixValue.isUndefinedOrNull())
549 return JSValue::encode(jsNumber(static_cast<int32_t>(n)));
550 }
551
552 // If ToString throws, we shouldn't call ToInt32.
553 String s = value.toString(exec)->value(exec);
554 if (exec->hadException())
555 return JSValue::encode(jsUndefined());
556
557 return JSValue::encode(jsNumber(parseInt(s, radixValue.toInt32(exec))));
558 }
559
560 EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec)
561 {
562 return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->value(exec))));
563 }
564
565 EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec)
566 {
567 return JSValue::encode(jsBoolean(std::isnan(exec->argument(0).toNumber(exec))));
568 }
569
570 EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec)
571 {
572 double n = exec->argument(0).toNumber(exec);
573 return JSValue::encode(jsBoolean(std::isfinite(n)));
574 }
575
576 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec)
577 {
578 static const char do_not_unescape_when_decoding_URI[] =
579 "#$&+,/:;=?@";
580
581 return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true));
582 }
583
584 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec)
585 {
586 return JSValue::encode(decode(exec, "", true));
587 }
588
589 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec)
590 {
591 static const char do_not_escape_when_encoding_URI[] =
592 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
593 "abcdefghijklmnopqrstuvwxyz"
594 "0123456789"
595 "!#$&'()*+,-./:;=?@_~";
596
597 return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI));
598 }
599
600 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec)
601 {
602 static const char do_not_escape_when_encoding_URI_component[] =
603 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
604 "abcdefghijklmnopqrstuvwxyz"
605 "0123456789"
606 "!'()*-._~";
607
608 return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component));
609 }
610
611 EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
612 {
613 static const char do_not_escape[] =
614 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
615 "abcdefghijklmnopqrstuvwxyz"
616 "0123456789"
617 "*+-./@_";
618
619 JSStringBuilder builder;
620 String str = exec->argument(0).toString(exec)->value(exec);
621 if (str.is8Bit()) {
622 const LChar* c = str.characters8();
623 for (unsigned k = 0; k < str.length(); k++, c++) {
624 int u = c[0];
625 if (u && strchr(do_not_escape, static_cast<char>(u)))
626 builder.append(*c);
627 else {
628 char tmp[4];
629 snprintf(tmp, sizeof(tmp), "%%%02X", u);
630 builder.append(tmp);
631 }
632 }
633
634 return JSValue::encode(builder.build(exec));
635 }
636
637 const UChar* c = str.characters16();
638 for (unsigned k = 0; k < str.length(); k++, c++) {
639 int u = c[0];
640 if (u > 255) {
641 char tmp[7];
642 snprintf(tmp, sizeof(tmp), "%%u%04X", u);
643 builder.append(tmp);
644 } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
645 builder.append(*c);
646 else {
647 char tmp[4];
648 snprintf(tmp, sizeof(tmp), "%%%02X", u);
649 builder.append(tmp);
650 }
651 }
652
653 return JSValue::encode(builder.build(exec));
654 }
655
656 EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec)
657 {
658 StringBuilder builder;
659 String str = exec->argument(0).toString(exec)->value(exec);
660 int k = 0;
661 int len = str.length();
662
663 if (str.is8Bit()) {
664 const LChar* characters = str.characters8();
665 LChar convertedLChar;
666 while (k < len) {
667 const LChar* c = characters + k;
668 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
669 if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
670 builder.append(Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]));
671 k += 6;
672 continue;
673 }
674 } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
675 convertedLChar = LChar(Lexer<LChar>::convertHex(c[1], c[2]));
676 c = &convertedLChar;
677 k += 2;
678 }
679 builder.append(*c);
680 k++;
681 }
682 } else {
683 const UChar* characters = str.characters16();
684
685 while (k < len) {
686 const UChar* c = characters + k;
687 UChar convertedUChar;
688 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
689 if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
690 convertedUChar = Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]);
691 c = &convertedUChar;
692 k += 5;
693 }
694 } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
695 convertedUChar = UChar(Lexer<UChar>::convertHex(c[1], c[2]));
696 c = &convertedUChar;
697 k += 2;
698 }
699 k++;
700 builder.append(*c);
701 }
702 }
703
704 return JSValue::encode(jsString(exec, builder.toString()));
705 }
706
707 EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec)
708 {
709 return throwVMTypeError(exec);
710 }
711
712 class GlobalFuncProtoGetterFunctor {
713 public:
714 GlobalFuncProtoGetterFunctor(JSObject* thisObject)
715 : m_hasSkippedFirstFrame(false)
716 , m_thisObject(thisObject)
717 , m_result(JSValue::encode(jsUndefined()))
718 {
719 }
720
721 EncodedJSValue result() { return m_result; }
722
723 StackVisitor::Status operator()(StackVisitor& visitor)
724 {
725 if (!m_hasSkippedFirstFrame) {
726 m_hasSkippedFirstFrame = true;
727 return StackVisitor::Continue;
728 }
729
730 if (m_thisObject->allowsAccessFrom(visitor->callFrame()))
731 m_result = JSValue::encode(m_thisObject->prototype());
732
733 return StackVisitor::Done;
734 }
735
736 private:
737 bool m_hasSkippedFirstFrame;
738 JSObject* m_thisObject;
739 EncodedJSValue m_result;
740 };
741
742 EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec)
743 {
744 JSObject* thisObject = jsDynamicCast<JSObject*>(exec->thisValue().toThis(exec, NotStrictMode));
745
746 if (!thisObject)
747 return JSValue::encode(exec->thisValue().synthesizePrototype(exec));
748
749 GlobalFuncProtoGetterFunctor functor(thisObject);
750 exec->iterate(functor);
751 return functor.result();
752 }
753
754 class GlobalFuncProtoSetterFunctor {
755 public:
756 GlobalFuncProtoSetterFunctor(JSObject* thisObject)
757 : m_hasSkippedFirstFrame(false)
758 , m_allowsAccess(false)
759 , m_thisObject(thisObject)
760 {
761 }
762
763 bool allowsAccess() const { return m_allowsAccess; }
764
765 StackVisitor::Status operator()(StackVisitor& visitor)
766 {
767 if (!m_hasSkippedFirstFrame) {
768 m_hasSkippedFirstFrame = true;
769 return StackVisitor::Continue;
770 }
771
772 m_allowsAccess = m_thisObject->allowsAccessFrom(visitor->callFrame());
773 return StackVisitor::Done;
774 }
775
776 private:
777 bool m_hasSkippedFirstFrame;
778 bool m_allowsAccess;
779 JSObject* m_thisObject;
780 };
781
782 EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec)
783 {
784 JSValue value = exec->argument(0);
785
786 JSObject* thisObject = jsDynamicCast<JSObject*>(exec->thisValue().toThis(exec, NotStrictMode));
787
788 // Setting __proto__ of a primitive should have no effect.
789 if (!thisObject)
790 return JSValue::encode(jsUndefined());
791
792 GlobalFuncProtoSetterFunctor functor(thisObject);
793 exec->iterate(functor);
794 if (!functor.allowsAccess())
795 return JSValue::encode(jsUndefined());
796
797 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
798 if (!value.isObject() && !value.isNull())
799 return JSValue::encode(jsUndefined());
800
801 if (!thisObject->isExtensible())
802 return throwVMError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
803
804 if (!thisObject->setPrototypeWithCycleCheck(exec, value))
805 exec->vm().throwException(exec, createError(exec, "cyclic __proto__ value"));
806 return JSValue::encode(jsUndefined());
807 }
808
809 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState* exec)
810 {
811 dataLog(exec->argument(0).toWTFString(exec), "\n");
812 return JSValue::encode(jsUndefined());
813 }
814
815 } // namespace JSC