diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-03-12 14:11:15 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-03-12 14:11:15 +0100 |
commit | dd91e772430dc294e3bf478c119ef8d43c0a3358 (patch) | |
tree | 6f33ce4d5872a5691e0291eb45bf6ab373a5f567 /Source/JavaScriptCore/runtime | |
parent | ad0d549d4cc13433f77c1ac8f0ab379c83d93f28 (diff) | |
download | qtwebkit-dd91e772430dc294e3bf478c119ef8d43c0a3358.tar.gz |
Imported WebKit commit 3db4eb1820ac8fb03065d7ea73a4d9db1e8fea1a (http://svn.webkit.org/repository/webkit/trunk@110422)
This includes build fixes for the latest qtbase/qtdeclarative as well as the final QML2 API.
Diffstat (limited to 'Source/JavaScriptCore/runtime')
53 files changed, 873 insertions, 469 deletions
diff --git a/Source/JavaScriptCore/runtime/Arguments.cpp b/Source/JavaScriptCore/runtime/Arguments.cpp index a099adb75..7a53ec1a4 100644 --- a/Source/JavaScriptCore/runtime/Arguments.cpp +++ b/Source/JavaScriptCore/runtime/Arguments.cpp @@ -196,7 +196,7 @@ void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyN JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); } -void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value) +void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow) { Arguments* thisObject = jsCast<Arguments*>(cell); if (i < static_cast<unsigned>(thisObject->d->numArguments) && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { @@ -204,7 +204,7 @@ void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue va return; } - PutPropertySlot slot; + PutPropertySlot slot(shouldThrow); JSObject::put(thisObject, exec, Identifier(exec, UString::number(i)), value, slot); } @@ -320,12 +320,15 @@ bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, const Ident if (descriptor.isAccessorDescriptor()) { // i. Call the [[Delete]] internal method of map passing P, and false as the arguments. thisObject->d->deletedArguments[i] = true; - } else if (descriptor.value()) { // b. Else i. If Desc.[[Value]] is present, then + } else { // b. Else + // i. If Desc.[[Value]] is present, then // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments. + if (descriptor.value()) + thisObject->argument(i).set(exec->globalData(), thisObject, descriptor.value()); // ii. If Desc.[[Writable]] is present and its value is false, then - thisObject->argument(i).set(exec->globalData(), thisObject, descriptor.value()); + // 1. Call the [[Delete]] internal method of map passing P and false as arguments. if (descriptor.writablePresent() && !descriptor.writable()) - thisObject->d->deletedArguments[i] = true; // 1. Call the [[Delete]] internal method of map passing P and false as arguments. + thisObject->d->deletedArguments[i] = true; } } diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index ee54a49eb..8e7af1844 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -114,7 +114,7 @@ namespace JSC { static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index cb9b12a59..2f000fc74 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -166,6 +166,90 @@ static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument return indexDouble > length ? length : static_cast<unsigned>(indexDouble); } + +// The shift/unshift function implement the shift/unshift behaviour required +// by the corresponding array prototype methods, and by splice. In both cases, +// the methods are operating an an array or array like object. +// +// header currentCount (remainder) +// [------][------------][-----------] +// header resultCount (remainder) +// [------][-----------][-----------] +// +// The set of properties in the range 'header' must be unchanged. The set of +// properties in the range 'remainder' (where remainder = length - header - +// currentCount) will be shifted to the left or right as appropriate; in the +// case of shift this must be removing values, in the case of unshift this +// must be introducing new values. +static inline void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) +{ + ASSERT(currentCount > resultCount); + unsigned count = currentCount - resultCount; + + ASSERT(header <= length); + ASSERT(currentCount <= (length - header)); + + if (!header && isJSArray(thisObj) && asArray(thisObj)->shiftCount(exec, count)) + return; + + for (unsigned k = header; k < length - currentCount; ++k) { + unsigned from = k + currentCount; + unsigned to = k + resultCount; + PropertySlot slot(thisObj); + if (thisObj->getPropertySlot(exec, from, slot)) { + JSValue value = slot.getValue(exec, from); + if (exec->hadException()) + return; + thisObj->methodTable()->putByIndex(thisObj, exec, to, value, true); + if (exec->hadException()) + return; + } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, to)) { + throwTypeError(exec, "Unable to delete property."); + return; + } + } + for (unsigned k = length; k > length - count; --k) { + if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1)) { + throwTypeError(exec, "Unable to delete property."); + return; + } + } +} +static inline void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) +{ + ASSERT(resultCount > currentCount); + unsigned count = resultCount - currentCount; + + ASSERT(header <= length); + ASSERT(currentCount <= (length - header)); + + // Guard against overflow. + if (count > (UINT_MAX - length)) { + throwOutOfMemoryError(exec); + return; + } + + if (!header && isJSArray(thisObj) && asArray(thisObj)->unshiftCount(exec, count)) + return; + + for (unsigned k = length - currentCount; k > header; --k) { + unsigned from = k + currentCount - 1; + unsigned to = k + resultCount - 1; + PropertySlot slot(thisObj); + if (thisObj->getPropertySlot(exec, from, slot)) { + JSValue value = slot.getValue(exec, from); + if (exec->hadException()) + return; + thisObj->methodTable()->putByIndex(thisObj, exec, to, value, true); + } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, to)) { + throwTypeError(exec, "Unable to delete property."); + return; + } + if (exec->hadException()) + return; + } +} + EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); @@ -265,9 +349,13 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) strBuffer.append(','); JSValue element = thisObj->get(exec, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); if (!element.isUndefinedOrNull()) { JSObject* o = element.toObject(exec); JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); UString str; CallData callData; CallType callType = getCallData(conversionFunction, callData); @@ -275,6 +363,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec)->value(exec); else str = element.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); strBuffer.append(str); } } @@ -368,11 +458,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) if (exec->hadException()) return JSValue::encode(jsUndefined()); if (v) - arr->methodTable()->putByIndex(arr, exec, n, v); + arr->putDirectIndex(exec, n, v); n++; } } else { - arr->methodTable()->putByIndex(arr, exec, n, curArg); + arr->putDirectIndex(exec, n, curArg); n++; } if (i == argCount) @@ -402,7 +492,12 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec) result = jsUndefined(); } else { result = thisObj->get(exec, length - 1); - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1)) { + throwTypeError(exec, "Unable to delete property."); + return JSValue::encode(jsUndefined()); + } putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); } return JSValue::encode(result); @@ -426,12 +521,14 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) for (unsigned n = 0; n < exec->argumentCount(); n++) { // Check for integer overflow; where safe we can do a fast put by index. if (length + n >= length) - thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n)); + thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n), true); else { PutPropertySlot slot; Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toString(exec)->value(exec)); thisObj->methodTable()->put(thisObj, exec, propertyName, exec->argument(n), slot); } + if (exec->hadException()) + return JSValue::encode(jsUndefined()); } JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount())); putProperty(exec, thisObj, exec->propertyNames().length, newLength); @@ -455,15 +552,23 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) if (exec->hadException()) return JSValue::encode(jsUndefined()); - if (obj2) - thisObj->methodTable()->putByIndex(thisObj, exec, k, obj2); - else - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k); + if (obj2) { + thisObj->methodTable()->putByIndex(thisObj, exec, k, obj2, true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k)) { + throwTypeError(exec, "Unable to delete property."); + return JSValue::encode(jsUndefined()); + } - if (obj) - thisObj->methodTable()->putByIndex(thisObj, exec, lk1, obj); - else - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, lk1); + if (obj) { + thisObj->methodTable()->putByIndex(thisObj, exec, lk1, obj, true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, lk1)) { + throwTypeError(exec, "Unable to delete property."); + return JSValue::encode(jsUndefined()); + } } return JSValue::encode(thisObj); } @@ -481,20 +586,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) result = jsUndefined(); } else { result = thisObj->get(exec, 0); - if (isJSArray(thisObj)) - ((JSArray *)thisObj)->shiftCount(exec, 1); - else { - for (unsigned k = 1; k < length; k++) { - JSValue obj = getProperty(exec, thisObj, k); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - if (obj) - thisObj->methodTable()->putByIndex(thisObj, exec, k - 1, obj); - else - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1); - } - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1); - } + shift(exec, thisObj, 0, 1, 0, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); } return JSValue::encode(result); @@ -521,7 +615,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) if (exec->hadException()) return JSValue::encode(jsUndefined()); if (v) - resObj->methodTable()->putByIndex(resObj, exec, n, v); + resObj->putDirectIndex(exec, n, v); } resObj->setLength(exec, n); return JSValue::encode(result); @@ -580,8 +674,12 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) } // Swap themin and i if (themin > i) { - thisObj->methodTable()->putByIndex(thisObj, exec, i, minObj); - thisObj->methodTable()->putByIndex(thisObj, exec, themin, iObj); + thisObj->methodTable()->putByIndex(thisObj, exec, i, minObj, true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + thisObj->methodTable()->putByIndex(thisObj, exec, themin, iObj, true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); } } return JSValue::encode(thisObj); @@ -627,41 +725,20 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) resObj->completeInitialization(deleteCount); unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); - if (additionalArgs != deleteCount) { - if (additionalArgs < deleteCount) { - if ((!begin) && (isJSArray(thisObj))) - ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs); - else { - for (unsigned k = begin; k < length - deleteCount; ++k) { - JSValue v = getProperty(exec, thisObj, k + deleteCount); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - if (v) - thisObj->methodTable()->putByIndex(thisObj, exec, k + additionalArgs, v); - else - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + additionalArgs); - } - for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1); - } - } else { - if ((!begin) && (isJSArray(thisObj))) - ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount); - else { - for (unsigned k = length - deleteCount; k > begin; --k) { - JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - if (obj) - thisObj->methodTable()->putByIndex(thisObj, exec, k + additionalArgs - 1, obj); - else - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + additionalArgs - 1); - } - } - } + if (additionalArgs < deleteCount) { + shift(exec, thisObj, begin, deleteCount, additionalArgs, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else if (additionalArgs > deleteCount) { + unshift(exec, thisObj, begin, deleteCount, additionalArgs, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + for (unsigned k = 0; k < additionalArgs; ++k) { + thisObj->methodTable()->putByIndex(thisObj, exec, k + begin, exec->argument(k + 2), true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); } - for (unsigned k = 0; k < additionalArgs; ++k) - thisObj->methodTable()->putByIndex(thisObj, exec, k + begin, exec->argument(k + 2)); putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); return JSValue::encode(result); @@ -677,23 +754,16 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) return JSValue::encode(jsUndefined()); unsigned nrArgs = exec->argumentCount(); - if ((nrArgs) && (length)) { - if (isJSArray(thisObj)) - ((JSArray *)thisObj)->unshiftCount(exec, nrArgs); - else { - for (unsigned k = length; k > 0; --k) { - JSValue v = getProperty(exec, thisObj, k - 1); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - if (v) - thisObj->methodTable()->putByIndex(thisObj, exec, k + nrArgs - 1, v); - else - thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + nrArgs - 1); - } - } + if (nrArgs) { + unshift(exec, thisObj, 0, 0, nrArgs, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + for (unsigned k = 0; k < nrArgs; ++k) { + thisObj->methodTable()->putByIndex(thisObj, exec, k, exec->argument(k), true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); } - for (unsigned k = 0; k < nrArgs; ++k) - thisObj->methodTable()->putByIndex(thisObj, exec, k, exec->argument(k)); JSValue result = jsNumber(length + nrArgs); putProperty(exec, thisObj, exec->propertyNames().length, result); return JSValue::encode(result); @@ -732,7 +802,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) JSValue result = cachedCall.call(); if (result.toBoolean(exec)) - resultArray->methodTable()->putByIndex(resultArray, exec, filterIndex++, v); + resultArray->putDirectIndex(exec, filterIndex++, v); } if (k == length) return JSValue::encode(resultArray); @@ -753,7 +823,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); if (result.toBoolean(exec)) - resultArray->methodTable()->putByIndex(resultArray, exec, filterIndex++, v); + resultArray->putDirectIndex(exec, filterIndex++, v); } return JSValue::encode(resultArray); } @@ -788,7 +858,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); - JSArray::putByIndex(resultArray, exec, k, cachedCall.call()); + resultArray->putDirectIndex(exec, k, cachedCall.call()); } } for (; k < length && !exec->hadException(); ++k) { @@ -809,7 +879,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) return JSValue::encode(jsUndefined()); JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); - resultArray->methodTable()->putByIndex(resultArray, exec, k, result); + resultArray->putDirectIndex(exec, k, result); } return JSValue::encode(resultArray); diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h index 9ebb33a74..214258cc6 100644 --- a/Source/JavaScriptCore/runtime/ClassInfo.h +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -48,7 +48,7 @@ namespace JSC { typedef void (*PutFunctionPtr)(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); PutFunctionPtr put; - typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue); + typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); PutByIndexFunctionPtr putByIndex; typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, const Identifier&); diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h index d79e5c783..0d9580197 100644 --- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -27,10 +27,6 @@ // MarkedArgumentBuffer of property names, passed to a macro so we can do set them up various // ways without repeating the list. #define JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \ - macro(__defineGetter__) \ - macro(__defineSetter__) \ - macro(__lookupGetter__) \ - macro(__lookupSetter__) \ macro(apply) \ macro(arguments) \ macro(bind) \ @@ -52,6 +48,7 @@ macro(input) \ macro(isArray) \ macro(isPrototypeOf) \ + macro(lastIndex) \ macro(length) \ macro(message) \ macro(multiline) \ diff --git a/Source/JavaScriptCore/runtime/Completion.cpp b/Source/JavaScriptCore/runtime/Completion.cpp index 98a66589a..ce620245b 100644 --- a/Source/JavaScriptCore/runtime/Completion.cpp +++ b/Source/JavaScriptCore/runtime/Completion.cpp @@ -30,7 +30,7 @@ #include "Interpreter.h" #include "Parser.h" #include "Debugger.h" -#include "WTFThreadData.h" +#include <wtf/WTFThreadData.h> #include <stdio.h> namespace JSC { diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp index 243dc8856..5266c1ebe 100644 --- a/Source/JavaScriptCore/runtime/Error.cpp +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -36,6 +36,8 @@ #include "NativeErrorConstructor.h" #include "SourceCode.h" +#include <wtf/text/StringBuilder.h> + namespace JSC { static const char* linePropertyName = "line"; @@ -132,20 +134,14 @@ JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, cons globalObject = globalData->dynamicGlobalObject; else globalObject = error->globalObject(); - // We use the tryCreateUninitialized creation mechanism and related initialization - // functions as they're the only mechanism we currently have that will guarantee we - // don't call setters on the prototype. Technically it's faster than the alternative, - // but the numerous allocations that take place in this loop makes that last bit - // somewhat moot. - JSArray* stackTraceArray = JSArray::tryCreateUninitialized(*globalData, globalObject->arrayStructure(), stackTrace.size()); - if (!stackTraceArray) - return error; + StringBuilder builder; for (unsigned i = 0; i < stackTrace.size(); i++) { - UString stackLevel = stackTrace[i].toString(globalObject->globalExec()); - stackTraceArray->initializeIndex(*globalData, i, jsString(globalData, stackLevel)); + builder.append(String(stackTrace[i].toString(globalObject->globalExec()).impl())); + if (i != stackTrace.size() - 1) + builder.append('\n'); } - stackTraceArray->completeInitialization(stackTrace.size()); - error->putDirect(*globalData, globalData->propertyNames->stack, stackTraceArray, ReadOnly | DontDelete); + + error->putDirect(*globalData, globalData->propertyNames->stack, jsString(globalData, UString(builder.toString().impl())), ReadOnly | DontDelete); } return error; diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp index 25ddf764a..ea40447e4 100644 --- a/Source/JavaScriptCore/runtime/Executable.cpp +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -34,7 +34,7 @@ #include "JITDriver.h" #include "Parser.h" #include "UStringBuilder.h" -#include "Vector.h" +#include <wtf/Vector.h> namespace JSC { @@ -177,10 +177,9 @@ JSObject* EvalExecutable::compileOptimized(ExecState* exec, ScopeChainNode* scop } #if ENABLE(JIT) -void EvalExecutable::jitCompile(JSGlobalData& globalData) +bool EvalExecutable::jitCompile(JSGlobalData& globalData) { - bool result = jitCompileIfAppropriate(globalData, m_evalCodeBlock, m_jitCodeForCall, JITCode::bottomTierJIT()); - ASSERT_UNUSED(result, result); + return jitCompileIfAppropriate(globalData, m_evalCodeBlock, m_jitCodeForCall, JITCode::bottomTierJIT(), JITCompilationCanFail); } #endif @@ -210,8 +209,7 @@ JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scope JSGlobalData* globalData = &exec->globalData(); JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); - if (!!m_evalCodeBlock && m_evalCodeBlock->canProduceCopyWithBytecode()) { - BytecodeDestructionBlocker blocker(m_evalCodeBlock.get()); + if (!!m_evalCodeBlock) { OwnPtr<EvalCodeBlock> newCodeBlock = adoptPtr(new EvalCodeBlock(CodeBlock::CopyParsedBlock, *m_evalCodeBlock)); newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_evalCodeBlock.release())); m_evalCodeBlock = newCodeBlock.release(); @@ -223,7 +221,7 @@ JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scope ASSERT(exception); return exception; } - recordParse(evalNode->features(), evalNode->hasCapturedVariables(), evalNode->lineNo(), evalNode->lastLine()); + recordParse(evalNode->scopeFlags(), evalNode->hasCapturedVariables(), evalNode->lineNo(), evalNode->lastLine()); JSGlobalObject* globalObject = scopeChainNode->globalObject.get(); @@ -328,10 +326,9 @@ JSObject* ProgramExecutable::compileOptimized(ExecState* exec, ScopeChainNode* s } #if ENABLE(JIT) -void ProgramExecutable::jitCompile(JSGlobalData& globalData) +bool ProgramExecutable::jitCompile(JSGlobalData& globalData) { - bool result = jitCompileIfAppropriate(globalData, m_programCodeBlock, m_jitCodeForCall, JITCode::bottomTierJIT()); - ASSERT_UNUSED(result, result); + return jitCompileIfAppropriate(globalData, m_programCodeBlock, m_jitCodeForCall, JITCode::bottomTierJIT(), JITCompilationCanFail); } #endif @@ -346,8 +343,7 @@ JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* sc JSGlobalData* globalData = &exec->globalData(); JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); - if (!!m_programCodeBlock && m_programCodeBlock->canProduceCopyWithBytecode()) { - BytecodeDestructionBlocker blocker(m_programCodeBlock.get()); + if (!!m_programCodeBlock) { OwnPtr<ProgramCodeBlock> newCodeBlock = adoptPtr(new ProgramCodeBlock(CodeBlock::CopyParsedBlock, *m_programCodeBlock)); newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_programCodeBlock.release())); m_programCodeBlock = newCodeBlock.release(); @@ -357,7 +353,7 @@ JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* sc ASSERT(exception); return exception; } - recordParse(programNode->features(), programNode->hasCapturedVariables(), programNode->lineNo(), programNode->lastLine()); + recordParse(programNode->scopeFlags(), programNode->hasCapturedVariables(), programNode->lineNo(), programNode->lastLine()); JSGlobalObject* globalObject = scopeChainNode->globalObject.get(); @@ -479,33 +475,26 @@ JSObject* FunctionExecutable::compileOptimizedForConstruct(ExecState* exec, Scop } #if ENABLE(JIT) -void FunctionExecutable::jitCompileForCall(JSGlobalData& globalData) +bool FunctionExecutable::jitCompileForCall(JSGlobalData& globalData) { - bool result = jitCompileFunctionIfAppropriate(globalData, m_codeBlockForCall, m_jitCodeForCall, m_jitCodeForCallWithArityCheck, m_symbolTable, JITCode::bottomTierJIT()); - ASSERT_UNUSED(result, result); + return jitCompileFunctionIfAppropriate(globalData, m_codeBlockForCall, m_jitCodeForCall, m_jitCodeForCallWithArityCheck, m_symbolTable, JITCode::bottomTierJIT(), JITCompilationCanFail); } -void FunctionExecutable::jitCompileForConstruct(JSGlobalData& globalData) +bool FunctionExecutable::jitCompileForConstruct(JSGlobalData& globalData) { - bool result = jitCompileFunctionIfAppropriate(globalData, m_codeBlockForConstruct, m_jitCodeForConstruct, m_jitCodeForConstructWithArityCheck, m_symbolTable, JITCode::bottomTierJIT()); - ASSERT_UNUSED(result, result); + return jitCompileFunctionIfAppropriate(globalData, m_codeBlockForConstruct, m_jitCodeForConstruct, m_jitCodeForConstructWithArityCheck, m_symbolTable, JITCode::bottomTierJIT(), JITCompilationCanFail); } #endif FunctionCodeBlock* FunctionExecutable::codeBlockWithBytecodeFor(CodeSpecializationKind kind) { - FunctionCodeBlock* codeBlock = baselineCodeBlockFor(kind); - if (codeBlock->canProduceCopyWithBytecode()) - return codeBlock; - return 0; + return baselineCodeBlockFor(kind); } PassOwnPtr<FunctionCodeBlock> FunctionExecutable::produceCodeBlockFor(ScopeChainNode* scopeChainNode, CompilationKind compilationKind, CodeSpecializationKind specializationKind, JSObject*& exception) { - if (!!codeBlockFor(specializationKind) && codeBlockFor(specializationKind)->canProduceCopyWithBytecode()) { - BytecodeDestructionBlocker blocker(codeBlockFor(specializationKind).get()); + if (!!codeBlockFor(specializationKind)) return adoptPtr(new FunctionCodeBlock(CodeBlock::CopyParsedBlock, *codeBlockFor(specializationKind))); - } exception = 0; JSGlobalData* globalData = scopeChainNode->globalData; @@ -519,7 +508,7 @@ PassOwnPtr<FunctionCodeBlock> FunctionExecutable::produceCodeBlockFor(ScopeChain if (m_forceUsesArguments) body->setUsesArguments(); body->finishParsing(m_parameters, m_name); - recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); + recordParse(body->scopeFlags(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); OwnPtr<FunctionCodeBlock> result; ASSERT((compilationKind == FirstCompilation) == !codeBlockFor(specializationKind)); diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h index 69e80b28e..08b39fcf0 100644 --- a/Source/JavaScriptCore/runtime/Executable.h +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -180,13 +180,14 @@ namespace JSC { class NativeExecutable : public ExecutableBase { friend class JIT; + friend class LLIntOffsetsExtractor; public: typedef ExecutableBase Base; #if ENABLE(JIT) static NativeExecutable* create(JSGlobalData& globalData, MacroAssemblerCodeRef callThunk, NativeFunction function, MacroAssemblerCodeRef constructThunk, NativeFunction constructor, Intrinsic intrinsic) { - ASSERT(globalData.canUseJIT()); + ASSERT(!globalData.interpreter->classicEnabled()); NativeExecutable* executable; if (!callThunk) { executable = new (NotNull, allocateCell<NativeExecutable>(globalData.heap)) NativeExecutable(globalData, function, constructor); @@ -228,7 +229,7 @@ namespace JSC { #if ENABLE(JIT) void finishCreation(JSGlobalData& globalData, JITCode callThunk, JITCode constructThunk, Intrinsic intrinsic) { - ASSERT(globalData.canUseJIT()); + ASSERT(!globalData.interpreter->classicEnabled()); Base::finishCreation(globalData); m_jitCodeForCall = callThunk; m_jitCodeForConstruct = constructThunk; @@ -270,14 +271,14 @@ namespace JSC { ScriptExecutable(Structure* structure, JSGlobalData& globalData, const SourceCode& source, bool isInStrictContext) : ExecutableBase(globalData, structure, NUM_PARAMETERS_NOT_COMPILED) , m_source(source) - , m_features(isInStrictContext ? StrictModeFeature : 0) + , m_scopeFlags(isInStrictContext ? StrictModeFlag : NoScopeFlags) { } ScriptExecutable(Structure* structure, ExecState* exec, const SourceCode& source, bool isInStrictContext) : ExecutableBase(exec->globalData(), structure, NUM_PARAMETERS_NOT_COMPILED) , m_source(source) - , m_features(isInStrictContext ? StrictModeFeature : 0) + , m_scopeFlags(isInStrictContext ? StrictModeFlag : NoScopeFlags) { } @@ -291,10 +292,10 @@ namespace JSC { int lineNo() const { return m_firstLine; } int lastLine() const { return m_lastLine; } - bool usesEval() const { return m_features & EvalFeature; } - bool usesArguments() const { return m_features & ArgumentsFeature; } - bool needsActivation() const { return m_hasCapturedVariables || m_features & (EvalFeature | WithFeature | CatchFeature); } - bool isStrictMode() const { return m_features & StrictModeFeature; } + bool usesEval() const { return m_scopeFlags & UsesEvalFlag; } + bool usesArguments() const { return m_scopeFlags & UsesArgumentsFlag; } + bool needsActivation() const { return m_hasCapturedVariables || m_scopeFlags & (UsesEvalFlag | UsesWithFlag | UsesCatchFlag); } + bool isStrictMode() const { return m_scopeFlags & StrictModeFlag; } void unlinkCalls(); @@ -310,16 +311,16 @@ namespace JSC { #endif } - void recordParse(CodeFeatures features, bool hasCapturedVariables, int firstLine, int lastLine) + void recordParse(ScopeFlags scopeFlags, bool hasCapturedVariables, int firstLine, int lastLine) { - m_features = features; + m_scopeFlags = scopeFlags; m_hasCapturedVariables = hasCapturedVariables; m_firstLine = firstLine; m_lastLine = lastLine; } SourceCode m_source; - CodeFeatures m_features; + ScopeFlags m_scopeFlags; bool m_hasCapturedVariables; int m_firstLine; int m_lastLine; @@ -346,7 +347,7 @@ namespace JSC { #if ENABLE(JIT) void jettisonOptimizedCode(JSGlobalData&); - void jitCompile(JSGlobalData&); + bool jitCompile(JSGlobalData&); #endif EvalCodeBlock& generatedBytecode() @@ -421,7 +422,7 @@ namespace JSC { #if ENABLE(JIT) void jettisonOptimizedCode(JSGlobalData&); - void jitCompile(JSGlobalData&); + bool jitCompile(JSGlobalData&); #endif ProgramCodeBlock& generatedBytecode() @@ -520,7 +521,7 @@ namespace JSC { #if ENABLE(JIT) void jettisonOptimizedCodeForCall(JSGlobalData&); - void jitCompileForCall(JSGlobalData&); + bool jitCompileForCall(JSGlobalData&); #endif bool isGeneratedForCall() const @@ -548,7 +549,7 @@ namespace JSC { #if ENABLE(JIT) void jettisonOptimizedCodeForConstruct(JSGlobalData&); - void jitCompileForConstruct(JSGlobalData&); + bool jitCompileForConstruct(JSGlobalData&); #endif bool isGeneratedForConstruct() const @@ -597,14 +598,12 @@ namespace JSC { } } - void jitCompileFor(JSGlobalData& globalData, CodeSpecializationKind kind) + bool jitCompileFor(JSGlobalData& globalData, CodeSpecializationKind kind) { - if (kind == CodeForCall) { - jitCompileForCall(globalData); - return; - } + if (kind == CodeForCall) + return jitCompileForCall(globalData); ASSERT(kind == CodeForConstruct); - jitCompileForConstruct(globalData); + return jitCompileForConstruct(globalData); } #endif diff --git a/Source/JavaScriptCore/runtime/ExecutionHarness.h b/Source/JavaScriptCore/runtime/ExecutionHarness.h index 774c5bf6b..e58e6fc74 100644 --- a/Source/JavaScriptCore/runtime/ExecutionHarness.h +++ b/Source/JavaScriptCore/runtime/ExecutionHarness.h @@ -46,7 +46,7 @@ inline bool prepareForExecution(JSGlobalData& globalData, OwnPtr<CodeBlockType>& return true; } #endif // ENABLE(LLINT) - return jitCompileIfAppropriate(globalData, codeBlock, jitCode, jitType); + return jitCompileIfAppropriate(globalData, codeBlock, jitCode, jitType, JITCode::isBaselineCode(jitType) ? JITCompilationMustSucceed : JITCompilationCanFail); } inline bool prepareFunctionForExecution(JSGlobalData& globalData, OwnPtr<FunctionCodeBlock>& codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck, SharedSymbolTable*& symbolTable, JITCode::JITType jitType, CodeSpecializationKind kind) @@ -61,7 +61,7 @@ inline bool prepareFunctionForExecution(JSGlobalData& globalData, OwnPtr<Functio #else UNUSED_PARAM(kind); #endif // ENABLE(LLINT) - return jitCompileFunctionIfAppropriate(globalData, codeBlock, jitCode, jitCodeWithArityCheck, symbolTable, jitType); + return jitCompileFunctionIfAppropriate(globalData, codeBlock, jitCode, jitCodeWithArityCheck, symbolTable, jitType, JITCode::isBaselineCode(jitType) ? JITCompilationMustSucceed : JITCompilationCanFail); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp index 2e878bfe3..7f45f0746 100644 --- a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp +++ b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp @@ -53,7 +53,6 @@ struct DefaultGCActivityCallbackPlatformData { }; const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10; -const CFTimeInterval triggerInterval = 2; // seconds void DefaultGCActivityCallbackPlatformData::trigger(CFRunLoopTimerRef timer, void *info) { @@ -95,6 +94,7 @@ void DefaultGCActivityCallback::commonConstructor(Heap* heap, CFRunLoopRef runLo void DefaultGCActivityCallback::operator()() { + CFTimeInterval triggerInterval = static_cast<Heap*>(d->context.info)->lastGCLength() * 100.0; CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + triggerInterval); } diff --git a/Source/JavaScriptCore/runtime/Identifier.cpp b/Source/JavaScriptCore/runtime/Identifier.cpp index 0d233e355..fbc5787ce 100644 --- a/Source/JavaScriptCore/runtime/Identifier.cpp +++ b/Source/JavaScriptCore/runtime/Identifier.cpp @@ -36,20 +36,6 @@ using WTF::ThreadSpecific; namespace JSC { -IdentifierTable::~IdentifierTable() -{ - HashSet<StringImpl*>::iterator end = m_table.end(); - for (HashSet<StringImpl*>::iterator iter = m_table.begin(); iter != end; ++iter) - (*iter)->setIsIdentifier(false); -} - -std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(StringImpl* value) -{ - std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add(value); - (*result.first)->setIsIdentifier(true); - return result; -} - IdentifierTable* createIdentifierTable() { return new IdentifierTable; diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h index 947c95b33..b9e5a1854 100644 --- a/Source/JavaScriptCore/runtime/Identifier.h +++ b/Source/JavaScriptCore/runtime/Identifier.h @@ -22,7 +22,7 @@ #define Identifier_h #include "JSGlobalData.h" -#include "ThreadSpecific.h" +#include <wtf/ThreadSpecific.h> #include "UString.h" #include <wtf/WTFThreadData.h> #include <wtf/text/CString.h> diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp index 2b874c708..b6fd6ce1f 100644 --- a/Source/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -37,7 +37,7 @@ #include "JSGlobalObject.h" #include "UString.h" #include "WriteBarrier.h" -#include "dtoa.h" +#include <wtf/dtoa.h> #include <wtf/Threading.h> #include <wtf/dtoa/cached-powers.h> diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index 71d520018..4244bc31c 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -41,6 +41,7 @@ using namespace WTF; namespace JSC { + ASSERT_CLASS_FITS_IN_CELL(JSArray); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray); @@ -104,23 +105,16 @@ const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CRE // This value is capped by the constant FIRST_VECTOR_GROW defined above. static unsigned lastArraySize = 0; -static inline size_t storageSize(unsigned vectorLength) +static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) { - ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH); - - // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH) - // - as asserted above - the following calculation cannot overflow. - size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>)); - // Assertion to detect integer overflow in previous calculation (should not be possible, provided that - // MAX_STORAGE_VECTOR_LENGTH is correctly defined). - ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)))); - - return size; + return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; } -static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) +static bool reject(ExecState* exec, bool throwException, const char* message) { - return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; + if (throwException) + throwTypeError(exec, message); + return false; } #if !CHECK_ARRAY_CONSISTENCY @@ -213,7 +207,7 @@ inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(J return result; } -inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value) +inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) { std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i); SparseArrayEntry& entry = result.first->second; @@ -223,14 +217,15 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i // extensible, this is not the right thing to have done - so remove again. if (result.second && !array->isExtensible()) { remove(result.first); - // FIXME: should throw in strict mode. + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } if (!(entry.attributes & Accessor)) { if (entry.attributes & ReadOnly) { - // FIXME: should throw if being called from strict mode. - // throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } @@ -243,8 +238,8 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i JSObject* setter = asGetterSetter(accessor)->setter(); if (!setter) { - // FIXME: should throw if being called from strict mode. - // throwTypeError(exec, "setting a property that has only a getter"); + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } @@ -255,6 +250,24 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i call(exec, setter, callType, callData, array, args); } +inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) +{ + std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i); + SparseArrayEntry& entry = result.first->second; + + // To save a separate find & add, we first always add to the sparse map. + // In the uncommon case that this is a new property, and the array is not + // extensible, this is not the right thing to have done - so remove again. + if (result.second && !array->isExtensible()) { + remove(result.first); + return reject(exec, shouldThrow, "Attempting to define property on object that is not extensible."); + } + + entry.attributes = 0; + entry.set(exec->globalData(), array, value); + return true; +} + inline void SparseArrayEntry::get(PropertySlot& slot) const { JSValue value = Base::get(); @@ -395,13 +408,6 @@ void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, Prope entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); } -static bool reject(ExecState* exec, bool throwException, const char* message) -{ - if (throwException) - throwTypeError(exec, message); - return false; -} - // Defined in ES5.1 8.12.9 bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) { @@ -414,8 +420,7 @@ bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, Property // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). if (!descriptor.attributes()) { ASSERT(!descriptor.isAccessorDescriptor()); - putByIndex(this, exec, index, descriptor.value()); - return true; + return putDirectIndex(exec, index, descriptor.value(), throwException); } enterDictionaryMode(exec->globalData()); @@ -721,7 +726,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, bool isArrayIndex; unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex) { - putByIndex(thisObject, exec, i, value); + putByIndex(thisObject, exec, i, value, slot.isStrictMode()); return; } @@ -738,7 +743,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSObject::put(thisObject, exec, propertyName, value, slot); } -void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value) +void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow) { JSArray* thisObject = jsCast<JSArray*>(cell); thisObject->checkConsistency(); @@ -765,17 +770,17 @@ void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue valu // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property. if (UNLIKELY(i > MAX_ARRAY_INDEX)) { - PutPropertySlot slot; + PutPropertySlot slot(shouldThrow); thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot); return; } // For all other cases, call putByIndexBeyondVectorLength. - thisObject->putByIndexBeyondVectorLength(exec, i, value); + thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow); thisObject->checkConsistency(); } -NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value) +void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) { JSGlobalData& globalData = exec->globalData(); @@ -806,7 +811,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. allocateSparseMap(exec->globalData()); map = m_sparseValueMap; - map->put(exec, this, i, value); + map->put(exec, this, i, value, shouldThrow); return; } @@ -815,7 +820,8 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne if (i >= length) { // Prohibit growing the array if length is not writable. if (map->lengthIsReadOnly() || !isExtensible()) { - // FIXME: should throw in strict mode. + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } length = i + 1; @@ -826,7 +832,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) { - map->put(exec, this, i, value); + map->put(exec, this, i, value, shouldThrow); return; } @@ -848,6 +854,77 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne valueSlot.set(globalData, this, value); } +bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) +{ + JSGlobalData& globalData = exec->globalData(); + + // i should be a valid array index that is outside of the current vector. + ASSERT(i >= m_vectorLength); + ASSERT(i <= MAX_ARRAY_INDEX); + + ArrayStorage* storage = m_storage; + SparseArrayValueMap* map = m_sparseValueMap; + + // First, handle cases where we don't currently have a sparse map. + if (LIKELY(!map)) { + // If the array is not extensible, we should have entered dictionary mode, and created the spare map. + ASSERT(isExtensible()); + + // Update m_length if necessary. + if (i >= storage->m_length) + storage->m_length = i + 1; + + // Check that it is sensible to still be using a vector, and then try to grow the vector. + if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) { + // success! - reread m_storage since it has likely been reallocated, and store to the vector. + storage = m_storage; + storage->m_vector[i].set(globalData, this, value); + ++storage->m_numValuesInVector; + return true; + } + // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. + allocateSparseMap(exec->globalData()); + map = m_sparseValueMap; + return map->putDirect(exec, this, i, value, shouldThrow); + } + + // Update m_length if necessary. + unsigned length = storage->m_length; + if (i >= length) { + // Prohibit growing the array if length is not writable. + if (map->lengthIsReadOnly()) + return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError); + if (!isExtensible()) + return reject(exec, shouldThrow, "Attempting to define property on object that is not extensible."); + length = i + 1; + storage->m_length = length; + } + + // We are currently using a map - check whether we still want to be doing so. + // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. + unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); + if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) + return map->putDirect(exec, this, i, value, shouldThrow); + + // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector. + storage = m_storage; + storage->m_numValuesInVector = numValuesInArray; + + // Copy all values from the map into the vector, and delete the map. + WriteBarrier<Unknown>* vector = storage->m_vector; + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) + vector[it->first].set(globalData, this, it->second.getNonSparseMode()); + deallocateSparseMap(); + + // Store the new property into the vector. + WriteBarrier<Unknown>& valueSlot = vector[i]; + if (!valueSlot) + ++storage->m_numValuesInVector; + valueSlot.set(globalData, this, value); + return true; +} + bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) { JSArray* thisObject = jsCast<JSArray*>(cell); @@ -1220,18 +1297,19 @@ void JSArray::push(ExecState* exec, JSValue value) // Pushing to an array of length 2^32-1 stores the property, but throws a range error. if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) { - methodTable()->putByIndex(this, exec, storage->m_length, value); + methodTable()->putByIndex(this, exec, storage->m_length, value, true); // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. - throwError(exec, createRangeError(exec, "Invalid array length")); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); return; } // Handled the same as putIndex. - putByIndexBeyondVectorLength(exec, storage->m_length, value); + putByIndexBeyondVectorLength(exec, storage->m_length, value, true); checkConsistency(); } -void JSArray::shiftCount(ExecState* exec, unsigned count) +bool JSArray::shiftCount(ExecState*, unsigned count) { ASSERT(count > 0); @@ -1239,32 +1317,15 @@ void JSArray::shiftCount(ExecState* exec, unsigned count) unsigned oldLength = storage->m_length; - if (!oldLength) - return; - - if (oldLength != storage->m_numValuesInVector) { - // If m_length and m_numValuesInVector aren't the same, we have a sparse vector - // which means we need to go through each entry looking for the the "empty" - // slots and then fill them with possible properties. See ECMA spec. - // 15.4.4.9 steps 11 through 13. - for (unsigned i = count; i < oldLength; ++i) { - if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { - PropertySlot slot(this); - JSValue p = prototype(); - if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) - methodTable()->putByIndex(this, exec, i, slot.getValue(exec, i)); - } - } - - storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. + // If the array contains holes or is otherwise in an abnormal state, + // use the generic algorithm in ArrayPrototype. + if (oldLength != storage->m_numValuesInVector || inSparseMode()) + return false; - // Need to decrement numValuesInvector based on number of real entries - for (unsigned i = 0; i < (unsigned)count; ++i) - if ((i < m_vectorLength) && (storage->m_vector[i])) - --storage->m_numValuesInVector; - } else - storage->m_numValuesInVector -= count; + if (!oldLength) + return true; + storage->m_numValuesInVector -= count; storage->m_length -= count; if (m_vectorLength) { @@ -1280,30 +1341,20 @@ void JSArray::shiftCount(ExecState* exec, unsigned count) m_indexBias += count; } } + return true; } - -void JSArray::unshiftCount(ExecState* exec, unsigned count) + +// Returns true if the unshift can be handled, false to fallback. +bool JSArray::unshiftCount(ExecState* exec, unsigned count) { ArrayStorage* storage = m_storage; unsigned length = storage->m_length; - if (length != storage->m_numValuesInVector) { - // If m_length and m_numValuesInVector aren't the same, we have a sparse vector - // which means we need to go through each entry looking for the the "empty" - // slots and then fill them with possible properties. See ECMA spec. - // 15.4.4.13 steps 8 through 10. - for (unsigned i = 0; i < length; ++i) { - if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { - PropertySlot slot(this); - JSValue p = prototype(); - if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) - methodTable()->putByIndex(this, exec, i, slot.getValue(exec, i)); - } - } - } - - storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. - + // If the array contains holes or is otherwise in an abnormal state, + // use the generic algorithm in ArrayPrototype. + if (length != storage->m_numValuesInVector || inSparseMode()) + return false; + if (m_indexBias >= count) { m_indexBias -= count; char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(WriteBarrier<Unknown>); @@ -1312,12 +1363,13 @@ void JSArray::unshiftCount(ExecState* exec, unsigned count) m_vectorLength += count; } else if (!unshiftCountSlowCase(exec->globalData(), count)) { throwOutOfMemoryError(exec); - return; + return true; } WriteBarrier<Unknown>* vector = m_storage->m_vector; for (unsigned i = 0; i < count; i++) vector[i].clear(); + return true; } void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor) diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index 3bb4c6320..ad98d6619 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -85,7 +85,8 @@ namespace JSC { } // These methods may mutate the contents of the map - void put(ExecState*, JSArray*, unsigned, JSValue); + void put(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow); + bool putDirect(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow); std::pair<iterator, bool> add(JSArray*, unsigned); iterator find(unsigned i) { return m_map.find(i); } // This should ASSERT the remove is valid (check the result of the find). @@ -122,11 +123,17 @@ namespace JSC { uintptr_t m_padding; #endif WriteBarrier<Unknown> m_vector[1]; + + static ptrdiff_t lengthOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_length); } + static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector); } + static ptrdiff_t allocBaseOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_allocBase); } + static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_vector); } }; class JSArray : public JSNonFinalObject { friend class LLIntOffsetsExtractor; friend class Walker; + friend class JIT; protected: JS_EXPORT_PRIVATE explicit JSArray(JSGlobalData&, Structure*); @@ -153,7 +160,19 @@ namespace JSC { static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); - static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + // This is similar to the JSObject::putDirect* methods: + // - the prototype chain is not consulted + // - accessors are not called. + // This method creates a property with attributes writable, enumerable and configurable all set to true. + bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow = true) + { + if (canSetIndex(propertyName)) { + setIndex(exec->globalData(), propertyName, value); + return true; + } + return putDirectIndexBeyondVectorLength(exec, propertyName, value, shouldThrow); + } static JS_EXPORTDATA const ClassInfo s_info; @@ -168,8 +187,8 @@ namespace JSC { void push(ExecState*, JSValue); JSValue pop(ExecState*); - void shiftCount(ExecState*, unsigned count); - void unshiftCount(ExecState*, unsigned count); + bool shiftCount(ExecState*, unsigned count); + bool unshiftCount(ExecState*, unsigned count); bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; } JSValue getIndex(unsigned i) @@ -262,6 +281,7 @@ namespace JSC { JS_EXPORT_PRIVATE void setSubclassData(void*); private: + static size_t storageSize(unsigned vectorLength); bool isLengthWritable() { SparseArrayValueMap* map = m_sparseValueMap; @@ -275,7 +295,8 @@ namespace JSC { void deallocateSparseMap(); bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&); - void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue); + void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); unsigned getNewVectorLength(unsigned desiredLength); bool increaseVectorLength(JSGlobalData&, unsigned newLength); @@ -293,6 +314,10 @@ namespace JSC { // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell? SparseArrayValueMap* m_sparseValueMap; void* m_subclassData; // A JSArray subclass can use this to fill the vector lazily. + + static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); } + static ptrdiff_t subclassDataOffset() { return OBJECT_OFFSETOF(JSArray, m_subclassData); } + static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); } }; inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength) @@ -334,6 +359,30 @@ namespace JSC { return i; } -} // namespace JSC +// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize +// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage +// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + +// (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t). +#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>)) + +// These values have to be macros to be used in max() and min() without introducing +// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. +#define MIN_SPARSE_ARRAY_INDEX 10000U +#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1) + inline size_t JSArray::storageSize(unsigned vectorLength) + { + ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH); + + // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH) + // - as asserted above - the following calculation cannot overflow. + size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>)); + // Assertion to detect integer overflow in previous calculation (should not be possible, provided that + // MAX_STORAGE_VECTOR_LENGTH is correctly defined). + ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)))); + + return size; + } + + } // namespace JSC #endif // JSArray_h diff --git a/Source/JavaScriptCore/runtime/JSByteArray.cpp b/Source/JavaScriptCore/runtime/JSByteArray.cpp index 3df21e6f5..39ea4d0b9 100644 --- a/Source/JavaScriptCore/runtime/JSByteArray.cpp +++ b/Source/JavaScriptCore/runtime/JSByteArray.cpp @@ -102,7 +102,7 @@ void JSByteArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyN JSObject::put(thisObject, exec, propertyName, value, slot); } -void JSByteArray::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value) +void JSByteArray::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool) { jsCast<JSByteArray*>(cell)->setIndex(exec, propertyName, value); } diff --git a/Source/JavaScriptCore/runtime/JSByteArray.h b/Source/JavaScriptCore/runtime/JSByteArray.h index d1f4ad630..06181d901 100644 --- a/Source/JavaScriptCore/runtime/JSByteArray.h +++ b/Source/JavaScriptCore/runtime/JSByteArray.h @@ -92,7 +92,7 @@ namespace JSC { JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&); JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); JS_EXPORT_PRIVATE static void put(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&); - JS_EXPORT_PRIVATE static void putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue); + JS_EXPORT_PRIVATE static void putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow); JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, EnumerationMode); diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp index 4703b681b..f08d0260a 100644 --- a/Source/JavaScriptCore/runtime/JSCell.cpp +++ b/Source/JavaScriptCore/runtime/JSCell.cpp @@ -97,14 +97,23 @@ bool JSCell::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i void JSCell::put(JSCell* cell, ExecState* exec, const Identifier& identifier, JSValue value, PutPropertySlot& slot) { + if (cell->isString()) { + JSValue(cell).putToPrimitive(exec, identifier, value, slot); + return; + } JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); thisObject->methodTable()->put(thisObject, exec, identifier, value, slot); } -void JSCell::putByIndex(JSCell* cell, ExecState* exec, unsigned identifier, JSValue value) +void JSCell::putByIndex(JSCell* cell, ExecState* exec, unsigned identifier, JSValue value, bool shouldThrow) { + if (cell->isString()) { + PutPropertySlot slot(shouldThrow); + JSValue(cell).putToPrimitive(exec, Identifier::from(exec, identifier), value, slot); + return; + } JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); - thisObject->methodTable()->putByIndex(thisObject, exec, identifier, value); + thisObject->methodTable()->putByIndex(thisObject, exec, identifier, value, shouldThrow); } bool JSCell::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& identifier) diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 78d2d0801..2ef359b76 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -107,7 +107,7 @@ namespace JSC { const ClassInfo* validatedClassInfo() const; const MethodTable* methodTable() const; static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp index 863073338..882f86fa0 100644 --- a/Source/JavaScriptCore/runtime/JSDateMath.cpp +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp @@ -72,8 +72,6 @@ #include "config.h" #include "JSDateMath.h" -#include "Assertions.h" -#include "ASCIICType.h" #include "CurrentTime.h" #include "JSObject.h" #include "MathExtras.h" @@ -86,6 +84,8 @@ #include <limits> #include <stdint.h> #include <time.h> +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> #include <wtf/text/StringBuilder.h> #if HAVE(ERRNO_H) diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp index 253128279..fa798f41a 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -181,7 +181,15 @@ JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identi { JSFunction* thisObj = asFunction(slotBase); ASSERT(!thisObj->isHostFunction()); - return exec->interpreter()->retrieveCallerFromVMCode(exec, thisObj); + JSValue caller = exec->interpreter()->retrieveCallerFromVMCode(exec, thisObj); + + // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller. + if (!caller.isObject() || !asObject(caller)->inherits(&JSFunction::s_info)) + return caller; + JSFunction* function = asFunction(caller); + if (function->isHostFunction() || !function->jsExecutable()->isStrictMode()) + return caller; + return throwTypeError(exec, "Function.caller used to retrieve strict caller"); } JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) @@ -329,21 +337,80 @@ void JSFunction::put(JSCell* cell, ExecState* exec, const Identifier& propertyNa Base::put(thisObject, exec, propertyName, value, slot); return; } - if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().caller) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; + } Base::put(thisObject, exec, propertyName, value, slot); } bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) { JSFunction* thisObject = jsCast<JSFunction*>(cell); - if (thisObject->isHostFunction()) - return Base::deleteProperty(thisObject, exec, propertyName); - if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty. + if (!thisObject->isHostFunction() && !exec->globalData().isInDefineOwnProperty() + && (propertyName == exec->propertyNames().arguments + || propertyName == exec->propertyNames().length + || propertyName == exec->propertyNames().prototype + || propertyName == exec->propertyNames().caller)) return false; return Base::deleteProperty(thisObject, exec, propertyName); } +bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) +{ + JSFunction* thisObject = jsCast<JSFunction*>(object); + if (thisObject->isHostFunction()) + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + + if (propertyName == exec->propertyNames().prototype) { + // Make sure prototype has been reified, such that it can only be overwritten + // following the rules set out in ECMA-262 8.12.9. + PropertySlot slot; + thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); + } else if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().caller) { + if (!object->isExtensible()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible.")); + return false; + } + if (descriptor.configurablePresent() && descriptor.configurable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); + return false; + } + if (descriptor.isAccessorDescriptor()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); + return false; + } + if (descriptor.writablePresent() && descriptor.writable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); + return false; + } + if (!descriptor.value()) + return true; + if (propertyName == exec->propertyNames().arguments && sameValue(exec, descriptor.value(), exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObject))) + return true; + if (propertyName == exec->propertyNames().length && sameValue(exec, descriptor.value(), jsNumber(thisObject->jsExecutable()->parameterCount()))) + return true; + if (propertyName == exec->propertyNames().caller && sameValue(exec, descriptor.value(), exec->interpreter()->retrieveCallerFromVMCode(exec, thisObject))) + return true; + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property.")); + return false; + } + + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); +} + // ECMA 13.2.2 [[Construct]] ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData) { diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h index 6e8557f59..288181060 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.h +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -133,6 +133,7 @@ namespace JSC { static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties); + static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index 2bdc28ab7..f138e75fb 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -193,7 +193,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); -#if ENABLE(JIT) && ENABLE(CLASSIC_INTERPRETER) +#if ENABLE(JIT) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) #if USE(CF) CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); @@ -213,13 +213,16 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread #endif #endif #if ENABLE(JIT) -#if ENABLE(CLASSIC_INTERPRETER) +#if ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT) if (m_canUseJIT) m_canUseJIT = executableAllocator.isValid(); + + if (!Options::useJIT) + m_canUseJIT = false; #endif jitStubs = adoptPtr(new JITThunks(this)); #endif - + interpreter->initialize(&llintData, this->canUseJIT()); initializeHostCallReturnValue(); // This is needed to convince the linker not to drop host call return support. diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h index 7e54c00db..acbcee816 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.h +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -47,6 +47,7 @@ #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/RefCounted.h> +#include <wtf/SimpleStats.h> #include <wtf/ThreadSpecific.h> #include <wtf/WTFThreadData.h> #if ENABLE(REGEXP_TRACING) @@ -201,6 +202,7 @@ namespace JSC { SmallStrings smallStrings; NumericStrings numericStrings; DateInstanceCache dateInstanceCache; + WTF::SimpleStats machineCodeBytesPerBytecodeWordForBaselineJIT; Vector<CodeBlock*> codeBlocksBeingCompiled; void startedCompiling(CodeBlock* codeBlock) { @@ -229,7 +231,7 @@ namespace JSC { #if !ENABLE(JIT) bool canUseJIT() { return false; } // interpreter only -#elif !ENABLE(CLASSIC_INTERPRETER) +#elif !ENABLE(CLASSIC_INTERPRETER) && !ENABLE(LLINT) bool canUseJIT() { return true; } // jit only #else bool canUseJIT() { return m_canUseJIT; } @@ -367,7 +369,7 @@ namespace JSC { JSGlobalData(GlobalDataType, ThreadStackType, HeapSize); static JSGlobalData*& sharedInstanceInternal(); void createNativeThunk(); -#if ENABLE(JIT) && ENABLE(CLASSIC_INTERPRETER) +#if ENABLE(JIT) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) bool m_canUseJIT; #endif #if ENABLE(GC_VALIDATION) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index db8ee1d85..75789e602 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -35,7 +35,7 @@ #include "Nodes.h" #include "Parser.h" #include "UStringBuilder.h" -#include "dtoa.h" +#include <wtf/dtoa.h> #include <stdio.h> #include <stdlib.h> #include <wtf/ASCIICType.h> diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.cpp b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp index 682400292..337e3a70c 100644 --- a/Source/JavaScriptCore/runtime/JSNotAnObject.cpp +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -70,7 +70,7 @@ void JSNotAnObject::put(JSCell*, ExecState* exec, const Identifier& , JSValue, P ASSERT_UNUSED(exec, exec->hadException()); } -void JSNotAnObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue) +void JSNotAnObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue, bool) { ASSERT_UNUSED(exec, exec->hadException()); } diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.h b/Source/JavaScriptCore/runtime/JSNotAnObject.h index 51ba456e1..5f80688b0 100644 --- a/Source/JavaScriptCore/runtime/JSNotAnObject.h +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.h @@ -73,7 +73,7 @@ namespace JSC { static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp index 83b118429..436e78353 100644 --- a/Source/JavaScriptCore/runtime/JSONObject.cpp +++ b/Source/JavaScriptCore/runtime/JSONObject.cpp @@ -704,12 +704,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) JSValue filteredValue = callReviver(array, jsString(m_exec, UString::number(indexStack.last())), outValue); if (filteredValue.isUndefined()) array->methodTable()->deletePropertyByIndex(array, m_exec, indexStack.last()); - else { - if (isJSArray(array) && array->canSetIndex(indexStack.last())) - array->setIndex(m_exec->globalData(), indexStack.last(), filteredValue); - else - array->methodTable()->putByIndex(array, m_exec, indexStack.last(), filteredValue); - } + else + array->putDirectIndex(m_exec, indexStack.last(), filteredValue, false); if (m_exec->hadException()) return jsNull(); indexStack.last()++; diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index acc4a181e..1bdb90ff6 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -119,11 +119,6 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); } -static void throwSetterError(ExecState* exec) -{ - throwError(exec, createTypeError(exec, "setting a property that has only a getter")); -} - // ECMA 8.6.2.2 void JSObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { @@ -161,7 +156,7 @@ void JSObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { if (slot.isStrictMode()) - throwSetterError(exec); + throwError(exec, createTypeError(exec, "setting a property that has only a getter")); return; } @@ -190,9 +185,9 @@ void JSObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName return; } -void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value) +void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) { - PutPropertySlot slot; + PutPropertySlot slot(shouldThrow); JSObject* thisObject = jsCast<JSObject*>(cell); thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); } @@ -707,7 +702,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identi if (descriptor.isGenericDescriptor()) { if (!current.attributesEqual(descriptor)) { object->methodTable()->deleteProperty(object, exec, propertyName); - return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current); + return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } return true; } @@ -720,7 +715,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identi return false; } object->methodTable()->deleteProperty(object, exec, propertyName); - return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current); + return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } // Changing the value and attributes of an existing property @@ -742,7 +737,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identi if (current.attributesEqual(descriptor) && !descriptor.value()) return true; object->methodTable()->deleteProperty(object, exec, propertyName); - return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current); + return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } // Changing the accessor functions of an existing accessor property diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index c117cffaf..3f3d281cf 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -109,7 +109,7 @@ namespace JSC { bool allowsAccessFrom(ExecState*); JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); // putDirect is effectively an unchecked vesion of 'defineOwnProperty': // - the prototype chain is not consulted @@ -834,21 +834,20 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { if (UNLIKELY(!isCell())) { - JSObject* thisObject = synthesizeObject(exec); - thisObject->methodTable()->put(thisObject, exec, propertyName, value, slot); + putToPrimitive(exec, propertyName, value, slot); return; } asCell()->methodTable()->put(asCell(), exec, propertyName, value, slot); } -inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) +inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) { if (UNLIKELY(!isCell())) { - JSObject* thisObject = synthesizeObject(exec); - thisObject->methodTable()->putByIndex(thisObject, exec, propertyName, value); + PutPropertySlot slot(shouldThrow); + putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot); return; } - asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value); + asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value, shouldThrow); } // --- JSValue inlines ---------------------------- diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp index cfa7d03b4..e84ce3620 100644 --- a/Source/JavaScriptCore/runtime/JSString.cpp +++ b/Source/JavaScriptCore/runtime/JSString.cpp @@ -65,9 +65,10 @@ void JSString::resolveRope(ExecState* exec) const if (is8Bit()) { LChar* buffer; - if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) + if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { + Heap::heap(this)->reportExtraMemoryCost(newImpl->cost()); m_value = newImpl.release(); - else { + } else { outOfMemory(exec); return; } @@ -92,9 +93,10 @@ void JSString::resolveRope(ExecState* exec) const } UChar* buffer; - if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) + if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { + Heap::heap(this)->reportExtraMemoryCost(newImpl->cost()); m_value = newImpl.release(); - else { + } else { outOfMemory(exec); return; } diff --git a/Source/JavaScriptCore/runtime/JSStringBuilder.h b/Source/JavaScriptCore/runtime/JSStringBuilder.h index b7e7e781e..1a2b812f0 100644 --- a/Source/JavaScriptCore/runtime/JSStringBuilder.h +++ b/Source/JavaScriptCore/runtime/JSStringBuilder.h @@ -29,7 +29,7 @@ #include "ExceptionHelpers.h" #include "JSString.h" #include "UStringConcatenate.h" -#include "Vector.h" +#include <wtf/Vector.h> namespace JSC { diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index e3843f02b..36697c60c 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -27,6 +27,7 @@ #include "BooleanPrototype.h" #include "Error.h" #include "ExceptionHelpers.h" +#include "GetterSetter.h" #include "JSGlobalObject.h" #include "JSFunction.h" #include "JSNotAnObject.h" @@ -90,30 +91,85 @@ JSObject* JSValue::toThisObjectSlowCase(ExecState* exec) const return exec->globalThisValue(); } -JSObject* JSValue::synthesizeObject(ExecState* exec) const +JSObject* JSValue::synthesizePrototype(ExecState* exec) const { - ASSERT(!isCell()); + if (isCell()) { + ASSERT(isString()); + return exec->lexicalGlobalObject()->stringPrototype(); + } + if (isNumber()) - return constructNumber(exec, exec->lexicalGlobalObject(), asValue()); + return exec->lexicalGlobalObject()->numberPrototype(); if (isBoolean()) - return constructBooleanFromImmediateBoolean(exec, exec->lexicalGlobalObject(), asValue()); + return exec->lexicalGlobalObject()->booleanPrototype(); ASSERT(isUndefinedOrNull()); throwError(exec, createNotAnObjectError(exec, *this)); return JSNotAnObject::create(exec); } -JSObject* JSValue::synthesizePrototype(ExecState* exec) const +// ECMA 8.7.2 +void JSValue::putToPrimitive(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { - ASSERT(!isCell()); - if (isNumber()) - return exec->lexicalGlobalObject()->numberPrototype(); - if (isBoolean()) - return exec->lexicalGlobalObject()->booleanPrototype(); + JSGlobalData& globalData = exec->globalData(); - ASSERT(isUndefinedOrNull()); - throwError(exec, createNotAnObjectError(exec, *this)); - return JSNotAnObject::create(exec); + // Check if there are any setters or getters in the prototype chain + JSObject* obj = synthesizePrototype(exec); + JSValue prototype; + if (propertyName != exec->propertyNames().underscoreProto) { + for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { + prototype = obj->prototype(); + if (prototype.isNull()) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + } + } + + for (; ; obj = asObject(prototype)) { + unsigned attributes; + JSCell* specificValue; + size_t offset = obj->structure()->get(globalData, propertyName, attributes, specificValue); + if (offset != WTF::notFound) { + if (attributes & ReadOnly) { + if (slot.isStrictMode()) + throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + return; + } + + JSValue gs = obj->getDirectOffset(offset); + if (gs.isGetterSetter()) { + JSObject* setterFunc = asGetterSetter(gs)->setter(); + if (!setterFunc) { + if (slot.isStrictMode()) + throwError(exec, createTypeError(exec, "setting a property that has only a getter")); + return; + } + + CallData callData; + CallType callType = setterFunc->methodTable()->getCallData(setterFunc, callData); + MarkedArgumentBuffer args; + args.append(value); + + // If this is WebCore's global object then we need to substitute the shell. + call(exec, setterFunc, callType, callData, *this, args); + return; + } + + // If there's an existing property on the object or one of its + // prototypes it should be replaced, so break here. + break; + } + + prototype = obj->prototype(); + if (prototype.isNull()) + break; + } + + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; } char* JSValue::description() diff --git a/Source/JavaScriptCore/runtime/JSValue.h b/Source/JavaScriptCore/runtime/JSValue.h index 9f797e05d..a6f359360 100644 --- a/Source/JavaScriptCore/runtime/JSValue.h +++ b/Source/JavaScriptCore/runtime/JSValue.h @@ -221,7 +221,8 @@ namespace JSC { JSValue get(ExecState*, unsigned propertyName) const; JSValue get(ExecState*, unsigned propertyName, PropertySlot&) const; void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - void put(ExecState*, unsigned propertyName, JSValue); + void putToPrimitive(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + void putByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); JSObject* toThisObject(ExecState*) const; @@ -252,8 +253,6 @@ namespace JSC { JS_EXPORT_PRIVATE JSObject* toObjectSlowCase(ExecState*, JSGlobalObject*) const; JS_EXPORT_PRIVATE JSObject* toThisObjectSlowCase(ExecState*) const; - JSObject* synthesizeObject(ExecState*) const; - #if USE(JSVALUE32_64) /* * On 32-bit platforms USE(JSVALUE32_64) should be defined, and we use a NaN-encoded diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.h b/Source/JavaScriptCore/runtime/JSVariableObject.h index 8d058f1fc..bcfe4ab89 100644 --- a/Source/JavaScriptCore/runtime/JSVariableObject.h +++ b/Source/JavaScriptCore/runtime/JSVariableObject.h @@ -32,7 +32,7 @@ #include "JSObject.h" #include "Register.h" #include "SymbolTable.h" -#include "UnusedParam.h" +#include <wtf/UnusedParam.h> #include <wtf/OwnArrayPtr.h> #include <wtf/UnusedParam.h> diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.cpp b/Source/JavaScriptCore/runtime/NumberPrototype.cpp index fb90bcd17..8f8c3c00f 100644 --- a/Source/JavaScriptCore/runtime/NumberPrototype.cpp +++ b/Source/JavaScriptCore/runtime/NumberPrototype.cpp @@ -29,7 +29,7 @@ #include "JSString.h" #include "Operations.h" #include "Uint16WithFraction.h" -#include "dtoa.h" +#include <wtf/dtoa.h> #include <wtf/Assertions.h> #include <wtf/MathExtras.h> #include <wtf/Vector.h> diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp index 5500508cf..c4bf39db4 100644 --- a/Source/JavaScriptCore/runtime/Options.cpp +++ b/Source/JavaScriptCore/runtime/Options.cpp @@ -45,6 +45,8 @@ namespace JSC { namespace Options { +bool useJIT; + unsigned maximumOptimizationCandidateInstructionCount; unsigned maximumFunctionForCallInlineCandidateInstructionCount; @@ -52,15 +54,12 @@ unsigned maximumFunctionForConstructInlineCandidateInstructionCount; unsigned maximumInliningDepth; -int32_t executionCounterValueForJITAfterWarmUp; -int32_t executionCounterValueForDontJITAnytimeSoon; -int32_t executionCounterValueForJITSoon; +int32_t thresholdForJITAfterWarmUp; +int32_t thresholdForJITSoon; -int32_t executionCounterValueForOptimizeAfterWarmUp; -int32_t executionCounterValueForOptimizeAfterLongWarmUp; -int32_t executionCounterValueForDontOptimizeAnytimeSoon; -int32_t executionCounterValueForOptimizeSoon; -int32_t executionCounterValueForOptimizeNextInvocation; +int32_t thresholdForOptimizeAfterWarmUp; +int32_t thresholdForOptimizeAfterLongWarmUp; +int32_t thresholdForOptimizeSoon; int32_t executionCounterIncrementForLoop; int32_t executionCounterIncrementForReturn; @@ -96,6 +95,19 @@ unsigned numberOfGCMarkers; unsigned opaqueRootMergeThreshold; #if ENABLE(RUN_TIME_HEURISTICS) +static bool parse(const char* string, bool& value) +{ + if (!strcasecmp(string, "true") || !strcasecmp(string, "yes") || !strcmp(string, "1")) { + value = true; + return true; + } + if (!strcasecmp(string, "false") || !strcasecmp(string, "no") || !strcmp(string, "0")) { + value = false; + return true; + } + return false; +} + static bool parse(const char* string, int32_t& value) { return sscanf(string, "%d", &value) == 1; @@ -134,22 +146,21 @@ void setHeuristic(T& variable, const char* name, U value) void initializeOptions() { - SET(maximumOptimizationCandidateInstructionCount, 1100); + SET(useJIT, true); + + SET(maximumOptimizationCandidateInstructionCount, 10000); SET(maximumFunctionForCallInlineCandidateInstructionCount, 180); SET(maximumFunctionForConstructInlineCandidateInstructionCount, 100); SET(maximumInliningDepth, 5); - SET(executionCounterValueForJITAfterWarmUp, -100); - SET(executionCounterValueForDontJITAnytimeSoon, std::numeric_limits<int32_t>::min()); - SET(executionCounterValueForJITSoon, -100); + SET(thresholdForJITAfterWarmUp, 100); + SET(thresholdForJITSoon, 100); - SET(executionCounterValueForOptimizeAfterWarmUp, -1000); - SET(executionCounterValueForOptimizeAfterLongWarmUp, -5000); - SET(executionCounterValueForDontOptimizeAnytimeSoon, std::numeric_limits<int32_t>::min()); - SET(executionCounterValueForOptimizeSoon, -1000); - SET(executionCounterValueForOptimizeNextInvocation, 0); + SET(thresholdForOptimizeAfterWarmUp, 1000); + SET(thresholdForOptimizeAfterLongWarmUp, 5000); + SET(thresholdForOptimizeSoon, 1000); SET(executionCounterIncrementForLoop, 1); SET(executionCounterIncrementForReturn, 15); @@ -193,15 +204,11 @@ void initializeOptions() if (cpusToUse < 1) cpusToUse = 1; - cpusToUse = 1; - SET(numberOfGCMarkers, cpusToUse); - ASSERT(executionCounterValueForDontOptimizeAnytimeSoon <= executionCounterValueForOptimizeAfterLongWarmUp); - ASSERT(executionCounterValueForOptimizeAfterLongWarmUp <= executionCounterValueForOptimizeAfterWarmUp); - ASSERT(executionCounterValueForOptimizeAfterWarmUp <= executionCounterValueForOptimizeSoon); - ASSERT(executionCounterValueForOptimizeAfterWarmUp < 0); - ASSERT(executionCounterValueForOptimizeSoon <= executionCounterValueForOptimizeNextInvocation); + ASSERT(thresholdForOptimizeAfterLongWarmUp >= thresholdForOptimizeAfterWarmUp); + ASSERT(thresholdForOptimizeAfterWarmUp >= thresholdForOptimizeSoon); + ASSERT(thresholdForOptimizeAfterWarmUp >= 0); // Compute the maximum value of the reoptimization retry counter. This is simply // the largest value at which we don't overflow the execute counter, when using it @@ -209,11 +216,11 @@ void initializeOptions() // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles // total on a 32-bit processor. reoptimizationRetryCounterMax = 0; - while ((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) >= static_cast<int64_t>(std::numeric_limits<int32_t>::min())) + while ((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max())) reoptimizationRetryCounterMax++; - ASSERT((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) < 0); - ASSERT((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) >= static_cast<int64_t>(std::numeric_limits<int32_t>::min())); + ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) > 0); + ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max())); } } } // namespace JSC::Options diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h index b9e68f90c..fae6a7376 100644 --- a/Source/JavaScriptCore/runtime/Options.h +++ b/Source/JavaScriptCore/runtime/Options.h @@ -30,6 +30,8 @@ namespace JSC { namespace Options { +extern bool useJIT; + extern unsigned maximumOptimizationCandidateInstructionCount; extern unsigned maximumFunctionForCallInlineCandidateInstructionCount; @@ -37,15 +39,13 @@ extern unsigned maximumFunctionForConstructInlineCandidateInstructionCount; extern unsigned maximumInliningDepth; // Depth of inline stack, so 1 = no inlining, 2 = one level, etc. -extern int32_t executionCounterValueForJITAfterWarmUp; -extern int32_t executionCounterValueForDontJITAnytimeSoon; -extern int32_t executionCounterValueForJITSoon; +extern int32_t thresholdForJITAfterWarmUp; +extern int32_t thresholdForJITSoon; -extern int32_t executionCounterValueForOptimizeAfterWarmUp; -extern int32_t executionCounterValueForOptimizeAfterLongWarmUp; -extern int32_t executionCounterValueForDontOptimizeAnytimeSoon; -extern int32_t executionCounterValueForOptimizeSoon; -extern int32_t executionCounterValueForOptimizeNextInvocation; +extern int32_t thresholdForOptimizeAfterWarmUp; +extern int32_t thresholdForOptimizeAfterLongWarmUp; +extern int32_t thresholdForOptimizeSoon; +extern int32_t thresholdForOptimizeNextInvocation; extern int32_t executionCounterIncrementForLoop; extern int32_t executionCounterIncrementForReturn; diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp index 0cb629584..236a8e5ae 100644 --- a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp +++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp @@ -206,22 +206,6 @@ bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const return true; } -unsigned PropertyDescriptor::attributesWithOverride(const PropertyDescriptor& other) const -{ - unsigned mismatch = other.m_attributes ^ m_attributes; - unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes; - unsigned newAttributes = m_attributes & defaultAttributes; - if (sharedSeen & WritablePresent && mismatch & ReadOnly) - newAttributes ^= ReadOnly; - if (sharedSeen & ConfigurablePresent && mismatch & DontDelete) - newAttributes ^= DontDelete; - if (sharedSeen & EnumerablePresent && mismatch & DontEnum) - newAttributes ^= DontEnum; - if (isAccessorDescriptor() && other.isDataDescriptor()) - newAttributes |= ReadOnly; - return newAttributes; -} - unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const { unsigned currentAttributes = current.m_attributes; diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.h b/Source/JavaScriptCore/runtime/PropertyDescriptor.h index 98af02e66..2c3878f57 100644 --- a/Source/JavaScriptCore/runtime/PropertyDescriptor.h +++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.h @@ -70,7 +70,6 @@ namespace JSC { bool getterPresent() const { return m_getter; } bool equalTo(ExecState* exec, const PropertyDescriptor& other) const; bool attributesEqual(const PropertyDescriptor& other) const; - unsigned attributesWithOverride(const PropertyDescriptor& other) const; unsigned attributesOverridingCurrent(const PropertyDescriptor& current) const; private: diff --git a/Source/JavaScriptCore/runtime/PropertySlot.cpp b/Source/JavaScriptCore/runtime/PropertySlot.cpp index edabd7a6e..8ac874115 100644 --- a/Source/JavaScriptCore/runtime/PropertySlot.cpp +++ b/Source/JavaScriptCore/runtime/PropertySlot.cpp @@ -34,11 +34,7 @@ JSValue PropertySlot::functionGetter(ExecState* exec) const CallData callData; CallType callType = m_data.getterFunc->methodTable()->getCallData(m_data.getterFunc, callData); - - // Only objects can have accessor properties. - // If the base is WebCore's global object then we need to substitute the shell. - ASSERT(m_slotBase.isObject()); - return call(exec, m_data.getterFunc, callType, callData, m_thisValue.toThisObject(exec), exec->emptyList()); + return call(exec, m_data.getterFunc, callType, callData, m_thisValue.isObject() ? m_thisValue.toThisObject(exec) : m_thisValue, exec->emptyList()); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp index 2b7feb4b5..1a3362b2d 100644 --- a/Source/JavaScriptCore/runtime/RegExp.cpp +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -363,7 +363,33 @@ int RegExp::match(JSGlobalData& globalData, const UString& s, unsigned startOffs #endif } else #endif - result = Yarr::interpret(m_representation->m_regExpBytecode.get(), s, startOffset, s.length(), offsetVector); + result = Yarr::interpret(m_representation->m_regExpBytecode.get(), s, startOffset, s.length(), reinterpret_cast<unsigned*>(offsetVector)); + + // FIXME: The YARR engine should handle unsigned or size_t length matches. + // The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed. + // The offset vector handling needs to change as well. + // Right now we convert a match where the offsets overflowed into match failure. + // There are two places in WebCore that call the interpreter directly that need to + // have their offsets changed to int as well. They are platform/text/RegularExpression.cpp + // and inspector/ContentSearchUtils.cpp. + if (s.length() > INT_MAX) { + bool overflowed = false; + + if (result < -1) + overflowed = true; + + for (unsigned i = 0; i <= m_numSubpatterns; i++) { + if ((offsetVector[i*2] < -1) || ((offsetVector[i*2] >= 0) && (offsetVector[i*2+1] < -1))) { + overflowed = true; + offsetVector[i*2] = -1; + offsetVector[i*2+1] = -1; + } + } + + if (overflowed) + result = -1; + } + ASSERT(result >= -1); #if REGEXP_FUNC_TEST_DATA_GEN diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp index 53e880e70..90082f07e 100644 --- a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -153,9 +153,9 @@ void RegExpMatchesArray::fillArrayInstance(ExecState* exec) for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { int start = m_regExpResult.ovector[2 * i]; if (start >= 0) - JSArray::putByIndex(this, exec, i, jsSubstring(exec, m_regExpResult.input, start, m_regExpResult.ovector[2 * i + 1] - start)); + putDirectIndex(exec, i, jsSubstring(exec, m_regExpResult.input, start, m_regExpResult.ovector[2 * i + 1] - start), false); else - JSArray::putByIndex(this, exec, i, jsUndefined()); + putDirectIndex(exec, i, jsUndefined(), false); } PutPropertySlot slot; diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h index c34920d8d..a3c4497fc 100644 --- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -82,12 +82,12 @@ namespace JSC { JSArray::put(thisObject, exec, propertyName, v, slot); } - static void putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue v) + static void putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue v, bool shouldThrow) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); if (!thisObject->m_didFillArrayInstance) thisObject->fillArrayInstance(exec); - JSArray::putByIndex(thisObject, exec, propertyName, v); + JSArray::putByIndex(thisObject, exec, propertyName, v, shouldThrow); } static bool deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index 4c192ff90..a81799c46 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,8 +40,6 @@ static JSValue regExpObjectGlobal(ExecState*, JSValue, const Identifier&); static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, const Identifier&); static JSValue regExpObjectMultiline(ExecState*, JSValue, const Identifier&); static JSValue regExpObjectSource(ExecState*, JSValue, const Identifier&); -static JSValue regExpObjectLastIndex(ExecState*, JSValue, const Identifier&); -static void setRegExpObjectLastIndex(ExecState*, JSObject*, JSValue); } // namespace JSC @@ -59,14 +57,15 @@ const ClassInfo RegExpObject::s_info = { "RegExp", &JSNonFinalObject::s_info, 0, ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum source regExpObjectSource DontDelete|ReadOnly|DontEnum - lastIndex regExpObjectLastIndex DontDelete|DontEnum @end */ RegExpObject::RegExpObject(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) : JSNonFinalObject(globalObject->globalData(), structure) - , d(adoptPtr(new RegExpObjectData(globalObject->globalData(), this, regExp))) + , m_regExp(globalObject->globalData(), this, regExp) + , m_lastIndexIsWritable(true) { + m_lastIndex.setWithoutWriteBarrier(jsNumber(0)); } void RegExpObject::finishCreation(JSGlobalObject* globalObject) @@ -75,11 +74,6 @@ void RegExpObject::finishCreation(JSGlobalObject* globalObject) ASSERT(inherits(&s_info)); } -void RegExpObject::destroy(JSCell* cell) -{ - jsCast<RegExpObject*>(cell)->RegExpObject::~RegExpObject(); -} - void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { RegExpObject* thisObject = jsCast<RegExpObject*>(cell); @@ -87,22 +81,87 @@ void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); - if (thisObject->d->regExp) - visitor.append(&thisObject->d->regExp); - if (UNLIKELY(!thisObject->d->lastIndex.get().isInt32())) - visitor.append(&thisObject->d->lastIndex); + if (thisObject->m_regExp) + visitor.append(&thisObject->m_regExp); + if (UNLIKELY(!thisObject->m_lastIndex.get().isInt32())) + visitor.append(&thisObject->m_lastIndex); } bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { + if (propertyName == exec->propertyNames().lastIndex) { + RegExpObject* regExp = asRegExpObject(cell); + slot.setValue(regExp, regExp->getLastIndex()); + return true; + } return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), propertyName, slot); } bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { + if (propertyName == exec->propertyNames().lastIndex) { + RegExpObject* regExp = asRegExpObject(object); + descriptor.setDescriptor(regExp->getLastIndex(), regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly); + return true; + } return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, descriptor); } +bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().lastIndex) + return false; + return Base::deleteProperty(cell, exec, propertyName); +} + +void RegExpObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getOwnPropertyNames(object, exec, propertyNames, mode); +} + +void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getPropertyNames(object, exec, propertyNames, mode); +} + +static bool reject(ExecState* exec, bool throwException, const char* message) +{ + if (throwException) + throwTypeError(exec, message); + return false; +} + +bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool shouldThrow) +{ + if (propertyName == exec->propertyNames().lastIndex) { + RegExpObject* regExp = asRegExpObject(object); + if (descriptor.configurablePresent() && descriptor.configurable()) + return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property."); + if (descriptor.enumerablePresent() && descriptor.enumerable()) + return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property."); + if (descriptor.isAccessorDescriptor()) + return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property."); + if (!regExp->m_lastIndexIsWritable) { + if (descriptor.writablePresent() && descriptor.writable()) + return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property."); + if (!sameValue(exec, regExp->getLastIndex(), descriptor.value())) + return reject(exec, shouldThrow, "Attempting to change value of a readonly property."); + return true; + } + if (descriptor.writablePresent() && !descriptor.writable()) + regExp->m_lastIndexIsWritable = false; + if (descriptor.value()) + regExp->setLastIndex(exec, descriptor.value(), false); + return true; + } + + return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); +} + JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, const Identifier&) { return jsBoolean(asRegExpObject(slotBase)->regExp()->global()); @@ -127,6 +186,14 @@ JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, const Identifier&) bool inBrackets = false; bool shouldEscape = false; + // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/', + // and also states that the result must be a valid RegularExpressionLiteral. '//' is + // not a valid RegularExpressionLiteral (since it is a single line comment), and hence + // source cannot ever validly be "". If the source is empty, return a different Pattern + // that would match the same thing. + if (!length) + return jsString(exec, "(?:)"); + // early return for strings that don't contain a forwards slash and LineTerminator for (unsigned i = 0; i < length; ++i) { UChar ch = characters[i]; @@ -200,21 +267,15 @@ JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, const Identifier&) return jsString(exec, result.toUString()); } -JSValue regExpObjectLastIndex(ExecState*, JSValue slotBase, const Identifier&) -{ - return asRegExpObject(slotBase)->getLastIndex(); -} - void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { + if (propertyName == exec->propertyNames().lastIndex) { + asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode()); + return; + } lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot); } -void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue value) -{ - asRegExpObject(baseObject)->setLastIndex(exec->globalData(), value); -} - JSValue RegExpObject::test(ExecState* exec) { return jsBoolean(match(exec)); @@ -236,7 +297,7 @@ bool RegExpObject::match(ExecState* exec) if (!regExp()->global()) { int position; int length; - regExpConstructor->performMatch(*globalData, d->regExp.get(), input, 0, position, length); + regExpConstructor->performMatch(*globalData, m_regExp.get(), input, 0, position, length); return position >= 0; } @@ -245,13 +306,13 @@ bool RegExpObject::match(ExecState* exec) if (LIKELY(jsLastIndex.isUInt32())) { lastIndex = jsLastIndex.asUInt32(); if (lastIndex > input.length()) { - setLastIndex(0); + setLastIndex(exec, 0); return false; } } else { double doubleLastIndex = jsLastIndex.toInteger(exec); if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { - setLastIndex(0); + setLastIndex(exec, 0); return false; } lastIndex = static_cast<unsigned>(doubleLastIndex); @@ -259,13 +320,13 @@ bool RegExpObject::match(ExecState* exec) int position; int length = 0; - regExpConstructor->performMatch(*globalData, d->regExp.get(), input, lastIndex, position, length); + regExpConstructor->performMatch(*globalData, m_regExp.get(), input, lastIndex, position, length); if (position < 0) { - setLastIndex(0); + setLastIndex(exec, 0); return false; } - setLastIndex(position + length); + setLastIndex(exec, position + length); return true; } diff --git a/Source/JavaScriptCore/runtime/RegExpObject.h b/Source/JavaScriptCore/runtime/RegExpObject.h index 081a7f111..456cfa683 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.h +++ b/Source/JavaScriptCore/runtime/RegExpObject.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -44,20 +44,27 @@ namespace JSC { return object; } - void setRegExp(JSGlobalData& globalData, RegExp* r) { d->regExp.set(globalData, this, r); } - RegExp* regExp() const { return d->regExp.get(); } + void setRegExp(JSGlobalData& globalData, RegExp* r) { m_regExp.set(globalData, this, r); } + RegExp* regExp() const { return m_regExp.get(); } - void setLastIndex(size_t lastIndex) + void setLastIndex(ExecState* exec, size_t lastIndex) { - d->lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex)); + m_lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex)); + if (LIKELY(m_lastIndexIsWritable)) + m_lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex)); + else + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); } - void setLastIndex(JSGlobalData& globalData, JSValue lastIndex) + void setLastIndex(ExecState* exec, JSValue lastIndex, bool shouldThrow) { - d->lastIndex.set(globalData, this, lastIndex); + if (LIKELY(m_lastIndexIsWritable)) + m_lastIndex.set(exec->globalData(), this, lastIndex); + else if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); } JSValue getLastIndex() const { - return d->lastIndex.get(); + return m_lastIndex.get(); } JSValue test(ExecState*); @@ -77,31 +84,22 @@ namespace JSC { protected: JS_EXPORT_PRIVATE RegExpObject(JSGlobalObject*, Structure*, RegExp*); JS_EXPORT_PRIVATE void finishCreation(JSGlobalObject*); - static void destroy(JSCell*); static const unsigned StructureFlags = OverridesVisitChildren | OverridesGetOwnPropertySlot | Base::StructureFlags; static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); + private: bool match(ExecState*); - struct RegExpObjectData { - WTF_MAKE_FAST_ALLOCATED; - public: - RegExpObjectData(JSGlobalData& globalData, RegExpObject* owner, RegExp* regExp) - : regExp(globalData, owner, regExp) - { - lastIndex.setWithoutWriteBarrier(jsNumber(0)); - } - - WriteBarrier<RegExp> regExp; - WriteBarrier<Unknown> lastIndex; - }; -#if COMPILER(MSVC) - friend void WTF::deleteOwnedPtr<RegExpObjectData>(RegExpObjectData*); -#endif - OwnPtr<RegExpObjectData> d; + WriteBarrier<RegExp> m_regExp; + WriteBarrier<Unknown> m_lastIndex; + bool m_lastIndexIsWritable; }; RegExpObject* asRegExpObject(JSValue); diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp index 9074e97c3..8e4b5a9d5 100644 --- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -129,7 +129,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage())); asRegExpObject(thisValue)->setRegExp(exec->globalData(), regExp); - asRegExpObject(thisValue)->setLastIndex(0); + asRegExpObject(thisValue)->setLastIndex(exec, 0); return JSValue::encode(jsUndefined()); } @@ -155,7 +155,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec) postfix[index] = 'm'; UString source = thisObject->get(exec, exec->propertyNames().source).toString(exec)->value(exec); // If source is empty, use "/(?:)/" to avoid colliding with comment syntax - return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix)); + return JSValue::encode(jsMakeNontrivialString(exec, "/", source, postfix)); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringObject.cpp b/Source/JavaScriptCore/runtime/StringObject.cpp index 4a24698fb..d7e1c8a17 100644 --- a/Source/JavaScriptCore/runtime/StringObject.cpp +++ b/Source/JavaScriptCore/runtime/StringObject.cpp @@ -21,6 +21,7 @@ #include "config.h" #include "StringObject.h" +#include "Error.h" #include "PropertyNameArray.h" namespace JSC { @@ -68,11 +69,56 @@ bool StringObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, c void StringObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { - if (propertyName == exec->propertyNames().length) + if (propertyName == exec->propertyNames().length) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; + } JSObject::put(cell, exec, propertyName, value, slot); } +bool StringObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) +{ + StringObject* thisObject = jsCast<StringObject*>(object); + + if (propertyName == exec->propertyNames().length) { + if (!object->isExtensible()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible.")); + return false; + } + if (descriptor.configurablePresent() && descriptor.configurable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); + return false; + } + if (descriptor.isAccessorDescriptor()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); + return false; + } + if (descriptor.writablePresent() && descriptor.writable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); + return false; + } + if (!descriptor.value()) + return true; + if (propertyName == exec->propertyNames().length && sameValue(exec, descriptor.value(), jsNumber(thisObject->internalValue()->length()))) + return true; + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property.")); + return false; + } + + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); +} + bool StringObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) { StringObject* thisObject = jsCast<StringObject*>(cell); diff --git a/Source/JavaScriptCore/runtime/StringObject.h b/Source/JavaScriptCore/runtime/StringObject.h index 248c71601..bad5595c3 100644 --- a/Source/JavaScriptCore/runtime/StringObject.h +++ b/Source/JavaScriptCore/runtime/StringObject.h @@ -53,6 +53,7 @@ namespace JSC { static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); static const JS_EXPORTDATA ClassInfo s_info; diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp index 63c00e27e..708c1fb77 100644 --- a/Source/JavaScriptCore/runtime/StringPrototype.cpp +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -455,11 +455,19 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS unsigned sourceLen = source.length(); if (exec->hadException()) return JSValue::encode(JSValue()); - RegExp* regExp = asRegExpObject(searchValue)->regExp(); + RegExpObject* regExpObject = asRegExpObject(searchValue); + RegExp* regExp = regExpObject->regExp(); bool global = regExp->global(); - if (global && callType == CallTypeNone && !replacementString.length()) - return removeUsingRegExpSearch(exec, string, source, regExp); + if (global) { + // ES5.1 15.5.4.10 step 8.a. + regExpObject->setLastIndex(exec, 0); + if (exec->hadException()) + return JSValue::encode(JSValue()); + + if (callType == CallTypeNone && !replacementString.length()) + return removeUsingRegExpSearch(exec, string, source, regExp); + } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); @@ -808,9 +816,17 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) JSValue a0 = exec->argument(0); RegExp* reg; - if (a0.inherits(&RegExpObject::s_info)) - reg = asRegExpObject(a0)->regExp(); - else { + bool global = false; + if (a0.inherits(&RegExpObject::s_info)) { + RegExpObject* regExpObject = asRegExpObject(a0); + reg = regExpObject->regExp(); + if ((global = reg->global())) { + // ES5.1 15.5.4.10 step 8.a. + regExpObject->setLastIndex(exec, 0); + if (exec->hadException()) + return JSValue::encode(JSValue()); + } + } else { /* * ECMA 15.5.4.12 String.prototype.search (regexp) * If regexp is not an object whose [[Class]] property is "RegExp", it is @@ -825,7 +841,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) int pos; int matchLength = 0; regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength); - if (!(reg->global())) { + if (!global) { // case without 'g' flag is handled like RegExp.prototype.exec if (pos < 0) return JSValue::encode(jsNull()); @@ -948,7 +964,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) if (separatorValue.isUndefined()) { // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0", // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. - result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input), false); // b. Return A. return JSValue::encode(result); } @@ -961,7 +977,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. // d. Return A. if (reg->match(*globalData, input, 0) < 0) - result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input), false); return JSValue::encode(result); } @@ -992,7 +1008,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // through q (exclusive). // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. - result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position)); + result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position), false); // 3. Increment lengthA by 1. // 4. If lengthA == lim, return A. if (++resultLength == limit) @@ -1011,7 +1027,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]: // true, [[Enumerable]]: true, [[Configurable]]: true}, and false. int sub = ovector[i * 2]; - result->methodTable()->putByIndex(result, exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub)); + result->putDirectIndex(exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub), false); // c Increment lengthA by 1. // d If lengthA == lim, return A. if (++resultLength == limit) @@ -1030,7 +1046,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) if (separatorValue.isUndefined()) { // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0", // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. - result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input), false); // b. Return A. return JSValue::encode(result); } @@ -1043,7 +1059,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. // d. Return A. if (!separator.isEmpty()) - result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input), false); return JSValue::encode(result); } @@ -1054,7 +1070,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) ASSERT(limit); do { - result->methodTable()->putByIndex(result, exec, position, jsSingleCharacterSubstring(exec, input, position)); + result->putDirectIndex(exec, position, jsSingleCharacterSubstring(exec, input, position), false); } while (++position < limit); return JSValue::encode(result); @@ -1071,7 +1087,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // through q (exclusive). // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. - result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position)); + result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position), false); // 3. Increment lengthA by 1. // 4. If lengthA == lim, return A. if (++resultLength == limit) @@ -1087,7 +1103,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // through s (exclusive). // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. - result->methodTable()->putByIndex(result, exec, resultLength++, jsSubstring(exec, input, position, input.length() - position)); + result->putDirectIndex(exec, resultLength++, jsSubstring(exec, input, position, input.length() - position), false); // 16. Return A. return JSValue::encode(result); diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h index 81731222b..f540a12c7 100644 --- a/Source/JavaScriptCore/runtime/SymbolTable.h +++ b/Source/JavaScriptCore/runtime/SymbolTable.h @@ -125,7 +125,7 @@ namespace JSC { public: static PassRefPtr<SharedSymbolTable> create() { return adoptRef(new SharedSymbolTable); } private: - SharedSymbolTable() { deprecatedTurnOffVerifier(); } + SharedSymbolTable() { turnOffVerifier(); } }; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/WriteBarrier.h b/Source/JavaScriptCore/runtime/WriteBarrier.h index 7e9db12fb..05f3bc5bb 100644 --- a/Source/JavaScriptCore/runtime/WriteBarrier.h +++ b/Source/JavaScriptCore/runtime/WriteBarrier.h @@ -30,7 +30,7 @@ #include "HandleTypes.h" #include "Heap.h" #include "SamplingCounter.h" -#include "TypeTraits.h" +#include <wtf/TypeTraits.h> namespace JSC { |