]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSGlobalObjectFunctions.cpp
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / runtime / JSGlobalObjectFunctions.cpp
CommitLineData
9dae56ea
A
1/*
2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
6fe7ccc8 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
9dae56ea
A
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"
f9bf01c6 29#include "Interpreter.h"
93a37866 30#include "JSFunction.h"
9dae56ea
A
31#include "JSGlobalObject.h"
32#include "JSString.h"
4e4e5a6f 33#include "JSStringBuilder.h"
9dae56ea 34#include "Lexer.h"
f9bf01c6 35#include "LiteralParser.h"
9dae56ea 36#include "Nodes.h"
81345200 37#include "JSCInlines.h"
f9bf01c6 38#include "Parser.h"
81345200 39#include "StackVisitor.h"
6fe7ccc8 40#include <wtf/dtoa.h>
9dae56ea
A
41#include <stdio.h>
42#include <stdlib.h>
9dae56ea
A
43#include <wtf/ASCIICType.h>
44#include <wtf/Assertions.h>
45#include <wtf/MathExtras.h>
14957cd0 46#include <wtf/StringExtras.h>
93a37866 47#include <wtf/text/StringBuilder.h>
9dae56ea
A
48#include <wtf/unicode/UTF8.h>
49
50using namespace WTF;
51using namespace Unicode;
52
53namespace JSC {
54
14957cd0 55static JSValue encode(ExecState* exec, const char* doNotEscape)
9dae56ea 56{
81345200 57 CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(StrictConversion);
4e4e5a6f 58 if (!cstr.data())
81345200 59 return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("String contained an illegal UTF-16 sequence.")));
9dae56ea 60
4e4e5a6f
A
61 JSStringBuilder builder;
62 const char* p = cstr.data();
63 for (size_t k = 0; k < cstr.length(); k++, p++) {
9dae56ea
A
64 char c = *p;
65 if (c && strchr(doNotEscape, c))
81345200 66 builder.append(static_cast<LChar>(c));
9dae56ea
A
67 else {
68 char tmp[4];
69 snprintf(tmp, sizeof(tmp), "%%%02X", static_cast<unsigned char>(c));
4e4e5a6f 70 builder.append(tmp);
9dae56ea
A
71 }
72 }
4e4e5a6f 73 return builder.build(exec);
9dae56ea
A
74}
75
6fe7ccc8
A
76template <typename CharType>
77ALWAYS_INLINE
78static JSValue decode(ExecState* exec, const CharType* characters, int length, const char* doNotUnescape, bool strict)
9dae56ea 79{
4e4e5a6f 80 JSStringBuilder builder;
9dae56ea 81 int k = 0;
9dae56ea 82 UChar u = 0;
6fe7ccc8
A
83 while (k < length) {
84 const CharType* p = characters + k;
85 CharType c = *p;
9dae56ea
A
86 if (c == '%') {
87 int charLen = 0;
6fe7ccc8
A
88 if (k <= length - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
89 const char b0 = Lexer<CharType>::convertHex(p[1], p[2]);
9dae56ea 90 const int sequenceLen = UTF8SequenceLength(b0);
6fe7ccc8 91 if (sequenceLen && k <= length - sequenceLen * 3) {
9dae56ea
A
92 charLen = sequenceLen * 3;
93 char sequence[5];
94 sequence[0] = b0;
95 for (int i = 1; i < sequenceLen; ++i) {
6fe7ccc8 96 const CharType* q = p + i * 3;
9dae56ea 97 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
6fe7ccc8 98 sequence[i] = Lexer<CharType>::convertHex(q[1], q[2]);
9dae56ea
A
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.
f9bf01c6 111 builder.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
9dae56ea
A
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)
81345200 120 return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("URI error")));
9dae56ea
A
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.
6fe7ccc8 123 if (k <= length - 6 && p[1] == 'u'
9dae56ea
A
124 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
125 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
126 charLen = 6;
6fe7ccc8 127 u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]);
9dae56ea
A
128 }
129 }
130 if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
81345200 131 builder.append(u);
6fe7ccc8
A
132 k += charLen;
133 continue;
9dae56ea
A
134 }
135 }
136 k++;
f9bf01c6 137 builder.append(c);
9dae56ea 138 }
4e4e5a6f 139 return builder.build(exec);
9dae56ea
A
140}
141
6fe7ccc8
A
142static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict)
143{
93a37866 144 String str = exec->argument(0).toString(exec)->value(exec);
6fe7ccc8
A
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
9dae56ea
A
151bool isStrWhiteSpace(UChar c)
152{
153 switch (c) {
14957cd0 154 // ECMA-262-5th 7.2 & 7.3
9dae56ea
A
155 case 0x0009:
156 case 0x000A:
157 case 0x000B:
158 case 0x000C:
159 case 0x000D:
160 case 0x0020:
161 case 0x00A0:
81345200 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.
9dae56ea
A
163 case 0x2028:
164 case 0x2029:
14957cd0 165 case 0xFEFF:
9dae56ea
A
166 return true;
167 default:
81345200 168 return c > 0xFF && u_charType(c) == U_SPACE_SEPARATOR;
9dae56ea
A
169 }
170}
171
172static 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
81345200 188double parseIntOverflow(const LChar* s, unsigned length, int radix)
9dae56ea
A
189{
190 double number = 0.0;
191 double radixMultiplier = 1.0;
192
6fe7ccc8
A
193 for (const LChar* p = s + length - 1; p >= s; p--) {
194 if (radixMultiplier == std::numeric_limits<double>::infinity()) {
9dae56ea 195 if (*p != '0') {
6fe7ccc8 196 number = std::numeric_limits<double>::infinity();
9dae56ea
A
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
81345200 210static double parseIntOverflow(const UChar* s, unsigned length, int radix)
14957cd0
A
211{
212 double number = 0.0;
213 double radixMultiplier = 1.0;
214
215 for (const UChar* p = s + length - 1; p >= s; p--) {
6fe7ccc8 216 if (radixMultiplier == std::numeric_limits<double>::infinity()) {
14957cd0 217 if (*p != '0') {
6fe7ccc8 218 number = std::numeric_limits<double>::infinity();
14957cd0
A
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
81345200
A
232static 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
6fe7ccc8
A
239// ES5.1 15.1.2.2
240template <typename CharType>
241ALWAYS_INLINE
93a37866 242static double parseInt(const String& s, const CharType* data, int radix)
9dae56ea 243{
6fe7ccc8
A
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.
14957cd0 248 int length = s.length();
9dae56ea 249 int p = 0;
9dae56ea
A
250 while (p < length && isStrWhiteSpace(data[p]))
251 ++p;
252
6fe7ccc8
A
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.
9dae56ea
A
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
6fe7ccc8
A
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.
9dae56ea
A
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;
6fe7ccc8
A
280 } else if (radix == 0)
281 radix = 10;
9dae56ea 282
6fe7ccc8 283 // 8.a If R < 2 or R > 36, then return NaN.
9dae56ea 284 if (radix < 2 || radix > 36)
81345200 285 return PNaN;
6fe7ccc8
A
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.
9dae56ea
A
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
6fe7ccc8
A
306 // 12. If Z is empty, return NaN.
307 if (!sawDigit)
81345200 308 return PNaN;
6fe7ccc8
A
309
310 // Alternate code path for certain large numbers.
9dae56ea 311 if (number >= mantissaOverflowLowerBound) {
6fe7ccc8
A
312 if (radix == 10) {
313 size_t parsedLength;
81345200 314 number = parseDouble(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), parsedLength);
6fe7ccc8 315 } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
81345200 316 number = parseIntOverflow(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), radix);
9dae56ea
A
317 }
318
6fe7ccc8 319 // 15. Return sign x number.
9dae56ea
A
320 return sign * number;
321}
322
93a37866 323static double parseInt(const String& s, int radix)
6fe7ccc8
A
324{
325 if (s.is8Bit())
326 return parseInt(s, s.characters8(), radix);
327 return parseInt(s, s.characters16(), radix);
328}
329
14957cd0
A
330static const int SizeOfInfinity = 8;
331
6fe7ccc8
A
332template <typename CharType>
333static bool isInfinity(const CharType* data, const CharType* end)
14957cd0
A
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
6fe7ccc8
A
347template <typename CharType>
348static double jsHexIntegerLiteral(const CharType*& data, const CharType* end)
14957cd0
A
349{
350 // Hex number.
351 data += 2;
6fe7ccc8 352 const CharType* firstDigitPosition = data;
14957cd0
A
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
6fe7ccc8
A
369template <typename CharType>
370static double jsStrDecimalLiteral(const CharType*& data, const CharType* end)
14957cd0 371{
93a37866 372 RELEASE_ASSERT(data < end);
14957cd0 373
6fe7ccc8
A
374 size_t parsedLength;
375 double number = parseDouble(data, end - data, parsedLength);
376 if (parsedLength) {
377 data += parsedLength;
14957cd0
A
378 return number;
379 }
380
381 // Check for [+-]?Infinity
382 switch (*data) {
383 case 'I':
384 if (isInfinity(data, end)) {
385 data += SizeOfInfinity;
6fe7ccc8 386 return std::numeric_limits<double>::infinity();
14957cd0
A
387 }
388 break;
389
390 case '+':
391 if (isInfinity(data + 1, end)) {
392 data += SizeOfInfinity + 1;
6fe7ccc8 393 return std::numeric_limits<double>::infinity();
14957cd0
A
394 }
395 break;
396
397 case '-':
398 if (isInfinity(data + 1, end)) {
399 data += SizeOfInfinity + 1;
6fe7ccc8 400 return -std::numeric_limits<double>::infinity();
14957cd0
A
401 }
402 break;
403 }
404
405 // Not a number.
81345200 406 return PNaN;
14957cd0
A
407}
408
6fe7ccc8
A
409template <typename CharType>
410static double toDouble(const CharType* characters, unsigned size)
14957cd0 411{
6fe7ccc8 412 const CharType* endCharacters = characters + size;
14957cd0
A
413
414 // Skip leading white space.
6fe7ccc8
A
415 for (; characters < endCharacters; ++characters) {
416 if (!isStrWhiteSpace(*characters))
14957cd0
A
417 break;
418 }
6fe7ccc8 419
14957cd0 420 // Empty string.
6fe7ccc8 421 if (characters == endCharacters)
14957cd0 422 return 0.0;
6fe7ccc8 423
14957cd0 424 double number;
6fe7ccc8
A
425 if (characters[0] == '0' && characters + 2 < endCharacters && (characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2]))
426 number = jsHexIntegerLiteral(characters, endCharacters);
14957cd0 427 else
6fe7ccc8
A
428 number = jsStrDecimalLiteral(characters, endCharacters);
429
14957cd0 430 // Allow trailing white space.
6fe7ccc8
A
431 for (; characters < endCharacters; ++characters) {
432 if (!isStrWhiteSpace(*characters))
14957cd0
A
433 break;
434 }
6fe7ccc8 435 if (characters != endCharacters)
81345200 436 return PNaN;
6fe7ccc8 437
14957cd0
A
438 return number;
439}
440
6fe7ccc8 441// See ecma-262 9.3.1
93a37866 442double jsToNumber(const String& s)
6fe7ccc8
A
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;
81345200 452 return PNaN;
6fe7ccc8
A
453 }
454
455 if (s.is8Bit())
456 return toDouble(s.characters8(), size);
457 return toDouble(s.characters16(), size);
458}
459
93a37866 460static double parseFloat(const String& s)
9dae56ea 461{
14957cd0 462 unsigned size = s.length();
9dae56ea 463
14957cd0 464 if (size == 1) {
6fe7ccc8 465 UChar c = s[0];
14957cd0
A
466 if (isASCIIDigit(c))
467 return c - '0';
81345200 468 return PNaN;
6fe7ccc8
A
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)
81345200 483 return PNaN;
6fe7ccc8
A
484
485 return jsStrDecimalLiteral(data, end);
14957cd0 486 }
9dae56ea 487
6fe7ccc8 488 const UChar* data = s.characters16();
14957cd0 489 const UChar* end = data + size;
9dae56ea 490
14957cd0
A
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)
81345200 499 return PNaN;
14957cd0
A
500
501 return jsStrDecimalLiteral(data, end);
9dae56ea
A
502}
503
14957cd0 504EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec)
9dae56ea 505{
14957cd0 506 JSValue x = exec->argument(0);
9dae56ea 507 if (!x.isString())
14957cd0 508 return JSValue::encode(x);
9dae56ea 509
93a37866 510 String s = x.toString(exec)->value(exec);
9dae56ea 511
6fe7ccc8
A
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 }
ba379fdc 521
93a37866 522 JSGlobalObject* calleeGlobalObject = exec->callee()->globalObject();
81345200
A
523 EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false);
524 if (!eval)
525 return JSValue::encode(jsUndefined());
9dae56ea 526
93a37866 527 return JSValue::encode(exec->interpreter()->execute(eval, exec, calleeGlobalObject->globalThis(), calleeGlobalObject));
9dae56ea
A
528}
529
14957cd0 530EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec)
9dae56ea 531{
14957cd0 532 JSValue value = exec->argument(0);
6fe7ccc8
A
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)));
9dae56ea
A
550 }
551
6fe7ccc8 552 // If ToString throws, we shouldn't call ToInt32.
93a37866 553 String s = value.toString(exec)->value(exec);
6fe7ccc8
A
554 if (exec->hadException())
555 return JSValue::encode(jsUndefined());
556
557 return JSValue::encode(jsNumber(parseInt(s, radixValue.toInt32(exec))));
9dae56ea
A
558}
559
14957cd0 560EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec)
9dae56ea 561{
6fe7ccc8 562 return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->value(exec))));
9dae56ea
A
563}
564
14957cd0 565EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec)
9dae56ea 566{
93a37866 567 return JSValue::encode(jsBoolean(std::isnan(exec->argument(0).toNumber(exec))));
9dae56ea
A
568}
569
14957cd0 570EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec)
9dae56ea 571{
14957cd0 572 double n = exec->argument(0).toNumber(exec);
93a37866 573 return JSValue::encode(jsBoolean(std::isfinite(n)));
9dae56ea
A
574}
575
14957cd0 576EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec)
9dae56ea
A
577{
578 static const char do_not_unescape_when_decoding_URI[] =
579 "#$&+,/:;=?@";
580
14957cd0 581 return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true));
9dae56ea
A
582}
583
14957cd0 584EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec)
9dae56ea 585{
14957cd0 586 return JSValue::encode(decode(exec, "", true));
9dae56ea
A
587}
588
14957cd0 589EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec)
9dae56ea
A
590{
591 static const char do_not_escape_when_encoding_URI[] =
592 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
593 "abcdefghijklmnopqrstuvwxyz"
594 "0123456789"
595 "!#$&'()*+,-./:;=?@_~";
596
14957cd0 597 return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI));
9dae56ea
A
598}
599
14957cd0 600EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec)
9dae56ea
A
601{
602 static const char do_not_escape_when_encoding_URI_component[] =
603 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
604 "abcdefghijklmnopqrstuvwxyz"
605 "0123456789"
606 "!'()*-._~";
607
14957cd0 608 return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component));
9dae56ea
A
609}
610
14957cd0 611EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
9dae56ea
A
612{
613 static const char do_not_escape[] =
614 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
615 "abcdefghijklmnopqrstuvwxyz"
616 "0123456789"
617 "*+-./@_";
618
4e4e5a6f 619 JSStringBuilder builder;
93a37866 620 String str = exec->argument(0).toString(exec)->value(exec);
6fe7ccc8
A
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)))
81345200 626 builder.append(*c);
6fe7ccc8
A
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();
14957cd0 638 for (unsigned k = 0; k < str.length(); k++, c++) {
9dae56ea
A
639 int u = c[0];
640 if (u > 255) {
641 char tmp[7];
642 snprintf(tmp, sizeof(tmp), "%%u%04X", u);
4e4e5a6f 643 builder.append(tmp);
9dae56ea 644 } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
81345200 645 builder.append(*c);
9dae56ea
A
646 else {
647 char tmp[4];
648 snprintf(tmp, sizeof(tmp), "%%%02X", u);
4e4e5a6f 649 builder.append(tmp);
9dae56ea 650 }
9dae56ea
A
651 }
652
14957cd0 653 return JSValue::encode(builder.build(exec));
9dae56ea
A
654}
655
14957cd0 656EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec)
9dae56ea 657{
93a37866
A
658 StringBuilder builder;
659 String str = exec->argument(0).toString(exec)->value(exec);
9dae56ea 660 int k = 0;
14957cd0 661 int len = str.length();
6fe7ccc8
A
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;
9dae56ea 678 }
6fe7ccc8
A
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);
9dae56ea 701 }
9dae56ea
A
702 }
703
93a37866 704 return JSValue::encode(jsString(exec, builder.toString()));
9dae56ea 705}
9dae56ea 706
6fe7ccc8
A
707EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec)
708{
709 return throwVMTypeError(exec);
710}
711
81345200
A
712class GlobalFuncProtoGetterFunctor {
713public:
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
736private:
737 bool m_hasSkippedFirstFrame;
738 JSObject* m_thisObject;
739 EncodedJSValue m_result;
740};
741
6fe7ccc8
A
742EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec)
743{
81345200 744 JSObject* thisObject = jsDynamicCast<JSObject*>(exec->thisValue().toThis(exec, NotStrictMode));
6fe7ccc8 745
81345200
A
746 if (!thisObject)
747 return JSValue::encode(exec->thisValue().synthesizePrototype(exec));
6fe7ccc8 748
81345200
A
749 GlobalFuncProtoGetterFunctor functor(thisObject);
750 exec->iterate(functor);
751 return functor.result();
6fe7ccc8
A
752}
753
81345200
A
754class GlobalFuncProtoSetterFunctor {
755public:
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
776private:
777 bool m_hasSkippedFirstFrame;
778 bool m_allowsAccess;
779 JSObject* m_thisObject;
780};
781
6fe7ccc8
A
782EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec)
783{
784 JSValue value = exec->argument(0);
785
81345200
A
786 JSObject* thisObject = jsDynamicCast<JSObject*>(exec->thisValue().toThis(exec, NotStrictMode));
787
6fe7ccc8 788 // Setting __proto__ of a primitive should have no effect.
81345200 789 if (!thisObject)
6fe7ccc8
A
790 return JSValue::encode(jsUndefined());
791
81345200
A
792 GlobalFuncProtoSetterFunctor functor(thisObject);
793 exec->iterate(functor);
794 if (!functor.allowsAccess())
6fe7ccc8
A
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
81345200
A
804 if (!thisObject->setPrototypeWithCycleCheck(exec, value))
805 exec->vm().throwException(exec, createError(exec, "cyclic __proto__ value"));
806 return JSValue::encode(jsUndefined());
807}
808
809EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState* exec)
810{
811 dataLog(exec->argument(0).toWTFString(exec), "\n");
6fe7ccc8
A
812 return JSValue::encode(jsUndefined());
813}
814
9dae56ea 815} // namespace JSC