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