Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2002 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2001 Peter Kelly (pmk (at) post.com)
      4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      5  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich (at) 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 "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(0).toString(exec);
     56     CString cstr = str.UTF8String(true);
     57     if (!cstr.c_str())
     58         return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
     59 
     60     JSStringBuilder 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(doNotEscape, c))
     65             builder.append(c);
     66         else {
     67             char tmp[4];
     68             snprintf(tmp, 4, "%%%02X", static_cast<unsigned char>(c));
     69             builder.append(tmp);
     70         }
     71     }
     72     return builder.build(exec);
     73 }
     74 
     75 static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict)
     76 {
     77     JSStringBuilder builder;
     78     UString str = args.at(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(p[1]) && isASCIIHexDigit(p[2])) {
     89                 const char b0 = Lexer::convertHex(p[1], 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(q[1]) && isASCIIHexDigit(q[2]))
     98                             sequence[i] = Lexer::convertHex(q[1], 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(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(p[2]) && isASCIIHexDigit(p[3])
    125                         && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
    126                     charLen = 6;
    127                     u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
    128                 }
    129             }
    130             if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
    131                 c = u;
    132                 k += charLen - 1;
    133             }
    134         }
    135         k++;
    136         builder.append(c);
    137     }
    138     return builder.build(exec);
    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(*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(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(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(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
    245         else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
    246             number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 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(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(true /*tolerant*/, 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, "The \"this\" value passed to eval must be the global object from which eval originated");
    280 
    281     JSValue x = args.at(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, makeSource(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(eval.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot());
    297 }
    298 
    299 JSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    300 {
    301     JSValue value = args.at(0);
    302     int32_t radix = args.at(1).toInt32(exec);
    303 
    304     if (radix != 0 && radix != 10)
    305         return jsNumber(exec, parseInt(value.toString(exec), radix));
    306 
    307     if (value.isInt32())
    308         return value;
    309 
    310     if (value.isDouble()) {
    311         double d = value.asDouble();
    312         if (isfinite(d))
    313             return jsNumber(exec, (d > 0) ? floor(d) : ceil(d));
    314         if (isnan(d) || isinf(d))
    315             return jsNaN(exec);
    316         return jsNumber(exec, 0);
    317     }
    318 
    319     return jsNumber(exec, parseInt(value.toString(exec), radix));
    320 }
    321 
    322 JSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    323 {
    324     return jsNumber(exec, parseFloat(args.at(0).toString(exec)));
    325 }
    326 
    327 JSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    328 {
    329     return jsBoolean(isnan(args.at(0).toNumber(exec)));
    330 }
    331 
    332 JSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    333 {
    334     double n = args.at(0).toNumber(exec);
    335     return jsBoolean(!isnan(n) && !isinf(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, do_not_unescape_when_decoding_URI, true);
    344 }
    345 
    346 JSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    347 {
    348     return decode(exec, args, "", 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, 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, 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     JSStringBuilder builder;
    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             builder.append(tmp);
    390         } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
    391             builder.append(c, 1);
    392         else {
    393             char tmp[4];
    394             sprintf(tmp, "%%%02X", u);
    395             builder.append(tmp);
    396         }
    397     }
    398 
    399     return builder.build(exec);
    400 }
    401 
    402 JSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    403 {
    404     StringBuilder builder;
    405     UString str = args.at(0).toString(exec);
    406     int k = 0;
    407     int len = str.size();
    408     while (k < len) {
    409         const UChar* c = str.data() + k;
    410         UChar u;
    411         if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
    412             if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) {
    413                 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
    414                 c = &u;
    415                 k += 5;
    416             }
    417         } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) {
    418             u = UChar(Lexer::convertHex(c[1], c[2]));
    419             c = &u;
    420             k += 2;
    421         }
    422         k++;
    423         builder.append(*c);
    424     }
    425 
    426     return jsString(exec, builder.build());
    427 }
    428 
    429 #ifndef NDEBUG
    430 JSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    431 {
    432     CStringBuffer string;
    433     args.at(0).toString(exec).getCString(string);
    434     puts(string.data());
    435     return jsUndefined();
    436 }
    437 #endif
    438 
    439 } // namespace JSC
    440