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 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
6 * Copyright (C) 2007 Maks Orlovich
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.
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.
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.
26 #include "JSGlobalObjectFunctions.h"
28 #include "CallFrame.h"
29 #include "Interpreter.h"
30 #include "JSGlobalObject.h"
32 #include "JSStringBuilder.h"
34 #include "LiteralParser.h"
37 #include "UStringBuilder.h"
41 #include <wtf/ASCIICType.h>
42 #include <wtf/Assertions.h>
43 #include <wtf/MathExtras.h>
44 #include <wtf/StringExtras.h>
45 #include <wtf/unicode/UTF8.h>
48 using namespace Unicode;
52 static JSValue encode(ExecState* exec, const char* doNotEscape)
54 UString str = exec->argument(0).toString(exec);
55 CString cstr = str.utf8(true);
57 return throwError(exec, createURIError(exec, "String contained an illegal UTF-16 sequence."));
59 JSStringBuilder builder;
60 const char* p = cstr.data();
61 for (size_t k = 0; k < cstr.length(); k++, p++) {
63 if (c && strchr(doNotEscape, c))
67 snprintf(tmp, sizeof(tmp), "%%%02X", static_cast<unsigned char>(c));
71 return builder.build(exec);
74 static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict)
76 JSStringBuilder builder;
77 UString str = exec->argument(0).toString(exec);
79 int len = str.length();
80 const UChar* d = str.characters();
83 const UChar* p = d + k;
87 if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
88 const char b0 = Lexer<UChar>::convertHex(p[1], p[2]);
89 const int sequenceLen = UTF8SequenceLength(b0);
90 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
91 charLen = sequenceLen * 3;
94 for (int i = 1; i < sequenceLen; ++i) {
95 const UChar* q = p + i * 3;
96 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
97 sequence[i] = Lexer<UChar>::convertHex(q[1], q[2]);
104 sequence[sequenceLen] = 0;
105 const int character = decodeUTF8Sequence(sequence);
106 if (character < 0 || character >= 0x110000)
108 else if (character >= 0x10000) {
109 // Convert to surrogate pair.
110 builder.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
111 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
113 u = static_cast<UChar>(character);
119 return throwError(exec, createURIError(exec, "URI error"));
120 // The only case where we don't use "strict" mode is the "unescape" function.
121 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
122 if (k <= len - 6 && p[1] == 'u'
123 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
124 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
126 u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]);
129 if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
137 return builder.build(exec);
140 bool isStrWhiteSpace(UChar c)
143 // ECMA-262-5th 7.2 & 7.3
156 return c > 0xff && isSeparatorSpace(c);
160 static int parseDigit(unsigned short c, int radix)
164 if (c >= '0' && c <= '9')
166 else if (c >= 'A' && c <= 'Z')
167 digit = c - 'A' + 10;
168 else if (c >= 'a' && c <= 'z')
169 digit = c - 'a' + 10;
176 double parseIntOverflow(const LChar* s, int length, int radix)
179 double radixMultiplier = 1.0;
181 for (const LChar* p = s + length - 1; p >= s; p--) {
182 if (radixMultiplier == std::numeric_limits<double>::infinity()) {
184 number = std::numeric_limits<double>::infinity();
188 int digit = parseDigit(*p, radix);
189 number += digit * radixMultiplier;
192 radixMultiplier *= radix;
198 double parseIntOverflow(const UChar* s, int length, int radix)
201 double radixMultiplier = 1.0;
203 for (const UChar* p = s + length - 1; p >= s; p--) {
204 if (radixMultiplier == std::numeric_limits<double>::infinity()) {
206 number = std::numeric_limits<double>::infinity();
210 int digit = parseDigit(*p, radix);
211 number += digit * radixMultiplier;
214 radixMultiplier *= radix;
220 static double parseInt(const UString& s, int radix)
222 int length = s.length();
223 const UChar* data = s.characters();
226 while (p < length && isStrWhiteSpace(data[p]))
233 else if (data[p] == '-') {
239 if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) {
242 } else if (radix == 0) {
243 if (p < length && data[p] == '0')
249 if (radix < 2 || radix > 36)
250 return std::numeric_limits<double>::quiet_NaN();
252 int firstDigitPosition = p;
253 bool sawDigit = false;
256 int digit = parseDigit(data[p], radix);
265 if (number >= mantissaOverflowLowerBound) {
267 number = WTF::strtod(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), 0);
268 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
269 number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix);
273 return std::numeric_limits<double>::quiet_NaN();
275 return sign * number;
278 static const int SizeOfInfinity = 8;
280 template <typename CharType>
281 static bool isInfinity(const CharType* data, const CharType* end)
283 return (end - data) >= SizeOfInfinity
294 // See ecma-262 9.3.1
295 template <typename CharType>
296 static double jsHexIntegerLiteral(const CharType*& data, const CharType* end)
300 const CharType* firstDigitPosition = data;
303 number = number * 16 + toASCIIHexValue(*data);
307 if (!isASCIIHexDigit(*data))
310 if (number >= mantissaOverflowLowerBound)
311 number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16);
316 // See ecma-262 9.3.1
317 template <typename CharType>
318 static double jsStrDecimalLiteral(const CharType*& data, const CharType* end)
322 // Copy the sting into a null-terminated byte buffer, and call strtod.
323 Vector<char, 32> byteBuffer;
324 for (const CharType* characters = data; characters < end; ++characters) {
325 CharType character = *characters;
326 byteBuffer.append(isASCII(character) ? static_cast<char>(character) : 0);
328 byteBuffer.append(0);
330 double number = WTF::strtod(byteBuffer.data(), &endOfNumber);
332 // Check if strtod found a number; if so return it.
333 ptrdiff_t consumed = endOfNumber - byteBuffer.data();
339 // Check for [+-]?Infinity
342 if (isInfinity(data, end)) {
343 data += SizeOfInfinity;
344 return std::numeric_limits<double>::infinity();
349 if (isInfinity(data + 1, end)) {
350 data += SizeOfInfinity + 1;
351 return std::numeric_limits<double>::infinity();
356 if (isInfinity(data + 1, end)) {
357 data += SizeOfInfinity + 1;
358 return -std::numeric_limits<double>::infinity();
364 return std::numeric_limits<double>::quiet_NaN();
367 template <typename CharType>
368 static double toDouble(const CharType* characters, unsigned size)
370 const CharType* endCharacters = characters + size;
372 // Skip leading white space.
373 for (; characters < endCharacters; ++characters) {
374 if (!isStrWhiteSpace(*characters))
379 if (characters == endCharacters)
383 if (characters[0] == '0' && characters + 2 < endCharacters && (characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2]))
384 number = jsHexIntegerLiteral(characters, endCharacters);
386 number = jsStrDecimalLiteral(characters, endCharacters);
388 // Allow trailing white space.
389 for (; characters < endCharacters; ++characters) {
390 if (!isStrWhiteSpace(*characters))
393 if (characters != endCharacters)
394 return std::numeric_limits<double>::quiet_NaN();
399 // See ecma-262 9.3.1
400 double jsToNumber(const UString& s)
402 unsigned size = s.length();
408 if (isStrWhiteSpace(c))
410 return std::numeric_limits<double>::quiet_NaN();
414 return toDouble(s.characters8(), size);
415 return toDouble(s.characters16(), size);
418 static double parseFloat(const UString& s)
420 unsigned size = s.length();
426 return std::numeric_limits<double>::quiet_NaN();
429 const UChar* data = s.characters();
430 const UChar* end = data + size;
432 // Skip leading white space.
433 for (; data < end; ++data) {
434 if (!isStrWhiteSpace(*data))
440 return std::numeric_limits<double>::quiet_NaN();
442 return jsStrDecimalLiteral(data, end);
445 EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec)
447 JSObject* thisObject = exec->hostThisValue().toThisObject(exec);
448 JSObject* unwrappedObject = thisObject->unwrappedObject();
449 if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != exec->callee())
450 return throwVMError(exec, createEvalError(exec, "The \"this\" value passed to eval must be the global object from which eval originated"));
452 JSValue x = exec->argument(0);
454 return JSValue::encode(x);
456 UString s = x.toString(exec);
459 LiteralParser<LChar> preparser(exec, s.characters8(), s.length(), NonStrictJSON);
460 if (JSValue parsedObject = preparser.tryLiteralParse())
461 return JSValue::encode(parsedObject);
463 LiteralParser<UChar> preparser(exec, s.characters16(), s.length(), NonStrictJSON);
464 if (JSValue parsedObject = preparser.tryLiteralParse())
465 return JSValue::encode(parsedObject);
468 EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false);
469 JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain());
471 return throwVMError(exec, error);
473 return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain()));
476 EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec)
478 JSValue value = exec->argument(0);
479 JSValue radixValue = exec->argument(1);
481 // Optimized handling for numbers:
482 // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt
483 // results in a truncation to integer. In the case of -0, this is converted to 0.
485 // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21,
486 // however these values cannot be trivially truncated to int since 10^21 exceeds
487 // even the int64_t range. Negative numbers are a little trickier, the case for
488 // values in the range -10^21 < n <= -1 are similar to those for integer, but
489 // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0.
490 static const double tenToTheMinus6 = 0.000001;
491 static const double intMaxPlusOne = 2147483648.0;
492 if (value.isNumber()) {
493 double n = value.asNumber();
494 if (((n < intMaxPlusOne && n >= tenToTheMinus6) || !n) && radixValue.isUndefinedOrNull())
495 return JSValue::encode(jsNumber(static_cast<int32_t>(n)));
498 // If ToString throws, we shouldn't call ToInt32.
499 UString s = value.toString(exec);
500 if (exec->hadException())
501 return JSValue::encode(jsUndefined());
503 return JSValue::encode(jsNumber(parseInt(s, radixValue.toInt32(exec))));
506 EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec)
508 return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec))));
511 EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec)
513 return JSValue::encode(jsBoolean(isnan(exec->argument(0).toNumber(exec))));
516 EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec)
518 double n = exec->argument(0).toNumber(exec);
519 return JSValue::encode(jsBoolean(isfinite(n)));
522 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec)
524 static const char do_not_unescape_when_decoding_URI[] =
527 return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true));
530 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec)
532 return JSValue::encode(decode(exec, "", true));
535 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec)
537 static const char do_not_escape_when_encoding_URI[] =
538 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
539 "abcdefghijklmnopqrstuvwxyz"
541 "!#$&'()*+,-./:;=?@_~";
543 return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI));
546 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec)
548 static const char do_not_escape_when_encoding_URI_component[] =
549 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
550 "abcdefghijklmnopqrstuvwxyz"
554 return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component));
557 EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
559 static const char do_not_escape[] =
560 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
561 "abcdefghijklmnopqrstuvwxyz"
565 JSStringBuilder builder;
566 UString str = exec->argument(0).toString(exec);
567 const UChar* c = str.characters();
568 for (unsigned k = 0; k < str.length(); k++, c++) {
572 snprintf(tmp, sizeof(tmp), "%%u%04X", u);
574 } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
575 builder.append(c, 1);
578 snprintf(tmp, sizeof(tmp), "%%%02X", u);
583 return JSValue::encode(builder.build(exec));
586 EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec)
588 UStringBuilder builder;
589 UString str = exec->argument(0).toString(exec);
591 int len = str.length();
593 const UChar* c = str.characters() + k;
595 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
596 if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
597 u = Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]);
601 } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
602 u = UChar(Lexer<UChar>::convertHex(c[1], c[2]));
610 return JSValue::encode(jsString(exec, builder.toUString()));
613 EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec)
615 return throwVMTypeError(exec);