source: webkit/trunk/JavaScriptCore/kjs/JSGlobalObjectFunctions.cpp@ 36847

Last change on this file since 36847 was 36434, checked in by [email protected], 17 years ago

2008-09-15 Cameron Zwarich <[email protected]>

Reviewed by Maciej Stachowiak.

Bug 20851: REGRESSION (r36410): fast/js/kde/GlobalObject.html fails
<https://bugs.webkit.org/show_bug.cgi?id=20851>

r36410 introduced an optimization for parseInt() that is incorrect when
its argument is larger than the range of a 32-bit integer. If the
argument is a number that is not an immediate integer, then the correct
behaviour is to return the floor of its value, unless it is an infinite
value, in which case the correct behaviour is to return 0.

JavaScriptCore:

  • kjs/JSGlobalObjectFunctions.cpp: (JSC::globalFuncParseInt):

LayoutTests:

  • fast/js/numeric-conversion-expected.txt:
  • fast/js/resources/numeric-conversion.js:
  • Property svn:eol-style set to native
File size: 13.1 KB
Line 
1/*
2 * Copyright (C) 1999-2002 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich ([email protected])
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 "ExecState.h"
29#include "GlobalEvalFunction.h"
30#include "JSGlobalObject.h"
31#include "JSString.h"
32#include "Machine.h"
33#include "Parser.h"
34#include "dtoa.h"
35#include "lexer.h"
36#include "nodes.h"
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <wtf/ASCIICType.h>
42#include <wtf/Assertions.h>
43#include <wtf/MathExtras.h>
44#include <wtf/unicode/UTF8.h>
45
46using namespace WTF;
47using namespace Unicode;
48
49namespace JSC {
50
51static JSValue* encode(ExecState* exec, const ArgList& args, const char* doNotEscape)
52{
53 UString str = args.at(exec, 0)->toString(exec);
54 CString cstr = str.UTF8String(true);
55 if (!cstr.c_str())
56 return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
57
58 UString result = "";
59 const char* p = cstr.c_str();
60 for (size_t k = 0; k < cstr.size(); k++, p++) {
61 char c = *p;
62 if (c && strchr(doNotEscape, c))
63 result.append(c);
64 else {
65 char tmp[4];
66 sprintf(tmp, "%%%02X", static_cast<unsigned char>(c));
67 result += tmp;
68 }
69 }
70 return jsString(exec, result);
71}
72
73static JSValue* decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict)
74{
75 UString result = "";
76 UString str = args.at(exec, 0)->toString(exec);
77 int k = 0;
78 int len = str.size();
79 const UChar* d = str.data();
80 UChar u = 0;
81 while (k < len) {
82 const UChar* p = d + k;
83 UChar c = *p;
84 if (c == '%') {
85 int charLen = 0;
86 if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
87 const char b0 = Lexer::convertHex(p[1], p[2]);
88 const int sequenceLen = UTF8SequenceLength(b0);
89 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
90 charLen = sequenceLen * 3;
91 char sequence[5];
92 sequence[0] = b0;
93 for (int i = 1; i < sequenceLen; ++i) {
94 const UChar* q = p + i * 3;
95 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
96 sequence[i] = Lexer::convertHex(q[1], q[2]);
97 else {
98 charLen = 0;
99 break;
100 }
101 }
102 if (charLen != 0) {
103 sequence[sequenceLen] = 0;
104 const int character = decodeUTF8Sequence(sequence);
105 if (character < 0 || character >= 0x110000)
106 charLen = 0;
107 else if (character >= 0x10000) {
108 // Convert to surrogate pair.
109 result.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
110 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
111 } else
112 u = static_cast<UChar>(character);
113 }
114 }
115 }
116 if (charLen == 0) {
117 if (strict)
118 return throwError(exec, URIError);
119 // The only case where we don't use "strict" mode is the "unescape" function.
120 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
121 if (k <= len - 6 && p[1] == 'u'
122 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
123 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
124 charLen = 6;
125 u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
126 }
127 }
128 if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
129 c = u;
130 k += charLen - 1;
131 }
132 }
133 k++;
134 result.append(c);
135 }
136 return jsString(exec, result);
137}
138
139bool isStrWhiteSpace(UChar c)
140{
141 switch (c) {
142 case 0x0009:
143 case 0x000A:
144 case 0x000B:
145 case 0x000C:
146 case 0x000D:
147 case 0x0020:
148 case 0x00A0:
149 case 0x2028:
150 case 0x2029:
151 return true;
152 default:
153 return c > 0xff && isSeparatorSpace(c);
154 }
155}
156
157static int parseDigit(unsigned short c, int radix)
158{
159 int digit = -1;
160
161 if (c >= '0' && c <= '9')
162 digit = c - '0';
163 else if (c >= 'A' && c <= 'Z')
164 digit = c - 'A' + 10;
165 else if (c >= 'a' && c <= 'z')
166 digit = c - 'a' + 10;
167
168 if (digit >= radix)
169 return -1;
170 return digit;
171}
172
173double parseIntOverflow(const char* s, int length, int radix)
174{
175 double number = 0.0;
176 double radixMultiplier = 1.0;
177
178 for (const char* p = s + length - 1; p >= s; p--) {
179 if (radixMultiplier == Inf) {
180 if (*p != '0') {
181 number = Inf;
182 break;
183 }
184 } else {
185 int digit = parseDigit(*p, radix);
186 number += digit * radixMultiplier;
187 }
188
189 radixMultiplier *= radix;
190 }
191
192 return number;
193}
194
195static double parseInt(const UString& s, int radix)
196{
197 int length = s.size();
198 int p = 0;
199
200 while (p < length && isStrWhiteSpace(s[p]))
201 ++p;
202
203 double sign = 1;
204 if (p < length) {
205 if (s[p] == '+')
206 ++p;
207 else if (s[p] == '-') {
208 sign = -1;
209 ++p;
210 }
211 }
212
213 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
214 radix = 16;
215 p += 2;
216 } else if (radix == 0) {
217 if (p < length && s[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(s[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 = 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
252static 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 int p = 0;
258 while (p < length && isStrWhiteSpace(s[p]))
259 ++p;
260
261 if (p < length && (s[p] == '+' || s[p] == '-'))
262 ++p;
263
264 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X'))
265 return 0;
266
267 return s.toDouble(true /*tolerant*/, false /* NaN for empty string */);
268}
269
270JSValue* globalFuncEval(ExecState* exec, JSObject* function, JSValue* thisValue, const ArgList& args)
271{
272 JSObject* thisObject = thisValue->toThisObject(exec);
273 JSGlobalObject* globalObject = thisObject->toGlobalObject(exec);
274 if (!globalObject || globalObject->evalFunction() != function)
275 return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
276
277 JSValue* x = args.at(exec, 0);
278 if (!x->isString())
279 return x;
280
281 UString s = x->toString(exec);
282
283 int sourceId;
284 int errLine;
285 UString errMsg;
286
287 RefPtr<EvalNode> evalNode = exec->parser()->parse<EvalNode>(exec, UString(), 1, UStringSourceProvider::create(s), &sourceId, &errLine, &errMsg);
288
289 if (!evalNode)
290 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
291
292 return exec->machine()->execute(evalNode.get(), exec, thisObject, globalObject->globalScopeChain().node(), exec->exceptionSlot());
293}
294
295JSValue* globalFuncParseInt(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
296{
297 JSValue* value = args.at(exec, 0);
298 int32_t radix = args.at(exec, 1)->toInt32(exec);
299
300 if (value->isNumber() && (radix == 0 || radix == 10)) {
301 if (JSImmediate::isImmediate(value))
302 return value;
303 double d = value->uncheckedGetNumber();
304 if (!isfinite(d))
305 return JSImmediate::zeroImmediate();
306 return jsNumber(exec, floor(d));
307 }
308
309 return jsNumber(exec, parseInt(value->toString(exec), radix));
310}
311
312JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
313{
314 return jsNumber(exec, parseFloat(args.at(exec, 0)->toString(exec)));
315}
316
317JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
318{
319 return jsBoolean(isnan(args.at(exec, 0)->toNumber(exec)));
320}
321
322JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
323{
324 double n = args.at(exec, 0)->toNumber(exec);
325 return jsBoolean(!isnan(n) && !isinf(n));
326}
327
328JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
329{
330 static const char do_not_unescape_when_decoding_URI[] =
331 "#$&+,/:;=?@";
332
333 return decode(exec, args, do_not_unescape_when_decoding_URI, true);
334}
335
336JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
337{
338 return decode(exec, args, "", true);
339}
340
341JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
342{
343 static const char do_not_escape_when_encoding_URI[] =
344 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
345 "abcdefghijklmnopqrstuvwxyz"
346 "0123456789"
347 "!#$&'()*+,-./:;=?@_~";
348
349 return encode(exec, args, do_not_escape_when_encoding_URI);
350}
351
352JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
353{
354 static const char do_not_escape_when_encoding_URI_component[] =
355 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
356 "abcdefghijklmnopqrstuvwxyz"
357 "0123456789"
358 "!'()*-._~";
359
360 return encode(exec, args, do_not_escape_when_encoding_URI_component);
361}
362
363JSValue* globalFuncEscape(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
364{
365 static const char do_not_escape[] =
366 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
367 "abcdefghijklmnopqrstuvwxyz"
368 "0123456789"
369 "*+-./@_";
370
371 UString result = "";
372 UString s;
373 UString str = args.at(exec, 0)->toString(exec);
374 const UChar* c = str.data();
375 for (int k = 0; k < str.size(); k++, c++) {
376 int u = c[0];
377 if (u > 255) {
378 char tmp[7];
379 sprintf(tmp, "%%u%04X", u);
380 s = UString(tmp);
381 } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
382 s = UString(c, 1);
383 else {
384 char tmp[4];
385 sprintf(tmp, "%%%02X", u);
386 s = UString(tmp);
387 }
388 result += s;
389 }
390
391 return jsString(exec, result);
392}
393
394JSValue* globalFuncUnescape(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
395{
396 UString result = "";
397 UString str = args.at(exec, 0)->toString(exec);
398 int k = 0;
399 int len = str.size();
400 while (k < len) {
401 const UChar* c = str.data() + k;
402 UChar u;
403 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
404 if (Lexer::isHexDigit(c[2]) && Lexer::isHexDigit(c[3]) && Lexer::isHexDigit(c[4]) && Lexer::isHexDigit(c[5])) {
405 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
406 c = &u;
407 k += 5;
408 }
409 } else if (c[0] == '%' && k <= len - 3 && Lexer::isHexDigit(c[1]) && Lexer::isHexDigit(c[2])) {
410 u = UChar(Lexer::convertHex(c[1], c[2]));
411 c = &u;
412 k += 2;
413 }
414 k++;
415 result.append(*c);
416 }
417
418 return jsString(exec, result);
419}
420
421#ifndef NDEBUG
422JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
423{
424 CStringBuffer string;
425 args.at(exec, 0)->toString(exec).getCString(string);
426 puts(string.data());
427 return jsUndefined();
428}
429#endif
430
431} // namespace JSC
Note: See TracBrowser for help on using the repository browser.