b0a68d5534e680e1a4e760b0838d2149a60480f9
[WebKit.git] / JavaScriptCore / runtime / JSGlobalObjectFunctions.cpp
1 /*
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
7  *
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.
12  *
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.
17  *
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.
22  *
23  */
24
25 #include "config.h"
26 #include "JSGlobalObjectFunctions.h"
27
28 #include "CallFrame.h"
29 #include "GlobalEvalFunction.h"
30 #include "Interpreter.h"
31 #include "JSGlobalObject.h"
32 #include "JSString.h"
33 #include "Lexer.h"
34 #include "LiteralParser.h"
35 #include "Nodes.h"
36 #include "Parser.h"
37 #include "StringBuilder.h"
38 #include "dtoa.h"
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <wtf/ASCIICType.h>
43 #include <wtf/Assertions.h>
44 #include <wtf/MathExtras.h>
45 #include <wtf/unicode/UTF8.h>
46
47 using namespace WTF;
48 using namespace Unicode;
49
50 namespace JSC {
51
52 static JSValue encode(ExecState* exec, const ArgList& args, const char* doNotEscape)
53 {
54     UString str = args.at(0).toString(exec);
55     CString cstr = str.UTF8String(true);
56     if (!cstr.c_str())
57         return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
58
59     StringBuilder builder;
60     const char* p = cstr.c_str();
61     for (size_t k = 0; k < cstr.size(); k++, p++) {
62         char c = *p;
63         if (c && strchr(doNotEscape, c))
64             builder.append(c);
65         else {
66             char tmp[4];
67             snprintf(tmp, 4, "%%%02X", static_cast<unsigned char>(c));
68             builder.append((const char*)tmp);
69         }
70     }
71     return jsString(exec, builder.release());
72 }
73
74 static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict)
75 {
76     StringBuilder builder;
77     UString str = args.at(0).toString(exec);
78     int k = 0;
79     int len = str.size();
80     const UChar* d = str.data();
81     UChar u = 0;
82     while (k < len) {
83         const UChar* p = d + k;
84         UChar c = *p;
85         if (c == '%') {
86             int charLen = 0;
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;
92                     char sequence[5];
93                     sequence[0] = b0;
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]);
98                         else {
99                             charLen = 0;
100                             break;
101                         }
102                     }
103                     if (charLen != 0) {
104                         sequence[sequenceLen] = 0;
105                         const int character = decodeUTF8Sequence(sequence);
106                         if (character < 0 || character >= 0x110000)
107                             charLen = 0;
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));
112                         } else
113                             u = static_cast<UChar>(character);
114                     }
115                 }
116             }
117             if (charLen == 0) {
118                 if (strict)
119                     return throwError(exec, URIError);
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])) {
125                     charLen = 6;
126                     u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
127                 }
128             }
129             if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
130                 c = u;
131                 k += charLen - 1;
132             }
133         }
134         k++;
135         builder.append(c);
136     }
137     return jsString(exec, builder.release());
138 }
139
140 bool isStrWhiteSpace(UChar c)
141 {
142     switch (c) {
143         case 0x0009:
144         case 0x000A:
145         case 0x000B:
146         case 0x000C:
147         case 0x000D:
148         case 0x0020:
149         case 0x00A0:
150         case 0x2028:
151         case 0x2029:
152             return true;
153         default:
154             return c > 0xff && isSeparatorSpace(c);
155     }
156 }
157
158 static int parseDigit(unsigned short c, int radix)
159 {
160     int digit = -1;
161
162     if (c >= '0' && c <= '9')
163         digit = c - '0';
164     else if (c >= 'A' && c <= 'Z')
165         digit = c - 'A' + 10;
166     else if (c >= 'a' && c <= 'z')
167         digit = c - 'a' + 10;
168
169     if (digit >= radix)
170         return -1;
171     return digit;
172 }
173
174 double parseIntOverflow(const char* s, int length, int radix)
175 {
176     double number = 0.0;
177     double radixMultiplier = 1.0;
178
179     for (const char* p = s + length - 1; p >= s; p--) {
180         if (radixMultiplier == Inf) {
181             if (*p != '0') {
182                 number = Inf;
183                 break;
184             }
185         } else {
186             int digit = parseDigit(*p, radix);
187             number += digit * radixMultiplier;
188         }
189
190         radixMultiplier *= radix;
191     }
192
193     return number;
194 }
195
196 static double parseInt(const UString& s, int radix)
197 {
198     int length = s.size();
199     const UChar* data = s.data();
200     int p = 0;
201
202     while (p < length && isStrWhiteSpace(data[p]))
203         ++p;
204
205     double sign = 1;
206     if (p < length) {
207         if (data[p] == '+')
208             ++p;
209         else if (data[p] == '-') {
210             sign = -1;
211             ++p;
212         }
213     }
214
215     if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) {
216         radix = 16;
217         p += 2;
218     } else if (radix == 0) {
219         if (p < length && data[p] == '0')
220             radix = 8;
221         else
222             radix = 10;
223     }
224
225     if (radix < 2 || radix > 36)
226         return NaN;
227
228     int firstDigitPosition = p;
229     bool sawDigit = false;
230     double number = 0;
231     while (p < length) {
232         int digit = parseDigit(data[p], radix);
233         if (digit == -1)
234             break;
235         sawDigit = true;
236         number *= radix;
237         number += digit;
238         ++p;
239     }
240
241     if (number >= mantissaOverflowLowerBound) {
242         if (radix == 10)
243             number = WTF::strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
244         else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
245             number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
246     }
247
248     if (!sawDigit)
249         return NaN;
250
251     return sign * number;
252 }
253
254 static double parseFloat(const UString& s)
255 {
256     // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
257     // Need to skip any whitespace and then one + or - sign.
258     int length = s.size();
259     const UChar* data = s.data();
260     int p = 0;
261     while (p < length && isStrWhiteSpace(data[p]))
262         ++p;
263
264     if (p < length && (data[p] == '+' || data[p] == '-'))
265         ++p;
266
267     if (length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X'))
268         return 0;
269
270     return s.toDouble(true /*tolerant*/, false /* NaN for empty string */);
271 }
272
273 JSValue JSC_HOST_CALL globalFuncEval(ExecState* exec, JSObject* function, JSValue thisValue, const ArgList& args)
274 {
275     JSObject* thisObject = thisValue.toThisObject(exec);
276     JSObject* unwrappedObject = thisObject->unwrappedObject();
277     if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != function)
278         return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
279
280     JSValue x = args.at(0);
281     if (!x.isString())
282         return x;
283
284     UString s = x.toString(exec);
285
286     LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON);
287     if (JSValue parsedObject = preparser.tryLiteralParse())
288         return parsedObject;
289
290     RefPtr<EvalExecutable> eval = EvalExecutable::create(exec, makeSource(s));
291     JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node());
292     if (error)
293         return throwError(exec, error);
294
295     return exec->interpreter()->execute(eval.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot());
296 }
297
298 JSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec, JSObject*, JSValue, const ArgList& args)
299 {
300     JSValue value = args.at(0);
301     int32_t radix = args.at(1).toInt32(exec);
302
303     if (radix != 0 && radix != 10)
304         return jsNumber(exec, parseInt(value.toString(exec), radix));
305
306     if (value.isInt32())
307         return value;
308
309     if (value.isDouble()) {
310         double d = value.asDouble();
311         if (isfinite(d))
312             return jsNumber(exec, (d > 0) ? floor(d) : ceil(d));
313         if (isnan(d) || isinf(d))
314             return jsNaN(exec);
315         return jsNumber(exec, 0);
316     }
317
318     return jsNumber(exec, parseInt(value.toString(exec), radix));
319 }
320
321 JSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec, JSObject*, JSValue, const ArgList& args)
322 {
323     return jsNumber(exec, parseFloat(args.at(0).toString(exec)));
324 }
325
326 JSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec, JSObject*, JSValue, const ArgList& args)
327 {
328     return jsBoolean(isnan(args.at(0).toNumber(exec)));
329 }
330
331 JSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec, JSObject*, JSValue, const ArgList& args)
332 {
333     double n = args.at(0).toNumber(exec);
334     return jsBoolean(!isnan(n) && !isinf(n));
335 }
336
337 JSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec, JSObject*, JSValue, const ArgList& args)
338 {
339     static const char do_not_unescape_when_decoding_URI[] =
340         "#$&+,/:;=?@";
341
342     return decode(exec, args, do_not_unescape_when_decoding_URI, true);
343 }
344
345 JSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args)
346 {
347     return decode(exec, args, "", true);
348 }
349
350 JSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue, const ArgList& args)
351 {
352     static const char do_not_escape_when_encoding_URI[] =
353         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
354         "abcdefghijklmnopqrstuvwxyz"
355         "0123456789"
356         "!#$&'()*+,-./:;=?@_~";
357
358     return encode(exec, args, do_not_escape_when_encoding_URI);
359 }
360
361 JSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args)
362 {
363     static const char do_not_escape_when_encoding_URI_component[] =
364         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
365         "abcdefghijklmnopqrstuvwxyz"
366         "0123456789"
367         "!'()*-._~";
368
369     return encode(exec, args, do_not_escape_when_encoding_URI_component);
370 }
371
372 JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, const ArgList& args)
373 {
374     static const char do_not_escape[] =
375         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
376         "abcdefghijklmnopqrstuvwxyz"
377         "0123456789"
378         "*+-./@_";
379
380     StringBuilder builder;
381     UString s;
382     UString str = args.at(0).toString(exec);
383     const UChar* c = str.data();
384     for (int k = 0; k < str.size(); k++, c++) {
385         int u = c[0];
386         if (u > 255) {
387             char tmp[7];
388             sprintf(tmp, "%%u%04X", u);
389             s = UString(tmp);
390         } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
391             s = UString(c, 1);
392         else {
393             char tmp[4];
394             sprintf(tmp, "%%%02X", u);
395             s = UString(tmp);
396         }
397         builder.append(s);
398     }
399
400     return jsString(exec, builder.release());
401 }
402
403 JSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec, JSObject*, JSValue, const ArgList& args)
404 {
405     StringBuilder builder;
406     UString str = args.at(0).toString(exec);
407     int k = 0;
408     int len = str.size();
409     while (k < len) {
410         const UChar* c = str.data() + k;
411         UChar u;
412         if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
413             if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
414                 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
415                 c = &u;
416                 k += 5;
417             }
418         } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
419             u = UChar(Lexer::convertHex(c[1], c[2]));
420             c = &u;
421             k += 2;
422         }
423         k++;
424         builder.append(*c);
425     }
426
427     return jsString(exec, builder.release());
428 }
429
430 #ifndef NDEBUG
431 JSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args)
432 {
433     CStringBuffer string;
434     args.at(0).toString(exec).getCString(string);
435     puts(string.data());
436     return jsUndefined();
437 }
438 #endif
439
440 } // namespace JSC