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