X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/9dae56ea45a0f5f8136a5c93d6f3a7f99399ca73..1df5f87f1309a8daa30dabdee855f48ae40d14ab:/runtime/JSGlobalObjectFunctions.cpp diff --git a/runtime/JSGlobalObjectFunctions.cpp b/runtime/JSGlobalObjectFunctions.cpp index f9dbe59..35507e8 100644 --- a/runtime/JSGlobalObjectFunctions.cpp +++ b/runtime/JSGlobalObjectFunctions.cpp @@ -26,20 +26,22 @@ #include "JSGlobalObjectFunctions.h" #include "CallFrame.h" -#include "GlobalEvalFunction.h" +#include "Interpreter.h" #include "JSGlobalObject.h" #include "JSString.h" -#include "Interpreter.h" -#include "Parser.h" -#include "dtoa.h" +#include "JSStringBuilder.h" #include "Lexer.h" +#include "LiteralParser.h" #include "Nodes.h" +#include "Parser.h" +#include "UStringBuilder.h" +#include "dtoa.h" #include #include -#include #include #include #include +#include #include using namespace WTF; @@ -47,35 +49,35 @@ using namespace Unicode; namespace JSC { -static JSValuePtr encode(ExecState* exec, const ArgList& args, const char* doNotEscape) +static JSValue encode(ExecState* exec, const char* doNotEscape) { - UString str = args.at(exec, 0).toString(exec); - CString cstr = str.UTF8String(true); - if (!cstr.c_str()) - return throwError(exec, URIError, "String contained an illegal UTF-16 sequence."); - - UString result = ""; - const char* p = cstr.c_str(); - for (size_t k = 0; k < cstr.size(); k++, p++) { + UString str = exec->argument(0).toString(exec); + CString cstr = str.utf8(true); + if (!cstr.data()) + return throwError(exec, createURIError(exec, "String contained an illegal UTF-16 sequence.")); + + JSStringBuilder builder; + const char* p = cstr.data(); + for (size_t k = 0; k < cstr.length(); k++, p++) { char c = *p; if (c && strchr(doNotEscape, c)) - result.append(c); + builder.append(c); else { char tmp[4]; snprintf(tmp, sizeof(tmp), "%%%02X", static_cast(c)); - result += tmp; + builder.append(tmp); } } - return jsString(exec, result); + return builder.build(exec); } -static JSValuePtr decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict) +static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) { - UString result = ""; - UString str = args.at(exec, 0).toString(exec); + JSStringBuilder builder; + 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; @@ -105,7 +107,7 @@ static JSValuePtr decode(ExecState* exec, const ArgList& args, const char* doNot charLen = 0; else if (character >= 0x10000) { // Convert to surrogate pair. - result.append(static_cast(0xD800 | ((character - 0x10000) >> 10))); + builder.append(static_cast(0xD800 | ((character - 0x10000) >> 10))); u = static_cast(0xDC00 | ((character - 0x10000) & 0x3FF)); } else u = static_cast(character); @@ -114,7 +116,7 @@ static JSValuePtr decode(ExecState* exec, const ArgList& args, const char* doNot } 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' @@ -130,14 +132,15 @@ static JSValuePtr decode(ExecState* exec, const ArgList& args, const char* doNot } } k++; - result.append(c); + builder.append(c); } - return jsString(exec, result); + return builder.build(exec); } bool isStrWhiteSpace(UChar c) { switch (c) { + // ECMA-262-5th 7.2 & 7.3 case 0x0009: case 0x000A: case 0x000B: @@ -147,6 +150,7 @@ bool isStrWhiteSpace(UChar c) case 0x00A0: case 0x2028: case 0x2029: + case 0xFEFF: return true; default: return c > 0xff && isSeparatorSpace(c); @@ -191,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])) @@ -238,9 +264,9 @@ static double parseInt(const UString& s, int radix) if (number >= mantissaOverflowLowerBound) { 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) @@ -249,99 +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; - 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); } -JSValuePtr globalFuncEval(ExecState* exec, JSObject* function, JSValuePtr 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")); - JSValuePtr x = args.at(exec, 0); + JSValue x = exec->argument(0); if (!x.isString()) - return x; + return JSValue::encode(x); UString s = x.toString(exec); - int errLine; - UString errMsg; - - SourceCode source = makeSource(s); - RefPtr evalNode = exec->globalData().parser->parse(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); + LiteralParser preparser(exec, s.characters(), s.length(), LiteralParser::NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return JSValue::encode(parsedObject); - if (!evalNode) - return throwError(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), NULL); + EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false); + JSObject* error = eval->compile(exec, static_cast(unwrappedObject)->globalScopeChain()); + if (error) + return throwVMError(exec, error); - return exec->interpreter()->execute(evalNode.get(), exec, thisObject, static_cast(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot()); + return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, static_cast(unwrappedObject)->globalScopeChain())); } -JSValuePtr globalFuncParseInt(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) { - JSValuePtr value = args.at(exec, 0); - int32_t radix = args.at(exec, 1).toInt32(exec); + JSValue value = exec->argument(0); + int32_t radix = exec->argument(1).toInt32(exec); - if (value.isNumber() && (radix == 0 || radix == 10)) { - if (value.isInt32Fast()) - return value; - double d = value.uncheckedGetNumber(); + if (radix != 0 && radix != 10) + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); + + if (value.isInt32()) + 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->globalData()); - return js0(); + 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))); } -JSValuePtr globalFuncParseFloat(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) { - return jsNumber(exec, parseFloat(args.at(exec, 0).toString(exec))); + return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)))); } -JSValuePtr globalFuncIsNaN(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) { - return jsBoolean(isnan(args.at(exec, 0).toNumber(exec))); + return JSValue::encode(jsBoolean(isnan(exec->argument(0).toNumber(exec)))); } -JSValuePtr globalFuncIsFinite(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) { - double n = args.at(exec, 0).toNumber(exec); - return jsBoolean(!isnan(n) && !isinf(n)); + double n = exec->argument(0).toNumber(exec); + return JSValue::encode(jsBoolean(!isnan(n) && !isinf(n))); } -JSValuePtr globalFuncDecodeURI(ExecState* exec, JSObject*, JSValuePtr, 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)); } -JSValuePtr globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) { - return decode(exec, args, "", true); + return JSValue::encode(decode(exec, "", true)); } -JSValuePtr globalFuncEncodeURI(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) { static const char do_not_escape_when_encoding_URI[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -349,10 +517,10 @@ JSValuePtr globalFuncEncodeURI(ExecState* exec, JSObject*, JSValuePtr, const Arg "0123456789" "!#$&'()*+,-./:;=?@_~"; - return encode(exec, args, do_not_escape_when_encoding_URI); + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI)); } -JSValuePtr globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) { static const char do_not_escape_when_encoding_URI_component[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -360,10 +528,10 @@ JSValuePtr globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValuePtr, "0123456789" "!'()*-._~"; - return encode(exec, args, do_not_escape_when_encoding_URI_component); + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component)); } -JSValuePtr globalFuncEscape(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) { static const char do_not_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -371,64 +539,52 @@ JSValuePtr globalFuncEscape(ExecState* exec, JSObject*, JSValuePtr, const ArgLis "0123456789" "*+-./@_"; - UString result = ""; - UString s; - UString str = args.at(exec, 0).toString(exec); - const UChar* c = str.data(); - for (int k = 0; k < str.size(); k++, c++) { + JSStringBuilder builder; + 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]; snprintf(tmp, sizeof(tmp), "%%u%04X", u); - s = UString(tmp); + builder.append(tmp); } else if (u != 0 && strchr(do_not_escape, static_cast(u))) - s = UString(c, 1); + builder.append(c, 1); else { char tmp[4]; snprintf(tmp, sizeof(tmp), "%%%02X", u); - s = UString(tmp); + builder.append(tmp); } - result += s; } - return jsString(exec, result); + return JSValue::encode(builder.build(exec)); } -JSValuePtr globalFuncUnescape(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) { - UString result = ""; - UString str = args.at(exec, 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 (Lexer::isHexDigit(c[2]) && Lexer::isHexDigit(c[3]) && Lexer::isHexDigit(c[4]) && Lexer::isHexDigit(c[5])) { + if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]); c = &u; k += 5; } - } else if (c[0] == '%' && k <= len - 3 && Lexer::isHexDigit(c[1]) && Lexer::isHexDigit(c[2])) { + } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { u = UChar(Lexer::convertHex(c[1], c[2])); c = &u; k += 2; } k++; - result.append(*c); + builder.append(*c); } - return jsString(exec, result); -} - -#ifndef NDEBUG -JSValuePtr globalFuncJSCPrint(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) -{ - CStringBuffer string; - args.at(exec, 0).toString(exec).getCString(string); - puts(string.data()); - return jsUndefined(); + return JSValue::encode(jsString(exec, builder.toUString())); } -#endif } // namespace JSC