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 "JSGlobalObject.h"
32 #include "Interpreter.h"
40 #include <wtf/ASCIICType.h>
41 #include <wtf/Assertions.h>
42 #include <wtf/MathExtras.h>
43 #include <wtf/unicode/UTF8.h>
46 using namespace Unicode
;
50 static JSValuePtr
encode(ExecState
* exec
, const ArgList
& args
, const char* doNotEscape
)
52 UString str
= args
.at(exec
, 0).toString(exec
);
53 CString cstr
= str
.UTF8String(true);
55 return throwError(exec
, URIError
, "String contained an illegal UTF-16 sequence.");
58 const char* p
= cstr
.c_str();
59 for (size_t k
= 0; k
< cstr
.size(); k
++, p
++) {
61 if (c
&& strchr(doNotEscape
, c
))
65 snprintf(tmp
, sizeof(tmp
), "%%%02X", static_cast<unsigned char>(c
));
69 return jsString(exec
, result
);
72 static JSValuePtr
decode(ExecState
* exec
, const ArgList
& args
, const char* doNotUnescape
, bool strict
)
75 UString str
= args
.at(exec
, 0).toString(exec
);
78 const UChar
* d
= str
.data();
81 const UChar
* p
= d
+ k
;
85 if (k
<= len
- 3 && isASCIIHexDigit(p
[1]) && isASCIIHexDigit(p
[2])) {
86 const char b0
= Lexer::convertHex(p
[1], p
[2]);
87 const int sequenceLen
= UTF8SequenceLength(b0
);
88 if (sequenceLen
!= 0 && k
<= len
- sequenceLen
* 3) {
89 charLen
= sequenceLen
* 3;
92 for (int i
= 1; i
< sequenceLen
; ++i
) {
93 const UChar
* q
= p
+ i
* 3;
94 if (q
[0] == '%' && isASCIIHexDigit(q
[1]) && isASCIIHexDigit(q
[2]))
95 sequence
[i
] = Lexer::convertHex(q
[1], q
[2]);
102 sequence
[sequenceLen
] = 0;
103 const int character
= decodeUTF8Sequence(sequence
);
104 if (character
< 0 || character
>= 0x110000)
106 else if (character
>= 0x10000) {
107 // Convert to surrogate pair.
108 result
.append(static_cast<UChar
>(0xD800 | ((character
- 0x10000) >> 10)));
109 u
= static_cast<UChar
>(0xDC00 | ((character
- 0x10000) & 0x3FF));
111 u
= static_cast<UChar
>(character
);
117 return throwError(exec
, URIError
);
118 // The only case where we don't use "strict" mode is the "unescape" function.
119 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
120 if (k
<= len
- 6 && p
[1] == 'u'
121 && isASCIIHexDigit(p
[2]) && isASCIIHexDigit(p
[3])
122 && isASCIIHexDigit(p
[4]) && isASCIIHexDigit(p
[5])) {
124 u
= Lexer::convertUnicode(p
[2], p
[3], p
[4], p
[5]);
127 if (charLen
&& (u
== 0 || u
>= 128 || !strchr(doNotUnescape
, u
))) {
135 return jsString(exec
, result
);
138 bool isStrWhiteSpace(UChar c
)
152 return c
> 0xff && isSeparatorSpace(c
);
156 static int parseDigit(unsigned short c
, int radix
)
160 if (c
>= '0' && c
<= '9')
162 else if (c
>= 'A' && c
<= 'Z')
163 digit
= c
- 'A' + 10;
164 else if (c
>= 'a' && c
<= 'z')
165 digit
= c
- 'a' + 10;
172 double parseIntOverflow(const char* s
, int length
, int radix
)
175 double radixMultiplier
= 1.0;
177 for (const char* p
= s
+ length
- 1; p
>= s
; p
--) {
178 if (radixMultiplier
== Inf
) {
184 int digit
= parseDigit(*p
, radix
);
185 number
+= digit
* radixMultiplier
;
188 radixMultiplier
*= radix
;
194 static double parseInt(const UString
& s
, int radix
)
196 int length
= s
.size();
197 const UChar
* data
= s
.data();
200 while (p
< length
&& isStrWhiteSpace(data
[p
]))
207 else if (data
[p
] == '-') {
213 if ((radix
== 0 || radix
== 16) && length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X')) {
216 } else if (radix
== 0) {
217 if (p
< length
&& data
[p
] == '0')
223 if (radix
< 2 || radix
> 36)
226 int firstDigitPosition
= p
;
227 bool sawDigit
= false;
230 int digit
= parseDigit(data
[p
], radix
);
239 if (number
>= mantissaOverflowLowerBound
) {
241 number
= WTF::strtod(s
.substr(firstDigitPosition
, p
- firstDigitPosition
).ascii(), 0);
242 else if (radix
== 2 || radix
== 4 || radix
== 8 || radix
== 16 || radix
== 32)
243 number
= parseIntOverflow(s
.substr(firstDigitPosition
, p
- firstDigitPosition
).ascii(), p
- firstDigitPosition
, radix
);
249 return sign
* number
;
252 static double parseFloat(const UString
& s
)
254 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
255 // Need to skip any whitespace and then one + or - sign.
256 int length
= s
.size();
257 const UChar
* data
= s
.data();
259 while (p
< length
&& isStrWhiteSpace(data
[p
]))
262 if (p
< length
&& (data
[p
] == '+' || data
[p
] == '-'))
265 if (length
- p
>= 2 && data
[p
] == '0' && (data
[p
+ 1] == 'x' || data
[p
+ 1] == 'X'))
268 return s
.toDouble(true /*tolerant*/, false /* NaN for empty string */);
271 JSValuePtr
globalFuncEval(ExecState
* exec
, JSObject
* function
, JSValuePtr thisValue
, const ArgList
& args
)
273 JSObject
* thisObject
= thisValue
.toThisObject(exec
);
274 JSObject
* unwrappedObject
= thisObject
->unwrappedObject();
275 if (!unwrappedObject
->isGlobalObject() || static_cast<JSGlobalObject
*>(unwrappedObject
)->evalFunction() != function
)
276 return throwError(exec
, EvalError
, "The \"this\" value passed to eval must be the global object from which eval originated");
278 JSValuePtr x
= args
.at(exec
, 0);
282 UString s
= x
.toString(exec
);
287 SourceCode source
= makeSource(s
);
288 RefPtr
<EvalNode
> evalNode
= exec
->globalData().parser
->parse
<EvalNode
>(exec
, exec
->dynamicGlobalObject()->debugger(), source
, &errLine
, &errMsg
);
291 return throwError(exec
, SyntaxError
, errMsg
, errLine
, source
.provider()->asID(), NULL
);
293 return exec
->interpreter()->execute(evalNode
.get(), exec
, thisObject
, static_cast<JSGlobalObject
*>(unwrappedObject
)->globalScopeChain().node(), exec
->exceptionSlot());
296 JSValuePtr
globalFuncParseInt(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
298 JSValuePtr value
= args
.at(exec
, 0);
299 int32_t radix
= args
.at(exec
, 1).toInt32(exec
);
301 if (value
.isNumber() && (radix
== 0 || radix
== 10)) {
302 if (value
.isInt32Fast())
304 double d
= value
.uncheckedGetNumber();
306 return jsNumber(exec
, (d
> 0) ? floor(d
) : ceil(d
));
307 if (isnan(d
) || isinf(d
))
308 return jsNaN(&exec
->globalData());
312 return jsNumber(exec
, parseInt(value
.toString(exec
), radix
));
315 JSValuePtr
globalFuncParseFloat(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
317 return jsNumber(exec
, parseFloat(args
.at(exec
, 0).toString(exec
)));
320 JSValuePtr
globalFuncIsNaN(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
322 return jsBoolean(isnan(args
.at(exec
, 0).toNumber(exec
)));
325 JSValuePtr
globalFuncIsFinite(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
327 double n
= args
.at(exec
, 0).toNumber(exec
);
328 return jsBoolean(!isnan(n
) && !isinf(n
));
331 JSValuePtr
globalFuncDecodeURI(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
333 static const char do_not_unescape_when_decoding_URI
[] =
336 return decode(exec
, args
, do_not_unescape_when_decoding_URI
, true);
339 JSValuePtr
globalFuncDecodeURIComponent(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
341 return decode(exec
, args
, "", true);
344 JSValuePtr
globalFuncEncodeURI(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
346 static const char do_not_escape_when_encoding_URI
[] =
347 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
348 "abcdefghijklmnopqrstuvwxyz"
350 "!#$&'()*+,-./:;=?@_~";
352 return encode(exec
, args
, do_not_escape_when_encoding_URI
);
355 JSValuePtr
globalFuncEncodeURIComponent(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
357 static const char do_not_escape_when_encoding_URI_component
[] =
358 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
359 "abcdefghijklmnopqrstuvwxyz"
363 return encode(exec
, args
, do_not_escape_when_encoding_URI_component
);
366 JSValuePtr
globalFuncEscape(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
368 static const char do_not_escape
[] =
369 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
370 "abcdefghijklmnopqrstuvwxyz"
376 UString str
= args
.at(exec
, 0).toString(exec
);
377 const UChar
* c
= str
.data();
378 for (int k
= 0; k
< str
.size(); k
++, c
++) {
382 snprintf(tmp
, sizeof(tmp
), "%%u%04X", u
);
384 } else if (u
!= 0 && strchr(do_not_escape
, static_cast<char>(u
)))
388 snprintf(tmp
, sizeof(tmp
), "%%%02X", u
);
394 return jsString(exec
, result
);
397 JSValuePtr
globalFuncUnescape(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
400 UString str
= args
.at(exec
, 0).toString(exec
);
402 int len
= str
.size();
404 const UChar
* c
= str
.data() + k
;
406 if (c
[0] == '%' && k
<= len
- 6 && c
[1] == 'u') {
407 if (Lexer::isHexDigit(c
[2]) && Lexer::isHexDigit(c
[3]) && Lexer::isHexDigit(c
[4]) && Lexer::isHexDigit(c
[5])) {
408 u
= Lexer::convertUnicode(c
[2], c
[3], c
[4], c
[5]);
412 } else if (c
[0] == '%' && k
<= len
- 3 && Lexer::isHexDigit(c
[1]) && Lexer::isHexDigit(c
[2])) {
413 u
= UChar(Lexer::convertHex(c
[1], c
[2]));
421 return jsString(exec
, result
);
425 JSValuePtr
globalFuncJSCPrint(ExecState
* exec
, JSObject
*, JSValuePtr
, const ArgList
& args
)
427 CStringBuffer string
;
428 args
.at(exec
, 0).toString(exec
).getCString(string
);
430 return jsUndefined();