X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/2d39b0e377c0896910ee49ae70082ba665faf986..HEAD:/runtime/JSGlobalObjectFunctions.cpp?ds=inline diff --git a/runtime/JSGlobalObjectFunctions.cpp b/runtime/JSGlobalObjectFunctions.cpp index 604de2b..45a24ec 100644 --- a/runtime/JSGlobalObjectFunctions.cpp +++ b/runtime/JSGlobalObjectFunctions.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -52,9 +53,18 @@ using namespace Unicode; namespace JSC { -static JSValue encode(ExecState* exec, const char* doNotEscape) +template +static Bitmap<256> makeCharacterBitmap(const char (&characters)[charactersCount]) { - CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(StrictConversion); + Bitmap<256> bitmap; + for (unsigned i = 0; i < charactersCount; ++i) + bitmap.set(characters[i]); + return bitmap; +} + +static JSValue encode(ExecState* exec, const Bitmap<256>& doNotEscape) +{ + CString cstr = exec->argument(0).toString(exec)->view(exec).get().utf8(StrictConversion); if (!cstr.data()) return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("String contained an illegal UTF-16 sequence."))); @@ -62,12 +72,11 @@ static JSValue encode(ExecState* exec, const char* doNotEscape) const char* p = cstr.data(); for (size_t k = 0; k < cstr.length(); k++, p++) { char c = *p; - if (c && strchr(doNotEscape, c)) + if (c && doNotEscape.get(static_cast(c))) builder.append(static_cast(c)); else { - char tmp[4]; - snprintf(tmp, sizeof(tmp), "%%%02X", static_cast(c)); - builder.append(tmp); + builder.append(static_cast('%')); + appendByteAsHex(c, builder); } } return builder.build(exec); @@ -75,7 +84,7 @@ static JSValue encode(ExecState* exec, const char* doNotEscape) template ALWAYS_INLINE -static JSValue decode(ExecState* exec, const CharType* characters, int length, const char* doNotUnescape, bool strict) +static JSValue decode(ExecState* exec, const CharType* characters, int length, const Bitmap<256>& doNotUnescape, bool strict) { JSStringBuilder builder; int k = 0; @@ -127,7 +136,7 @@ static JSValue decode(ExecState* exec, const CharType* characters, int length, c u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]); } } - if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) { + if (charLen && (u == 0 || u >= 128 || !doNotUnescape.get(static_cast(u)))) { builder.append(u); k += charLen; continue; @@ -139,9 +148,9 @@ static JSValue decode(ExecState* exec, const CharType* characters, int length, c return builder.build(exec); } -static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) +static JSValue decode(ExecState* exec, const Bitmap<256>& doNotUnescape, bool strict) { - String str = exec->argument(0).toString(exec)->value(exec); + StringView str = exec->argument(0).toString(exec)->view(exec); if (str.is8Bit()) return decode(exec, str.characters8(), str.length(), doNotUnescape, strict); @@ -239,7 +248,7 @@ static double parseIntOverflow(StringView string, int radix) // ES5.1 15.1.2.2 template ALWAYS_INLINE -static double parseInt(const String& s, const CharType* data, int radix) +static double parseInt(StringView s, const CharType* data, int radix) { // 1. Let inputString be ToString(string). // 2. Let S be a newly created substring of inputString consisting of the first character that is not a @@ -311,16 +320,16 @@ static double parseInt(const String& s, const CharType* data, int radix) if (number >= mantissaOverflowLowerBound) { if (radix == 10) { size_t parsedLength; - number = parseDouble(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), parsedLength); + number = parseDouble(s.substring(firstDigitPosition, p - firstDigitPosition), parsedLength); } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) - number = parseIntOverflow(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), radix); + number = parseIntOverflow(s.substring(firstDigitPosition, p - firstDigitPosition), radix); } // 15. Return sign x number. return sign * number; } -static double parseInt(const String& s, int radix) +static double parseInt(StringView s, int radix) { if (s.is8Bit()) return parseInt(s, s.characters8(), radix); @@ -343,7 +352,51 @@ static bool isInfinity(const CharType* data, const CharType* end) && data[7] == 'y'; } -// See ecma-262 9.3.1 +// See ecma-262 6th 11.8.3 +template +static double jsBinaryIntegerLiteral(const CharType*& data, const CharType* end) +{ + // Binary number. + data += 2; + const CharType* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 2 + (*data - '0'); + ++data; + if (data == end) + break; + if (!isASCIIBinaryDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 2); + + return number; +} + +// See ecma-262 6th 11.8.3 +template +static double jsOctalIntegerLiteral(const CharType*& data, const CharType* end) +{ + // Octal number. + data += 2; + const CharType* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 8 + (*data - '0'); + ++data; + if (data == end) + break; + if (!isASCIIOctalDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 8); + + return number; +} + +// See ecma-262 6th 11.8.3 template static double jsHexIntegerLiteral(const CharType*& data, const CharType* end) { @@ -365,7 +418,7 @@ static double jsHexIntegerLiteral(const CharType*& data, const CharType* end) return number; } -// See ecma-262 9.3.1 +// See ecma-262 6th 11.8.3 template static double jsStrDecimalLiteral(const CharType*& data, const CharType* end) { @@ -422,9 +475,16 @@ static double toDouble(const CharType* characters, unsigned size) return 0.0; double number; - if (characters[0] == '0' && characters + 2 < endCharacters && (characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2])) - number = jsHexIntegerLiteral(characters, endCharacters); - else + if (characters[0] == '0' && characters + 2 < endCharacters) { + if ((characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2])) + number = jsHexIntegerLiteral(characters, endCharacters); + else if ((characters[1] | 0x20) == 'o' && isASCIIOctalDigit(characters[2])) + number = jsOctalIntegerLiteral(characters, endCharacters); + else if ((characters[1] | 0x20) == 'b' && isASCIIBinaryDigit(characters[2])) + number = jsBinaryIntegerLiteral(characters, endCharacters); + else + number = jsStrDecimalLiteral(characters, endCharacters); + } else number = jsStrDecimalLiteral(characters, endCharacters); // Allow trailing white space. @@ -438,8 +498,8 @@ static double toDouble(const CharType* characters, unsigned size) return number; } -// See ecma-262 9.3.1 -double jsToNumber(const String& s) +// See ecma-262 6th 11.8.3 +double jsToNumber(StringView s) { unsigned size = s.length(); @@ -457,7 +517,7 @@ double jsToNumber(const String& s) return toDouble(s.characters16(), size); } -static double parseFloat(const String& s) +static double parseFloat(StringView s) { unsigned size = s.length(); @@ -520,7 +580,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) } JSGlobalObject* calleeGlobalObject = exec->callee()->globalObject(); - EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false); + EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false, ThisTDZMode::CheckIfNeeded); if (!eval) return JSValue::encode(jsUndefined()); @@ -550,7 +610,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) } // If ToString throws, we shouldn't call ToInt32. - String s = value.toString(exec)->value(exec); + StringView s = value.toString(exec)->view(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); @@ -559,7 +619,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) { - return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->value(exec)))); + return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->view(exec)))); } EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) @@ -575,59 +635,63 @@ EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec) { - static const char do_not_unescape_when_decoding_URI[] = - "#$&+,/:;=?@"; + static Bitmap<256> doNotUnescapeWhenDecodingURI = makeCharacterBitmap( + "#$&+,/:;=?@" + ); - return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true)); + return JSValue::encode(decode(exec, doNotUnescapeWhenDecodingURI, true)); } EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) { - return JSValue::encode(decode(exec, "", true)); + static Bitmap<256> emptyBitmap; + return JSValue::encode(decode(exec, emptyBitmap, true)); } EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) { - static const char do_not_escape_when_encoding_URI[] = + static Bitmap<256> doNotEscapeWhenEncodingURI = makeCharacterBitmap( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" - "!#$&'()*+,-./:;=?@_~"; + "!#$&'()*+,-./:;=?@_~" + ); - return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI)); + return JSValue::encode(encode(exec, doNotEscapeWhenEncodingURI)); } EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) { - static const char do_not_escape_when_encoding_URI_component[] = + static Bitmap<256> doNotEscapeWhenEncodingURIComponent = makeCharacterBitmap( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" - "!'()*-._~"; + "!'()*-._~" + ); - return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component)); + return JSValue::encode(encode(exec, doNotEscapeWhenEncodingURIComponent)); } EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) { - static const char do_not_escape[] = + static Bitmap<256> doNotEscape = makeCharacterBitmap( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" - "*+-./@_"; + "*+-./@_" + ); JSStringBuilder builder; - String str = exec->argument(0).toString(exec)->value(exec); + StringView str = exec->argument(0).toString(exec)->view(exec); if (str.is8Bit()) { const LChar* c = str.characters8(); for (unsigned k = 0; k < str.length(); k++, c++) { int u = c[0]; - if (u && strchr(do_not_escape, static_cast(u))) + if (u && doNotEscape.get(static_cast(u))) builder.append(*c); else { - char tmp[4]; - snprintf(tmp, sizeof(tmp), "%%%02X", u); - builder.append(tmp); + builder.append(static_cast('%')); + appendByteAsHex(static_cast(u), builder); } } @@ -638,15 +702,15 @@ EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) 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); - builder.append(tmp); - } else if (u != 0 && strchr(do_not_escape, static_cast(u))) + builder.append(static_cast('%')); + builder.append(static_cast('u')); + appendByteAsHex(u >> 8, builder); + appendByteAsHex(u & 0xFF, builder); + } else if (u != 0 && doNotEscape.get(static_cast(u))) builder.append(*c); else { - char tmp[4]; - snprintf(tmp, sizeof(tmp), "%%%02X", u); - builder.append(tmp); + builder.append(static_cast('%')); + appendByteAsHex(u, builder); } } @@ -656,7 +720,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) { StringBuilder builder; - String str = exec->argument(0).toString(exec)->value(exec); + StringView str = exec->argument(0).toString(exec)->view(exec); int k = 0; int len = str.length(); @@ -741,6 +805,9 @@ private: EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec) { + if (exec->thisValue().isUndefinedOrNull()) + return throwVMError(exec, createTypeError(exec, "Can't convert undefined or null to object")); + JSObject* thisObject = jsDynamicCast(exec->thisValue().toThis(exec, NotStrictMode)); if (!thisObject) @@ -779,8 +846,18 @@ private: JSObject* m_thisObject; }; +bool checkProtoSetterAccessAllowed(ExecState* exec, JSObject* object) +{ + GlobalFuncProtoSetterFunctor functor(object); + exec->iterate(functor); + return functor.allowsAccess(); +} + EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec) { + if (exec->thisValue().isUndefinedOrNull()) + return throwVMError(exec, createTypeError(exec, "Can't convert undefined or null to object")); + JSValue value = exec->argument(0); JSObject* thisObject = jsDynamicCast(exec->thisValue().toThis(exec, NotStrictMode)); @@ -789,9 +866,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec) if (!thisObject) return JSValue::encode(jsUndefined()); - GlobalFuncProtoSetterFunctor functor(thisObject); - exec->iterate(functor); - if (!functor.allowsAccess()) + if (!checkProtoSetterAccessAllowed(exec, thisObject)) return JSValue::encode(jsUndefined()); // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. @@ -802,7 +877,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec) return throwVMError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); if (!thisObject->setPrototypeWithCycleCheck(exec, value)) - exec->vm().throwException(exec, createError(exec, "cyclic __proto__ value")); + exec->vm().throwException(exec, createError(exec, ASCIILiteral("cyclic __proto__ value"))); return JSValue::encode(jsUndefined()); }