X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/4e4e5a6f2694187498445a6ac6f1634ce8141119..14957cd040308e3eeec43d26bae5d76da13fcd85:/runtime/JSGlobalObjectFunctions.cpp?ds=sidebyside diff --git a/runtime/JSGlobalObjectFunctions.cpp b/runtime/JSGlobalObjectFunctions.cpp index 228ed6c..35507e8 100644 --- a/runtime/JSGlobalObjectFunctions.cpp +++ b/runtime/JSGlobalObjectFunctions.cpp @@ -26,7 +26,6 @@ #include "JSGlobalObjectFunctions.h" #include "CallFrame.h" -#include "GlobalEvalFunction.h" #include "Interpreter.h" #include "JSGlobalObject.h" #include "JSString.h" @@ -35,15 +34,14 @@ #include "LiteralParser.h" #include "Nodes.h" #include "Parser.h" -#include "StringBuilder.h" -#include "StringExtras.h" +#include "UStringBuilder.h" #include "dtoa.h" #include #include -#include #include #include #include +#include #include using namespace WTF; @@ -51,12 +49,12 @@ using namespace Unicode; namespace JSC { -static JSValue encode(ExecState* exec, const ArgList& args, const char* doNotEscape) +static JSValue encode(ExecState* exec, const char* doNotEscape) { - UString str = args.at(0).toString(exec); - CString cstr = str.UTF8String(true); + UString str = exec->argument(0).toString(exec); + CString cstr = str.utf8(true); if (!cstr.data()) - return throwError(exec, URIError, "String contained an illegal UTF-16 sequence."); + return throwError(exec, createURIError(exec, "String contained an illegal UTF-16 sequence.")); JSStringBuilder builder; const char* p = cstr.data(); @@ -73,13 +71,13 @@ static JSValue encode(ExecState* exec, const ArgList& args, const char* doNotEsc return builder.build(exec); } -static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict) +static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) { JSStringBuilder builder; - UString str = args.at(0).toString(exec); + UString str = exec->argument(0).toString(exec); int k = 0; - int len = str.size(); - const UChar* d = str.data(); + int len = str.length(); + const UChar* d = str.characters(); UChar u = 0; while (k < len) { const UChar* p = d + k; @@ -118,7 +116,7 @@ static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUne } if (charLen == 0) { if (strict) - return throwError(exec, URIError); + return throwError(exec, createURIError(exec, "URI error")); // The only case where we don't use "strict" mode is the "unescape" function. // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. if (k <= len - 6 && p[1] == 'u' @@ -142,6 +140,7 @@ static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUne bool isStrWhiteSpace(UChar c) { switch (c) { + // ECMA-262-5th 7.2 & 7.3 case 0x0009: case 0x000A: case 0x000B: @@ -151,6 +150,7 @@ bool isStrWhiteSpace(UChar c) case 0x00A0: case 0x2028: case 0x2029: + case 0xFEFF: return true; default: return c > 0xff && isSeparatorSpace(c); @@ -195,10 +195,32 @@ double parseIntOverflow(const char* s, int length, int radix) return number; } +double parseIntOverflow(const UChar* s, int length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const UChar* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == Inf) { + if (*p != '0') { + number = Inf; + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + static double parseInt(const UString& s, int radix) { - int length = s.size(); - const UChar* data = s.data(); + int length = s.length(); + const UChar* data = s.characters(); int p = 0; while (p < length && isStrWhiteSpace(data[p])) @@ -241,11 +263,10 @@ static double parseInt(const UString& s, int radix) } if (number >= mantissaOverflowLowerBound) { - // FIXME: It is incorrect to use UString::ascii() here because it's not thread-safe. if (radix == 10) - number = WTF::strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0); + number = WTF::strtod(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), 0); else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) - number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix); + number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix); } if (!sawDigit) @@ -254,105 +275,241 @@ static double parseInt(const UString& s, int radix) return sign * number; } +static const int SizeOfInfinity = 8; + +static bool isInfinity(const UChar* data, const UChar* end) +{ + return (end - data) >= SizeOfInfinity + && data[0] == 'I' + && data[1] == 'n' + && data[2] == 'f' + && data[3] == 'i' + && data[4] == 'n' + && data[5] == 'i' + && data[6] == 't' + && data[7] == 'y'; +} + +// See ecma-262 9.3.1 +static double jsHexIntegerLiteral(const UChar*& data, const UChar* end) +{ + // Hex number. + data += 2; + const UChar* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 16 + toASCIIHexValue(*data); + ++data; + if (data == end) + break; + if (!isASCIIHexDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16); + + return number; +} + +// See ecma-262 9.3.1 +static double jsStrDecimalLiteral(const UChar*& data, const UChar* end) +{ + ASSERT(data < end); + + // Copy the sting into a null-terminated byte buffer, and call strtod. + Vector byteBuffer; + for (const UChar* characters = data; characters < end; ++characters) { + UChar character = *characters; + byteBuffer.append(isASCII(character) ? character : 0); + } + byteBuffer.append(0); + char* endOfNumber; + double number = WTF::strtod(byteBuffer.data(), &endOfNumber); + + // Check if strtod found a number; if so return it. + ptrdiff_t consumed = endOfNumber - byteBuffer.data(); + if (consumed) { + data += consumed; + return number; + } + + // Check for [+-]?Infinity + switch (*data) { + case 'I': + if (isInfinity(data, end)) { + data += SizeOfInfinity; + return Inf; + } + break; + + case '+': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return Inf; + } + break; + + case '-': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return -Inf; + } + break; + } + + // Not a number. + return NaN; +} + +// See ecma-262 9.3.1 +double jsToNumber(const UString& s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s.characters()[0]; + if (isASCIIDigit(c)) + return c - '0'; + if (isStrWhiteSpace(c)) + return 0; + return NaN; + } + + const UChar* data = s.characters(); + const UChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return 0.0; + + double number; + if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2])) + number = jsHexIntegerLiteral(data, end); + else + number = jsStrDecimalLiteral(data, end); + + // Allow trailing white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + if (data != end) + return NaN; + + return number; +} + static double parseFloat(const UString& s) { - // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0. - // Need to skip any whitespace and then one + or - sign. - int length = s.size(); - const UChar* data = s.data(); - int p = 0; - while (p < length && isStrWhiteSpace(data[p])) - ++p; + unsigned size = s.length(); - if (p < length && (data[p] == '+' || data[p] == '-')) - ++p; + if (size == 1) { + UChar c = s.characters()[0]; + if (isASCIIDigit(c)) + return c - '0'; + return NaN; + } - if (length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) - return 0; + const UChar* data = s.characters(); + const UChar* end = data + size; - // FIXME: UString::toDouble will ignore leading ASCII spaces, but we need to ignore - // other StrWhiteSpaceChar values as well. - return s.toDouble(true /*tolerant*/, false /* NaN for empty string */); + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return NaN; + + return jsStrDecimalLiteral(data, end); } -JSValue JSC_HOST_CALL globalFuncEval(ExecState* exec, JSObject* function, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) { - JSObject* thisObject = thisValue.toThisObject(exec); + JSObject* thisObject = exec->hostThisValue().toThisObject(exec); JSObject* unwrappedObject = thisObject->unwrappedObject(); - if (!unwrappedObject->isGlobalObject() || static_cast(unwrappedObject)->evalFunction() != function) - return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated"); + if (!unwrappedObject->isGlobalObject() || static_cast(unwrappedObject)->evalFunction() != exec->callee()) + return throwVMError(exec, createEvalError(exec, "The \"this\" value passed to eval must be the global object from which eval originated")); - JSValue x = args.at(0); + JSValue x = exec->argument(0); if (!x.isString()) - return x; + return JSValue::encode(x); UString s = x.toString(exec); - LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON); + LiteralParser preparser(exec, s.characters(), s.length(), LiteralParser::NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) - return parsedObject; + return JSValue::encode(parsedObject); - RefPtr eval = EvalExecutable::create(exec, makeSource(s)); - JSObject* error = eval->compile(exec, static_cast(unwrappedObject)->globalScopeChain().node()); + EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false); + JSObject* error = eval->compile(exec, static_cast(unwrappedObject)->globalScopeChain()); if (error) - return throwError(exec, error); + return throwVMError(exec, error); - return exec->interpreter()->execute(eval.get(), exec, thisObject, static_cast(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot()); + return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, static_cast(unwrappedObject)->globalScopeChain())); } -JSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) { - JSValue value = args.at(0); - int32_t radix = args.at(1).toInt32(exec); + JSValue value = exec->argument(0); + int32_t radix = exec->argument(1).toInt32(exec); if (radix != 0 && radix != 10) - return jsNumber(exec, parseInt(value.toString(exec), radix)); + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); if (value.isInt32()) - return value; + return JSValue::encode(value); if (value.isDouble()) { double d = value.asDouble(); if (isfinite(d)) - return jsNumber(exec, (d > 0) ? floor(d) : ceil(d)); + return JSValue::encode(jsNumber((d > 0) ? floor(d) : ceil(d))); if (isnan(d) || isinf(d)) - return jsNaN(exec); - return jsNumber(exec, 0); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(0)); } - return jsNumber(exec, parseInt(value.toString(exec), radix)); + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); } -JSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) { - return jsNumber(exec, parseFloat(args.at(0).toString(exec))); + return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)))); } -JSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) { - return jsBoolean(isnan(args.at(0).toNumber(exec))); + return JSValue::encode(jsBoolean(isnan(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) { - double n = args.at(0).toNumber(exec); - return jsBoolean(!isnan(n) && !isinf(n)); + double n = exec->argument(0).toNumber(exec); + return JSValue::encode(jsBoolean(!isnan(n) && !isinf(n))); } -JSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec) { static const char do_not_unescape_when_decoding_URI[] = "#$&+,/:;=?@"; - return decode(exec, args, do_not_unescape_when_decoding_URI, true); + return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true)); } -JSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) { - return decode(exec, args, "", true); + return JSValue::encode(decode(exec, "", true)); } -JSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) { static const char do_not_escape_when_encoding_URI[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -360,10 +517,10 @@ JSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue, c "0123456789" "!#$&'()*+,-./:;=?@_~"; - return encode(exec, args, do_not_escape_when_encoding_URI); + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI)); } -JSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) { static const char do_not_escape_when_encoding_URI_component[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -371,10 +528,10 @@ JSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec, JSObject*, J "0123456789" "!'()*-._~"; - return encode(exec, args, do_not_escape_when_encoding_URI_component); + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component)); } -JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) { static const char do_not_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -383,9 +540,9 @@ JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, cons "*+-./@_"; JSStringBuilder builder; - UString str = args.at(0).toString(exec); - const UChar* c = str.data(); - for (unsigned k = 0; k < str.size(); k++, c++) { + UString str = exec->argument(0).toString(exec); + const UChar* c = str.characters(); + for (unsigned k = 0; k < str.length(); k++, c++) { int u = c[0]; if (u > 255) { char tmp[7]; @@ -400,17 +557,17 @@ JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, cons } } - return builder.build(exec); + return JSValue::encode(builder.build(exec)); } -JSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) { - StringBuilder builder; - UString str = args.at(0).toString(exec); + UStringBuilder builder; + UString str = exec->argument(0).toString(exec); int k = 0; - int len = str.size(); + int len = str.length(); while (k < len) { - const UChar* c = str.data() + k; + const UChar* c = str.characters() + k; UChar u; if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { @@ -427,16 +584,7 @@ JSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec, JSObject*, JSValue, co builder.append(*c); } - return jsString(exec, builder.build()); -} - -#ifndef NDEBUG -JSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args) -{ - CString string = args.at(0).toString(exec).UTF8String(); - puts(string.data()); - return jsUndefined(); + return JSValue::encode(jsString(exec, builder.toUString())); } -#endif } // namespace JSC