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 "GlobalEvalFunction.h"
30 #include "Interpreter.h"
31 #include "JSGlobalObject.h"
33 #include "JSStringBuilder.h"
35 #include "LiteralParser.h"
38 #include "StringBuilder.h"
39 #include "StringExtras.h"
44 #include <wtf/ASCIICType.h>
45 #include <wtf/Assertions.h>
46 #include <wtf/MathExtras.h>
47 #include <wtf/unicode/UTF8.h>
50 using namespace Unicode
;
54 static JSValue
encode(ExecState
* exec
, const ArgList
& args
, const char* doNotEscape
)
56 UString str
= args
.at(0).toString(exec
);
57 CString cstr
= str
.UTF8String(true);
59 return throwError(exec
, URIError
, "String contained an illegal UTF-16 sequence.");
61 JSStringBuilder builder
;
62 const char* p
= cstr
.data();
63 for (size_t k
= 0; k
< cstr
.length(); k
++, p
++) {
65 if (c
&& strchr(doNotEscape
, c
))
69 snprintf(tmp
, sizeof(tmp
), "%%%02X", static_cast<unsigned char>(c
));
73 return builder
.build(exec
);
76 static JSValue
decode(ExecState
* exec
, const ArgList
& args
, const char* doNotUnescape
, bool strict
)
78 JSStringBuilder builder
;
79 UString str
= args
.at(0).toString(exec
);
82 const UChar
* d
= str
.data();
85 const UChar
* p
= d
+ k
;
89 if (k
<= len
- 3 && isASCIIHexDigit(p
[1]) && isASCIIHexDigit(p
[2])) {
90 const char b0
= Lexer::convertHex(p
[1], p
[2]);
91 const int sequenceLen
= UTF8SequenceLength(b0
);
92 if (sequenceLen
!= 0 && k
<= len
- sequenceLen
* 3) {
93 charLen
= sequenceLen
* 3;
96 for (int i
= 1; i
< sequenceLen
; ++i
) {
97 const UChar
* q
= p
+ i
* 3;
98 if (q
[0] == '%' && isASCIIHexDigit(q
[1]) && isASCIIHexDigit(q
[2]))
99 sequence
[i
] = Lexer::convertHex(q
[1], q
[2]);
106 sequence
[sequenceLen
] = 0;
107 const int character
= decodeUTF8Sequence(sequence
);
108 if (character
< 0 || character
>= 0x110000)
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));
115 u
= static_cast<UChar
>(character
);
121 return throwError(exec
, URIError
);
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
<= len
- 6 && p
[1] == 'u'
125 && isASCIIHexDigit(p
[2]) && isASCIIHexDigit(p
[3])
126 && isASCIIHexDigit(p
[4]) && isASCIIHexDigit(p
[5])) {
128 u
= Lexer::convertUnicode(p
[2], p
[3], p
[4], p
[5]);
131 if (charLen
&& (u
== 0 || u
>= 128 || !strchr(doNotUnescape
, u
))) {
139 return builder
.build(exec
);
142 bool isStrWhiteSpace(UChar c
)
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 static double parseInt(const UString
& s
, int radix
)
200 int length
= s
.size();
201 const UChar
* data
= s
.data();
204 while (p
< length
&& isStrWhiteSpace(data
[p
]))
211 else if (data
[p
] == '-') {
217 if ((radix
== 0 || radix
== 16) && length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X')) {
220 } else if (radix
== 0) {
221 if (p
< length
&& data
[p
] == '0')
227 if (radix
< 2 || radix
> 36)
230 int firstDigitPosition
= p
;
231 bool sawDigit
= false;
234 int digit
= parseDigit(data
[p
], radix
);
243 if (number
>= mantissaOverflowLowerBound
) {
244 // FIXME: It is incorrect to use UString::ascii() here because it's not thread-safe.
246 number
= WTF::strtod(s
.substr(firstDigitPosition
, p
- firstDigitPosition
).ascii(), 0);
247 else if (radix
== 2 || radix
== 4 || radix
== 8 || radix
== 16 || radix
== 32)
248 number
= parseIntOverflow(s
.substr(firstDigitPosition
, p
- firstDigitPosition
).ascii(), p
- firstDigitPosition
, radix
);
254 return sign
* number
;
257 static double parseFloat(const UString
& s
)
259 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
260 // Need to skip any whitespace and then one + or - sign.
261 int length
= s
.size();
262 const UChar
* data
= s
.data();
264 while (p
< length
&& isStrWhiteSpace(data
[p
]))
267 if (p
< length
&& (data
[p
] == '+' || data
[p
] == '-'))
270 if (length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X'))
273 // FIXME: UString::toDouble will ignore leading ASCII spaces, but we need to ignore
274 // other StrWhiteSpaceChar values as well.
275 return s
.toDouble(true /*tolerant*/, false /* NaN for empty string */);
278 JSValue JSC_HOST_CALL
globalFuncEval(ExecState
* exec
, JSObject
* function
, JSValue thisValue
, const ArgList
& args
)
280 JSObject
* thisObject
= thisValue
.toThisObject(exec
);
281 JSObject
* unwrappedObject
= thisObject
->unwrappedObject();
282 if (!unwrappedObject
->isGlobalObject() || static_cast<JSGlobalObject
*>(unwrappedObject
)->evalFunction() != function
)
283 return throwError(exec
, EvalError
, "The \"this\" value passed to eval must be the global object from which eval originated");
285 JSValue x
= args
.at(0);
289 UString s
= x
.toString(exec
);
291 LiteralParser
preparser(exec
, s
, LiteralParser::NonStrictJSON
);
292 if (JSValue parsedObject
= preparser
.tryLiteralParse())
295 RefPtr
<EvalExecutable
> eval
= EvalExecutable::create(exec
, makeSource(s
));
296 JSObject
* error
= eval
->compile(exec
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain().node());
298 return throwError(exec
, error
);
300 return exec
->interpreter()->execute(eval
.get(), exec
, thisObject
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain().node(), exec
->exceptionSlot());
303 JSValue JSC_HOST_CALL
globalFuncParseInt(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
305 JSValue value
= args
.at(0);
306 int32_t radix
= args
.at(1).toInt32(exec
);
308 if (radix
!= 0 && radix
!= 10)
309 return jsNumber(exec
, parseInt(value
.toString(exec
), radix
));
314 if (value
.isDouble()) {
315 double d
= value
.asDouble();
317 return jsNumber(exec
, (d
> 0) ? floor(d
) : ceil(d
));
318 if (isnan(d
) || isinf(d
))
320 return jsNumber(exec
, 0);
323 return jsNumber(exec
, parseInt(value
.toString(exec
), radix
));
326 JSValue JSC_HOST_CALL
globalFuncParseFloat(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
328 return jsNumber(exec
, parseFloat(args
.at(0).toString(exec
)));
331 JSValue JSC_HOST_CALL
globalFuncIsNaN(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
333 return jsBoolean(isnan(args
.at(0).toNumber(exec
)));
336 JSValue JSC_HOST_CALL
globalFuncIsFinite(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
338 double n
= args
.at(0).toNumber(exec
);
339 return jsBoolean(!isnan(n
) && !isinf(n
));
342 JSValue JSC_HOST_CALL
globalFuncDecodeURI(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
344 static const char do_not_unescape_when_decoding_URI
[] =
347 return decode(exec
, args
, do_not_unescape_when_decoding_URI
, true);
350 JSValue JSC_HOST_CALL
globalFuncDecodeURIComponent(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
352 return decode(exec
, args
, "", true);
355 JSValue JSC_HOST_CALL
globalFuncEncodeURI(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
357 static const char do_not_escape_when_encoding_URI
[] =
358 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
359 "abcdefghijklmnopqrstuvwxyz"
361 "!#$&'()*+,-./:;=?@_~";
363 return encode(exec
, args
, do_not_escape_when_encoding_URI
);
366 JSValue JSC_HOST_CALL
globalFuncEncodeURIComponent(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
368 static const char do_not_escape_when_encoding_URI_component
[] =
369 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
370 "abcdefghijklmnopqrstuvwxyz"
374 return encode(exec
, args
, do_not_escape_when_encoding_URI_component
);
377 JSValue JSC_HOST_CALL
globalFuncEscape(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
379 static const char do_not_escape
[] =
380 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
381 "abcdefghijklmnopqrstuvwxyz"
385 JSStringBuilder builder
;
386 UString str
= args
.at(0).toString(exec
);
387 const UChar
* c
= str
.data();
388 for (unsigned k
= 0; k
< str
.size(); k
++, c
++) {
392 snprintf(tmp
, sizeof(tmp
), "%%u%04X", u
);
394 } else if (u
!= 0 && strchr(do_not_escape
, static_cast<char>(u
)))
395 builder
.append(c
, 1);
398 snprintf(tmp
, sizeof(tmp
), "%%%02X", u
);
403 return builder
.build(exec
);
406 JSValue JSC_HOST_CALL
globalFuncUnescape(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
408 StringBuilder builder
;
409 UString str
= args
.at(0).toString(exec
);
411 int len
= str
.size();
413 const UChar
* c
= str
.data() + k
;
415 if (c
[0] == '%' && k
<= len
- 6 && c
[1] == 'u') {
416 if (isASCIIHexDigit(c
[2]) && isASCIIHexDigit(c
[3]) && isASCIIHexDigit(c
[4]) && isASCIIHexDigit(c
[5])) {
417 u
= Lexer::convertUnicode(c
[2], c
[3], c
[4], c
[5]);
421 } else if (c
[0] == '%' && k
<= len
- 3 && isASCIIHexDigit(c
[1]) && isASCIIHexDigit(c
[2])) {
422 u
= UChar(Lexer::convertHex(c
[1], c
[2]));
430 return jsString(exec
, builder
.build());
434 JSValue JSC_HOST_CALL
globalFuncJSCPrint(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
436 CString string
= args
.at(0).toString(exec
).UTF8String();
438 return jsUndefined();