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"
34 #include "LiteralParser.h"
37 #include "StringBuilder.h"
38 #include "StringExtras.h"
43 #include <wtf/ASCIICType.h>
44 #include <wtf/Assertions.h>
45 #include <wtf/MathExtras.h>
46 #include <wtf/unicode/UTF8.h>
49 using namespace Unicode
;
53 static JSValue
encode(ExecState
* exec
, const ArgList
& args
, const char* doNotEscape
)
55 UString str
= args
.at(0).toString(exec
);
56 CString cstr
= str
.UTF8String(true);
58 return throwError(exec
, URIError
, "String contained an illegal UTF-16 sequence.");
60 StringBuilder builder
;
61 const char* p
= cstr
.c_str();
62 for (size_t k
= 0; k
< cstr
.size(); k
++, p
++) {
64 if (c
&& strchr(doNotEscape
, c
))
68 snprintf(tmp
, sizeof(tmp
), "%%%02X", static_cast<unsigned char>(c
));
69 builder
.append((const char*)tmp
);
72 return jsString(exec
, builder
.release());
75 static JSValue
decode(ExecState
* exec
, const ArgList
& args
, const char* doNotUnescape
, bool strict
)
77 StringBuilder builder
;
78 UString str
= args
.at(0).toString(exec
);
81 const UChar
* d
= str
.data();
84 const UChar
* p
= d
+ k
;
88 if (k
<= len
- 3 && isASCIIHexDigit(p
[1]) && isASCIIHexDigit(p
[2])) {
89 const char b0
= Lexer::convertHex(p
[1], p
[2]);
90 const int sequenceLen
= UTF8SequenceLength(b0
);
91 if (sequenceLen
!= 0 && k
<= len
- sequenceLen
* 3) {
92 charLen
= sequenceLen
* 3;
95 for (int i
= 1; i
< sequenceLen
; ++i
) {
96 const UChar
* q
= p
+ i
* 3;
97 if (q
[0] == '%' && isASCIIHexDigit(q
[1]) && isASCIIHexDigit(q
[2]))
98 sequence
[i
] = Lexer::convertHex(q
[1], q
[2]);
105 sequence
[sequenceLen
] = 0;
106 const int character
= decodeUTF8Sequence(sequence
);
107 if (character
< 0 || character
>= 0x110000)
109 else if (character
>= 0x10000) {
110 // Convert to surrogate pair.
111 builder
.append(static_cast<UChar
>(0xD800 | ((character
- 0x10000) >> 10)));
112 u
= static_cast<UChar
>(0xDC00 | ((character
- 0x10000) & 0x3FF));
114 u
= static_cast<UChar
>(character
);
120 return throwError(exec
, URIError
);
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.
123 if (k
<= len
- 6 && p
[1] == 'u'
124 && isASCIIHexDigit(p
[2]) && isASCIIHexDigit(p
[3])
125 && isASCIIHexDigit(p
[4]) && isASCIIHexDigit(p
[5])) {
127 u
= Lexer::convertUnicode(p
[2], p
[3], p
[4], p
[5]);
130 if (charLen
&& (u
== 0 || u
>= 128 || !strchr(doNotUnescape
, u
))) {
138 return jsString(exec
, builder
.release());
141 bool isStrWhiteSpace(UChar c
)
155 return c
> 0xff && isSeparatorSpace(c
);
159 static int parseDigit(unsigned short c
, int radix
)
163 if (c
>= '0' && c
<= '9')
165 else if (c
>= 'A' && c
<= 'Z')
166 digit
= c
- 'A' + 10;
167 else if (c
>= 'a' && c
<= 'z')
168 digit
= c
- 'a' + 10;
175 double parseIntOverflow(const char* s
, int length
, int radix
)
178 double radixMultiplier
= 1.0;
180 for (const char* p
= s
+ length
- 1; p
>= s
; p
--) {
181 if (radixMultiplier
== Inf
) {
187 int digit
= parseDigit(*p
, radix
);
188 number
+= digit
* radixMultiplier
;
191 radixMultiplier
*= radix
;
197 static double parseInt(const UString
& s
, int radix
)
199 int length
= s
.size();
200 const UChar
* data
= s
.data();
203 while (p
< length
&& isStrWhiteSpace(data
[p
]))
210 else if (data
[p
] == '-') {
216 if ((radix
== 0 || radix
== 16) && length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X')) {
219 } else if (radix
== 0) {
220 if (p
< length
&& data
[p
] == '0')
226 if (radix
< 2 || radix
> 36)
229 int firstDigitPosition
= p
;
230 bool sawDigit
= false;
233 int digit
= parseDigit(data
[p
], radix
);
242 if (number
>= mantissaOverflowLowerBound
) {
244 number
= WTF::strtod(s
.substr(firstDigitPosition
, p
- firstDigitPosition
).ascii(), 0);
245 else if (radix
== 2 || radix
== 4 || radix
== 8 || radix
== 16 || radix
== 32)
246 number
= parseIntOverflow(s
.substr(firstDigitPosition
, p
- firstDigitPosition
).ascii(), p
- firstDigitPosition
, radix
);
252 return sign
* number
;
255 static double parseFloat(const UString
& s
)
257 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
258 // Need to skip any whitespace and then one + or - sign.
259 int length
= s
.size();
260 const UChar
* data
= s
.data();
262 while (p
< length
&& isStrWhiteSpace(data
[p
]))
265 if (p
< length
&& (data
[p
] == '+' || data
[p
] == '-'))
268 if (length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X'))
271 return s
.toDouble(true /*tolerant*/, false /* NaN for empty string */);
274 JSValue JSC_HOST_CALL
globalFuncEval(ExecState
* exec
, JSObject
* function
, JSValue thisValue
, const ArgList
& args
)
276 JSObject
* thisObject
= thisValue
.toThisObject(exec
);
277 JSObject
* unwrappedObject
= thisObject
->unwrappedObject();
278 if (!unwrappedObject
->isGlobalObject() || static_cast<JSGlobalObject
*>(unwrappedObject
)->evalFunction() != function
)
279 return throwError(exec
, EvalError
, "The \"this\" value passed to eval must be the global object from which eval originated");
281 JSValue x
= args
.at(0);
285 UString s
= x
.toString(exec
);
287 LiteralParser
preparser(exec
, s
, LiteralParser::NonStrictJSON
);
288 if (JSValue parsedObject
= preparser
.tryLiteralParse())
291 RefPtr
<EvalExecutable
> eval
= EvalExecutable::create(exec
, makeSource(s
));
292 JSObject
* error
= eval
->compile(exec
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain().node());
294 return throwError(exec
, error
);
296 return exec
->interpreter()->execute(eval
.get(), exec
, thisObject
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain().node(), exec
->exceptionSlot());
299 JSValue JSC_HOST_CALL
globalFuncParseInt(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
301 JSValue value
= args
.at(0);
302 int32_t radix
= args
.at(1).toInt32(exec
);
304 if (radix
!= 0 && radix
!= 10)
305 return jsNumber(exec
, parseInt(value
.toString(exec
), radix
));
310 if (value
.isDouble()) {
311 double d
= value
.asDouble();
313 return jsNumber(exec
, (d
> 0) ? floor(d
) : ceil(d
));
314 if (isnan(d
) || isinf(d
))
316 return jsNumber(exec
, 0);
319 return jsNumber(exec
, parseInt(value
.toString(exec
), radix
));
322 JSValue JSC_HOST_CALL
globalFuncParseFloat(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
324 return jsNumber(exec
, parseFloat(args
.at(0).toString(exec
)));
327 JSValue JSC_HOST_CALL
globalFuncIsNaN(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
329 return jsBoolean(isnan(args
.at(0).toNumber(exec
)));
332 JSValue JSC_HOST_CALL
globalFuncIsFinite(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
334 double n
= args
.at(0).toNumber(exec
);
335 return jsBoolean(!isnan(n
) && !isinf(n
));
338 JSValue JSC_HOST_CALL
globalFuncDecodeURI(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
340 static const char do_not_unescape_when_decoding_URI
[] =
343 return decode(exec
, args
, do_not_unescape_when_decoding_URI
, true);
346 JSValue JSC_HOST_CALL
globalFuncDecodeURIComponent(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
348 return decode(exec
, args
, "", true);
351 JSValue JSC_HOST_CALL
globalFuncEncodeURI(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
353 static const char do_not_escape_when_encoding_URI
[] =
354 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
355 "abcdefghijklmnopqrstuvwxyz"
357 "!#$&'()*+,-./:;=?@_~";
359 return encode(exec
, args
, do_not_escape_when_encoding_URI
);
362 JSValue JSC_HOST_CALL
globalFuncEncodeURIComponent(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
364 static const char do_not_escape_when_encoding_URI_component
[] =
365 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
366 "abcdefghijklmnopqrstuvwxyz"
370 return encode(exec
, args
, do_not_escape_when_encoding_URI_component
);
373 JSValue JSC_HOST_CALL
globalFuncEscape(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
375 static const char do_not_escape
[] =
376 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
377 "abcdefghijklmnopqrstuvwxyz"
381 StringBuilder builder
;
383 UString str
= args
.at(0).toString(exec
);
384 const UChar
* c
= str
.data();
385 for (int k
= 0; k
< str
.size(); k
++, c
++) {
389 snprintf(tmp
, sizeof(tmp
), "%%u%04X", u
);
391 } else if (u
!= 0 && strchr(do_not_escape
, static_cast<char>(u
)))
395 snprintf(tmp
, sizeof(tmp
), "%%%02X", u
);
401 return jsString(exec
, builder
.release());
404 JSValue JSC_HOST_CALL
globalFuncUnescape(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
406 StringBuilder builder
;
407 UString str
= args
.at(0).toString(exec
);
409 int len
= str
.size();
411 const UChar
* c
= str
.data() + k
;
413 if (c
[0] == '%' && k
<= len
- 6 && c
[1] == 'u') {
414 if (isASCIIHexDigit(c
[2]) && isASCIIHexDigit(c
[3]) && isASCIIHexDigit(c
[4]) && isASCIIHexDigit(c
[5])) {
415 u
= Lexer::convertUnicode(c
[2], c
[3], c
[4], c
[5]);
419 } else if (c
[0] == '%' && k
<= len
- 3 && isASCIIHexDigit(c
[1]) && isASCIIHexDigit(c
[2])) {
420 u
= UChar(Lexer::convertHex(c
[1], c
[2]));
428 return jsString(exec
, builder
.release());
432 JSValue JSC_HOST_CALL
globalFuncJSCPrint(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
434 CStringBuffer string
;
435 args
.at(0).toString(exec
).getCString(string
);
437 return jsUndefined();