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::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::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::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 char* s
, int length
, int radix
)
179 double radixMultiplier
= 1.0;
181 for (const char* p
= s
+ length
- 1; p
>= s
; p
--) {
182 if (radixMultiplier
== Inf
) {
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
== Inf
) {
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)
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
);
275 return sign
* number
;
278 static const int SizeOfInfinity
= 8;
280 static bool isInfinity(const UChar
* data
, const UChar
* end
)
282 return (end
- data
) >= SizeOfInfinity
293 // See ecma-262 9.3.1
294 static double jsHexIntegerLiteral(const UChar
*& data
, const UChar
* end
)
298 const UChar
* firstDigitPosition
= data
;
301 number
= number
* 16 + toASCIIHexValue(*data
);
305 if (!isASCIIHexDigit(*data
))
308 if (number
>= mantissaOverflowLowerBound
)
309 number
= parseIntOverflow(firstDigitPosition
, data
- firstDigitPosition
, 16);
314 // See ecma-262 9.3.1
315 static double jsStrDecimalLiteral(const UChar
*& data
, const UChar
* end
)
319 // Copy the sting into a null-terminated byte buffer, and call strtod.
320 Vector
<char, 32> byteBuffer
;
321 for (const UChar
* characters
= data
; characters
< end
; ++characters
) {
322 UChar character
= *characters
;
323 byteBuffer
.append(isASCII(character
) ? character
: 0);
325 byteBuffer
.append(0);
327 double number
= WTF::strtod(byteBuffer
.data(), &endOfNumber
);
329 // Check if strtod found a number; if so return it.
330 ptrdiff_t consumed
= endOfNumber
- byteBuffer
.data();
336 // Check for [+-]?Infinity
339 if (isInfinity(data
, end
)) {
340 data
+= SizeOfInfinity
;
346 if (isInfinity(data
+ 1, end
)) {
347 data
+= SizeOfInfinity
+ 1;
353 if (isInfinity(data
+ 1, end
)) {
354 data
+= SizeOfInfinity
+ 1;
364 // See ecma-262 9.3.1
365 double jsToNumber(const UString
& s
)
367 unsigned size
= s
.length();
370 UChar c
= s
.characters()[0];
373 if (isStrWhiteSpace(c
))
378 const UChar
* data
= s
.characters();
379 const UChar
* end
= data
+ size
;
381 // Skip leading white space.
382 for (; data
< end
; ++data
) {
383 if (!isStrWhiteSpace(*data
))
392 if (data
[0] == '0' && data
+ 2 < end
&& (data
[1] | 0x20) == 'x' && isASCIIHexDigit(data
[2]))
393 number
= jsHexIntegerLiteral(data
, end
);
395 number
= jsStrDecimalLiteral(data
, end
);
397 // Allow trailing white space.
398 for (; data
< end
; ++data
) {
399 if (!isStrWhiteSpace(*data
))
408 static double parseFloat(const UString
& s
)
410 unsigned size
= s
.length();
413 UChar c
= s
.characters()[0];
419 const UChar
* data
= s
.characters();
420 const UChar
* end
= data
+ size
;
422 // Skip leading white space.
423 for (; data
< end
; ++data
) {
424 if (!isStrWhiteSpace(*data
))
432 return jsStrDecimalLiteral(data
, end
);
435 EncodedJSValue JSC_HOST_CALL
globalFuncEval(ExecState
* exec
)
437 JSObject
* thisObject
= exec
->hostThisValue().toThisObject(exec
);
438 JSObject
* unwrappedObject
= thisObject
->unwrappedObject();
439 if (!unwrappedObject
->isGlobalObject() || static_cast<JSGlobalObject
*>(unwrappedObject
)->evalFunction() != exec
->callee())
440 return throwVMError(exec
, createEvalError(exec
, "The \"this\" value passed to eval must be the global object from which eval originated"));
442 JSValue x
= exec
->argument(0);
444 return JSValue::encode(x
);
446 UString s
= x
.toString(exec
);
448 LiteralParser
preparser(exec
, s
.characters(), s
.length(), LiteralParser::NonStrictJSON
);
449 if (JSValue parsedObject
= preparser
.tryLiteralParse())
450 return JSValue::encode(parsedObject
);
452 EvalExecutable
* eval
= EvalExecutable::create(exec
, makeSource(s
), false);
453 JSObject
* error
= eval
->compile(exec
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain());
455 return throwVMError(exec
, error
);
457 return JSValue::encode(exec
->interpreter()->execute(eval
, exec
, thisObject
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain()));
460 EncodedJSValue JSC_HOST_CALL
globalFuncParseInt(ExecState
* exec
)
462 JSValue value
= exec
->argument(0);
463 int32_t radix
= exec
->argument(1).toInt32(exec
);
465 if (radix
!= 0 && radix
!= 10)
466 return JSValue::encode(jsNumber(parseInt(value
.toString(exec
), radix
)));
469 return JSValue::encode(value
);
471 if (value
.isDouble()) {
472 double d
= value
.asDouble();
474 return JSValue::encode(jsNumber((d
> 0) ? floor(d
) : ceil(d
)));
475 if (isnan(d
) || isinf(d
))
476 return JSValue::encode(jsNaN());
477 return JSValue::encode(jsNumber(0));
480 return JSValue::encode(jsNumber(parseInt(value
.toString(exec
), radix
)));
483 EncodedJSValue JSC_HOST_CALL
globalFuncParseFloat(ExecState
* exec
)
485 return JSValue::encode(jsNumber(parseFloat(exec
->argument(0).toString(exec
))));
488 EncodedJSValue JSC_HOST_CALL
globalFuncIsNaN(ExecState
* exec
)
490 return JSValue::encode(jsBoolean(isnan(exec
->argument(0).toNumber(exec
))));
493 EncodedJSValue JSC_HOST_CALL
globalFuncIsFinite(ExecState
* exec
)
495 double n
= exec
->argument(0).toNumber(exec
);
496 return JSValue::encode(jsBoolean(!isnan(n
) && !isinf(n
)));
499 EncodedJSValue JSC_HOST_CALL
globalFuncDecodeURI(ExecState
* exec
)
501 static const char do_not_unescape_when_decoding_URI
[] =
504 return JSValue::encode(decode(exec
, do_not_unescape_when_decoding_URI
, true));
507 EncodedJSValue JSC_HOST_CALL
globalFuncDecodeURIComponent(ExecState
* exec
)
509 return JSValue::encode(decode(exec
, "", true));
512 EncodedJSValue JSC_HOST_CALL
globalFuncEncodeURI(ExecState
* exec
)
514 static const char do_not_escape_when_encoding_URI
[] =
515 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
516 "abcdefghijklmnopqrstuvwxyz"
518 "!#$&'()*+,-./:;=?@_~";
520 return JSValue::encode(encode(exec
, do_not_escape_when_encoding_URI
));
523 EncodedJSValue JSC_HOST_CALL
globalFuncEncodeURIComponent(ExecState
* exec
)
525 static const char do_not_escape_when_encoding_URI_component
[] =
526 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
527 "abcdefghijklmnopqrstuvwxyz"
531 return JSValue::encode(encode(exec
, do_not_escape_when_encoding_URI_component
));
534 EncodedJSValue JSC_HOST_CALL
globalFuncEscape(ExecState
* exec
)
536 static const char do_not_escape
[] =
537 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
538 "abcdefghijklmnopqrstuvwxyz"
542 JSStringBuilder builder
;
543 UString str
= exec
->argument(0).toString(exec
);
544 const UChar
* c
= str
.characters();
545 for (unsigned k
= 0; k
< str
.length(); k
++, c
++) {
549 snprintf(tmp
, sizeof(tmp
), "%%u%04X", u
);
551 } else if (u
!= 0 && strchr(do_not_escape
, static_cast<char>(u
)))
552 builder
.append(c
, 1);
555 snprintf(tmp
, sizeof(tmp
), "%%%02X", u
);
560 return JSValue::encode(builder
.build(exec
));
563 EncodedJSValue JSC_HOST_CALL
globalFuncUnescape(ExecState
* exec
)
565 UStringBuilder builder
;
566 UString str
= exec
->argument(0).toString(exec
);
568 int len
= str
.length();
570 const UChar
* c
= str
.characters() + k
;
572 if (c
[0] == '%' && k
<= len
- 6 && c
[1] == 'u') {
573 if (isASCIIHexDigit(c
[2]) && isASCIIHexDigit(c
[3]) && isASCIIHexDigit(c
[4]) && isASCIIHexDigit(c
[5])) {
574 u
= Lexer::convertUnicode(c
[2], c
[3], c
[4], c
[5]);
578 } else if (c
[0] == '%' && k
<= len
- 3 && isASCIIHexDigit(c
[1]) && isASCIIHexDigit(c
[2])) {
579 u
= UChar(Lexer::convertHex(c
[1], c
[2]));
587 return JSValue::encode(jsString(exec
, builder
.toUString()));