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