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