diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-22 09:09:45 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-22 09:10:13 +0100 |
commit | 470286ecfe79d59df14944e5b5d34630fc739391 (patch) | |
tree | 43983212872e06cebefd2ae474418fa2908ca54c /Source/JavaScriptCore/runtime | |
parent | 23037105e948c2065da5a937d3a2396b0ff45c1e (diff) | |
download | qtwebkit-470286ecfe79d59df14944e5b5d34630fc739391.tar.gz |
Imported WebKit commit e89504fa9195b2063b2530961d4b73dd08de3242 (http://svn.webkit.org/repository/webkit/trunk@135485)
Change-Id: I03774e5ac79721c13ffa30d152537a74d0b12e66
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'Source/JavaScriptCore/runtime')
50 files changed, 2204 insertions, 826 deletions
diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index 7961d4bc8..8ae991422 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -34,246 +34,246 @@ namespace JSC { - class Arguments : public JSDestructibleObject { - friend class JIT; - friend class DFG::SpeculativeJIT; - public: - typedef JSDestructibleObject Base; - - static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame) - { - Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); - arguments->finishCreation(callFrame); - return arguments; - } - - static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame, InlineCallFrame* inlineCallFrame) - { - Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); - arguments->finishCreation(callFrame, inlineCallFrame); - return arguments; - } - - enum { MaxArguments = 0x10000 }; - - private: - enum NoParametersType { NoParameters }; - - Arguments(CallFrame*); - Arguments(CallFrame*, NoParametersType); - - void tearOffForInlineCallFrame(JSGlobalData& globalData, Register*, InlineCallFrame*); +class Arguments : public JSDestructibleObject { + friend class JIT; + friend class DFG::SpeculativeJIT; +public: + typedef JSDestructibleObject Base; - public: - static const ClassInfo s_info; - - static void visitChildren(JSCell*, SlotVisitor&); - - void fillArgList(ExecState*, MarkedArgumentBuffer&); - - uint32_t length(ExecState* exec) const - { - if (UNLIKELY(m_overrodeLength)) - return get(exec, exec->propertyNames().length).toUInt32(exec); - return m_numArguments; - } - - void copyToArguments(ExecState*, CallFrame*, uint32_t length); - void tearOff(CallFrame*); - void tearOff(CallFrame*, InlineCallFrame*); - bool isTornOff() const { return m_registerArray; } - void didTearOffActivation(ExecState*, JSActivation*); - - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } - - protected: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; - - void finishCreation(CallFrame*); - void finishCreation(CallFrame*, InlineCallFrame*); - - private: - static void destroy(JSCell*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); - static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); - static bool deleteProperty(JSCell*, ExecState*, PropertyName); - static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); - static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); - void createStrictModeCallerIfNecessary(ExecState*); - void createStrictModeCalleeIfNecessary(ExecState*); - - bool isArgument(size_t); - bool trySetArgument(JSGlobalData&, size_t argument, JSValue); - JSValue tryGetArgument(size_t argument); - bool isDeletedArgument(size_t); - bool tryDeleteArgument(size_t); - WriteBarrierBase<Unknown>& argument(size_t); - void allocateSlowArguments(); - - void init(CallFrame*); - - WriteBarrier<JSActivation> m_activation; - - unsigned m_numArguments; - - // We make these full byte booleans to make them easy to test from the JIT, - // and because even if they were single-bit booleans we still wouldn't save - // any space. - bool m_overrodeLength; - bool m_overrodeCallee; - bool m_overrodeCaller; - bool m_isStrictMode; - - WriteBarrierBase<Unknown>* m_registers; - OwnArrayPtr<WriteBarrier<Unknown> > m_registerArray; - - OwnArrayPtr<SlowArgument> m_slowArguments; - - WriteBarrier<JSFunction> m_callee; - }; - - Arguments* asArguments(JSValue); - - inline Arguments* asArguments(JSValue value) + static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame) { - ASSERT(asObject(value)->inherits(&Arguments::s_info)); - return static_cast<Arguments*>(asObject(value)); + Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame); + return arguments; } - - inline Arguments::Arguments(CallFrame* callFrame) - : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) + + static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame, InlineCallFrame* inlineCallFrame) { + Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame, inlineCallFrame); + return arguments; } - inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) - : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) - { - } + enum { MaxArguments = 0x10000 }; - inline void Arguments::allocateSlowArguments() - { - if (m_slowArguments) - return; - m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); - for (size_t i = 0; i < m_numArguments; ++i) { - ASSERT(m_slowArguments[i].status == SlowArgument::Normal); - m_slowArguments[i].index = CallFrame::argumentOffset(i); - } - } +private: + enum NoParametersType { NoParameters }; + + Arguments(CallFrame*); + Arguments(CallFrame*, NoParametersType); + + void tearOffForInlineCallFrame(JSGlobalData& globalData, Register*, InlineCallFrame*); - inline bool Arguments::tryDeleteArgument(size_t argument) - { - if (!isArgument(argument)) - return false; - allocateSlowArguments(); - m_slowArguments[argument].status = SlowArgument::Deleted; - return true; - } +public: + static const ClassInfo s_info; - inline bool Arguments::trySetArgument(JSGlobalData& globalData, size_t argument, JSValue value) - { - if (!isArgument(argument)) - return false; - this->argument(argument).set(globalData, this, value); - return true; - } + static void visitChildren(JSCell*, SlotVisitor&); - inline JSValue Arguments::tryGetArgument(size_t argument) - { - if (!isArgument(argument)) - return JSValue(); - return this->argument(argument).get(); - } + void fillArgList(ExecState*, MarkedArgumentBuffer&); - inline bool Arguments::isDeletedArgument(size_t argument) + uint32_t length(ExecState* exec) const { - if (argument >= m_numArguments) - return false; - if (!m_slowArguments) - return false; - if (m_slowArguments[argument].status != SlowArgument::Deleted) - return false; - return true; + if (UNLIKELY(m_overrodeLength)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return m_numArguments; } - - inline bool Arguments::isArgument(size_t argument) - { - if (argument >= m_numArguments) - return false; - if (m_slowArguments && m_slowArguments[argument].status == SlowArgument::Deleted) - return false; - return true; + + void copyToArguments(ExecState*, CallFrame*, uint32_t length); + void tearOff(CallFrame*); + void tearOff(CallFrame*, InlineCallFrame*); + bool isTornOff() const { return m_registerArray; } + void didTearOffActivation(ExecState*, JSActivation*); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } - - inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) - { - ASSERT(isArgument(argument)); - if (!m_slowArguments) - return m_registers[CallFrame::argumentOffset(argument)]; - - int index = m_slowArguments[argument].index; - if (!m_activation || m_slowArguments[argument].status != SlowArgument::Captured) - return m_registers[index]; - - return m_activation->registerAt(index); + +protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + void finishCreation(CallFrame*); + void finishCreation(CallFrame*, InlineCallFrame*); + +private: + static void destroy(JSCell*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); + void createStrictModeCallerIfNecessary(ExecState*); + void createStrictModeCalleeIfNecessary(ExecState*); + + bool isArgument(size_t); + bool trySetArgument(JSGlobalData&, size_t argument, JSValue); + JSValue tryGetArgument(size_t argument); + bool isDeletedArgument(size_t); + bool tryDeleteArgument(size_t); + WriteBarrierBase<Unknown>& argument(size_t); + void allocateSlowArguments(); + + void init(CallFrame*); + + WriteBarrier<JSActivation> m_activation; + + unsigned m_numArguments; + + // We make these full byte booleans to make them easy to test from the JIT, + // and because even if they were single-bit booleans we still wouldn't save + // any space. + bool m_overrodeLength; + bool m_overrodeCallee; + bool m_overrodeCaller; + bool m_isStrictMode; + + WriteBarrierBase<Unknown>* m_registers; + OwnArrayPtr<WriteBarrier<Unknown> > m_registerArray; + + OwnArrayPtr<SlowArgument> m_slowArguments; + + WriteBarrier<JSFunction> m_callee; +}; + +Arguments* asArguments(JSValue); + +inline Arguments* asArguments(JSValue value) +{ + ASSERT(asObject(value)->inherits(&Arguments::s_info)); + return static_cast<Arguments*>(asObject(value)); +} + +inline Arguments::Arguments(CallFrame* callFrame) + : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) +{ +} + +inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) + : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) +{ +} + +inline void Arguments::allocateSlowArguments() +{ + if (m_slowArguments) + return; + m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); + for (size_t i = 0; i < m_numArguments; ++i) { + ASSERT(m_slowArguments[i].status == SlowArgument::Normal); + m_slowArguments[i].index = CallFrame::argumentOffset(i); } - - inline void Arguments::finishCreation(CallFrame* callFrame) - { - Base::finishCreation(callFrame->globalData()); - ASSERT(inherits(&s_info)); - - JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); - m_numArguments = callFrame->argumentCount(); - m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); - m_callee.set(callFrame->globalData(), this, callee); - m_overrodeLength = false; - m_overrodeCallee = false; - m_overrodeCaller = false; - m_isStrictMode = callFrame->codeBlock()->isStrictMode(); - - SharedSymbolTable* symbolTable = callFrame->codeBlock()->symbolTable(); - const SlowArgument* slowArguments = symbolTable->slowArguments(); - if (slowArguments) { - allocateSlowArguments(); - size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount()); - for (size_t i = 0; i < count; ++i) - m_slowArguments[i] = slowArguments[i]; - } - - // The bytecode generator omits op_tear_off_activation in cases of no - // declared parameters, so we need to tear off immediately. - if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) - tearOff(callFrame); +} + +inline bool Arguments::tryDeleteArgument(size_t argument) +{ + if (!isArgument(argument)) + return false; + allocateSlowArguments(); + m_slowArguments[argument].status = SlowArgument::Deleted; + return true; +} + +inline bool Arguments::trySetArgument(JSGlobalData& globalData, size_t argument, JSValue value) +{ + if (!isArgument(argument)) + return false; + this->argument(argument).set(globalData, this, value); + return true; +} + +inline JSValue Arguments::tryGetArgument(size_t argument) +{ + if (!isArgument(argument)) + return JSValue(); + return this->argument(argument).get(); +} + +inline bool Arguments::isDeletedArgument(size_t argument) +{ + if (argument >= m_numArguments) + return false; + if (!m_slowArguments) + return false; + if (m_slowArguments[argument].status != SlowArgument::Deleted) + return false; + return true; +} + +inline bool Arguments::isArgument(size_t argument) +{ + if (argument >= m_numArguments) + return false; + if (m_slowArguments && m_slowArguments[argument].status == SlowArgument::Deleted) + return false; + return true; +} + +inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) +{ + ASSERT(isArgument(argument)); + if (!m_slowArguments) + return m_registers[CallFrame::argumentOffset(argument)]; + + int index = m_slowArguments[argument].index; + if (!m_activation || m_slowArguments[argument].status != SlowArgument::Captured) + return m_registers[index]; + + return m_activation->registerAt(index); +} + +inline void Arguments::finishCreation(CallFrame* callFrame) +{ + Base::finishCreation(callFrame->globalData()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); + m_numArguments = callFrame->argumentCount(); + m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); + m_callee.set(callFrame->globalData(), this, callee); + m_overrodeLength = false; + m_overrodeCallee = false; + m_overrodeCaller = false; + m_isStrictMode = callFrame->codeBlock()->isStrictMode(); + + SharedSymbolTable* symbolTable = callFrame->codeBlock()->symbolTable(); + const SlowArgument* slowArguments = symbolTable->slowArguments(); + if (slowArguments) { + allocateSlowArguments(); + size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount()); + for (size_t i = 0; i < count; ++i) + m_slowArguments[i] = slowArguments[i]; } - inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) - { - Base::finishCreation(callFrame->globalData()); - ASSERT(inherits(&s_info)); - - JSFunction* callee = inlineCallFrame->callee.get(); - m_numArguments = inlineCallFrame->arguments.size() - 1; - m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset; - m_callee.set(callFrame->globalData(), this, callee); - m_overrodeLength = false; - m_overrodeCallee = false; - m_overrodeCaller = false; - m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); - ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments()); - - // The bytecode generator omits op_tear_off_activation in cases of no - // declared parameters, so we need to tear off immediately. - if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) - tearOff(callFrame, inlineCallFrame); - } + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame); +} + +inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) +{ + Base::finishCreation(callFrame->globalData()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = inlineCallFrame->callee.get(); + m_numArguments = inlineCallFrame->arguments.size() - 1; + m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset; + m_callee.set(callFrame->globalData(), this, callee); + m_overrodeLength = false; + m_overrodeCallee = false; + m_overrodeCaller = false; + m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); + ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments()); + + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame, inlineCallFrame); +} } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp index 5c2cd7167..a3fce45f2 100644 --- a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -25,8 +25,8 @@ #include "ArrayConstructor.h" #include "ArrayPrototype.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" @@ -77,15 +77,15 @@ bool ArrayConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exe // ------------------------------ Functions --------------------------- -JSObject* constructArrayWithSizeQuirk(ExecState* exec, JSGlobalObject* globalObject, JSValue length) +JSObject* constructArrayWithSizeQuirk(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, JSValue length) { if (!length.isNumber()) - return constructArray(exec, globalObject, &length, 1); + return constructArray(exec, profile, globalObject, &length, 1); uint32_t n = length.toUInt32(exec); if (n != length.toNumber(exec)) return throwError(exec, createRangeError(exec, ASCIILiteral("Array size is not a small enough positive integer."))); - return constructEmptyArray(exec, globalObject, n); + return constructEmptyArray(exec, profile, globalObject, n); } static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) @@ -94,10 +94,10 @@ static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgLi // a single numeric argument denotes the array size (!) if (args.size() == 1) - return constructArrayWithSizeQuirk(exec, globalObject, args.at(0)); + return constructArrayWithSizeQuirk(exec, 0, globalObject, args.at(0)); // otherwise the array is constructed with the arguments in it - return constructArray(exec, globalObject, args); + return constructArray(exec, 0, globalObject, args); } static EncodedJSValue JSC_HOST_CALL constructWithArrayConstructor(ExecState* exec) diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.h b/Source/JavaScriptCore/runtime/ArrayConstructor.h index dcbf0a1b3..96860b0fc 100644 --- a/Source/JavaScriptCore/runtime/ArrayConstructor.h +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.h @@ -25,42 +25,42 @@ namespace JSC { - class ArrayPrototype; - class JSArray; +class ArrayPrototype; +class JSArray; - class ArrayConstructor : public InternalFunction { - public: - typedef InternalFunction Base; +class ArrayConstructor : public InternalFunction { +public: + typedef InternalFunction Base; - static ArrayConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ArrayPrototype* arrayPrototype) - { - ArrayConstructor* constructor = new (NotNull, allocateCell<ArrayConstructor>(*exec->heap())) ArrayConstructor(globalObject, structure); - constructor->finishCreation(exec, arrayPrototype); - return constructor; - } + static ArrayConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ArrayPrototype* arrayPrototype) + { + ArrayConstructor* constructor = new (NotNull, allocateCell<ArrayConstructor>(*exec->heap())) ArrayConstructor(globalObject, structure); + constructor->finishCreation(exec, arrayPrototype); + return constructor; + } - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } - protected: - void finishCreation(ExecState*, ArrayPrototype*); - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; +protected: + void finishCreation(ExecState*, ArrayPrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; - private: - ArrayConstructor(JSGlobalObject*, Structure*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); +private: + ArrayConstructor(JSGlobalObject*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - static ConstructType getConstructData(JSCell*, ConstructData&); - static CallType getCallData(JSCell*, CallData&); - }; + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; - JSObject* constructArrayWithSizeQuirk(ExecState*, JSGlobalObject*, JSValue); +JSObject* constructArrayWithSizeQuirk(ExecState*, ArrayAllocationProfile*, JSGlobalObject*, JSValue); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index 6975dc778..e6d37fb02 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -24,10 +24,10 @@ #include "config.h" #include "ArrayPrototype.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CachedCall.h" #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "Interpreter.h" #include "JIT.h" #include "JSStringBuilder.h" @@ -116,15 +116,14 @@ const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, 0, ExecStat ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) { - Butterfly* butterfly = createArrayButterfly(exec->globalData(), 0); - ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure, butterfly); + ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure); prototype->finishCreation(globalObject); return prototype; } // ECMA 15.4.4 -ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure, Butterfly* butterfly) - : JSArray(globalObject->globalData(), structure, butterfly) +ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure) + : JSArray(globalObject->globalData(), structure, 0) { } @@ -456,7 +455,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); - JSArray* arr = constructEmptyArray(exec); + JSArray* arr = constructEmptyArray(exec, 0); unsigned n = 0; JSValue curArg = thisValue.toObject(exec); if (exec->hadException()) @@ -618,7 +617,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) return JSValue::encode(jsUndefined()); // We return a new array - JSArray* resObj = constructEmptyArray(exec); + JSArray* resObj = constructEmptyArray(exec, 0); JSValue result = resObj; unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); @@ -733,7 +732,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) return JSValue::encode(jsUndefined()); if (!exec->argumentCount()) - return JSValue::encode(constructEmptyArray(exec)); + return JSValue::encode(constructEmptyArray(exec, 0)); unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); @@ -748,7 +747,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) deleteCount = static_cast<unsigned>(deleteDouble); } - JSArray* resObj = JSArray::tryCreateUninitialized(exec->globalData(), exec->lexicalGlobalObject()->arrayStructure(), deleteCount); + JSArray* resObj = JSArray::tryCreateUninitialized(exec->globalData(), exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount); if (!resObj) return JSValue::encode(throwOutOfMemoryError(exec)); @@ -820,7 +819,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) return throwVMTypeError(exec); JSValue applyThis = exec->argument(1); - JSArray* resultArray = constructEmptyArray(exec); + JSArray* resultArray = constructEmptyArray(exec, 0); unsigned filterIndex = 0; unsigned k = 0; @@ -880,7 +879,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) JSValue applyThis = exec->argument(1); - JSArray* resultArray = constructEmptyArray(exec, length); + JSArray* resultArray = constructEmptyArray(exec, 0, length); unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { JSFunction* f = jsCast<JSFunction*>(function); diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.h b/Source/JavaScriptCore/runtime/ArrayPrototype.h index b33021121..c23bcdec1 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.h +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h @@ -26,28 +26,28 @@ namespace JSC { - class ArrayPrototype : public JSArray { - private: - ArrayPrototype(JSGlobalObject*, Structure*, Butterfly*); +class ArrayPrototype : public JSArray { +private: + ArrayPrototype(JSGlobalObject*, Structure*); - public: - typedef JSArray Base; +public: + typedef JSArray Base; - static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*); + static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayClass); + } - protected: - void finishCreation(JSGlobalObject*); - }; +protected: + void finishCreation(JSGlobalObject*); +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h index 610b8d16c..178bf3fe9 100644 --- a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h +++ b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -31,25 +31,25 @@ namespace JSC { - class BatchedTransitionOptimizer { - WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer); - public: - BatchedTransitionOptimizer(JSGlobalData& globalData, JSObject* object) - : m_globalData(&globalData) - , m_object(object) - { - } - - ~BatchedTransitionOptimizer() - { - if (m_object->structure()->isDictionary()) - m_object->flattenDictionaryObject(*m_globalData); - } - - private: - JSGlobalData* m_globalData; - JSObject* m_object; - }; +class BatchedTransitionOptimizer { + WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer); +public: + BatchedTransitionOptimizer(JSGlobalData& globalData, JSObject* object) + : m_globalData(&globalData) + , m_object(object) + { + } + + ~BatchedTransitionOptimizer() + { + if (m_object->structure()->isDictionary()) + m_object->flattenDictionaryObject(*m_globalData); + } + +private: + JSGlobalData* m_globalData; + JSObject* m_object; +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.h b/Source/JavaScriptCore/runtime/BooleanConstructor.h index 2b6bafa2f..f395374ae 100644 --- a/Source/JavaScriptCore/runtime/BooleanConstructor.h +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.h @@ -25,37 +25,37 @@ namespace JSC { - class BooleanPrototype; +class BooleanPrototype; - class BooleanConstructor : public InternalFunction { - public: - typedef InternalFunction Base; +class BooleanConstructor : public InternalFunction { +public: + typedef InternalFunction Base; - static BooleanConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, BooleanPrototype* booleanPrototype) - { - BooleanConstructor* constructor = new (NotNull, allocateCell<BooleanConstructor>(*exec->heap())) BooleanConstructor(globalObject, structure); - constructor->finishCreation(exec, booleanPrototype); - return constructor; - } + static BooleanConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, BooleanPrototype* booleanPrototype) + { + BooleanConstructor* constructor = new (NotNull, allocateCell<BooleanConstructor>(*exec->heap())) BooleanConstructor(globalObject, structure); + constructor->finishCreation(exec, booleanPrototype); + return constructor; + } - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } - protected: - void finishCreation(ExecState*, BooleanPrototype*); +protected: + void finishCreation(ExecState*, BooleanPrototype*); - private: - BooleanConstructor(JSGlobalObject*, Structure*); - static ConstructType getConstructData(JSCell*, ConstructData&); - static CallType getCallData(JSCell*, CallData&); - }; +private: + BooleanConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; - JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSGlobalObject*, JSValue); - JSObject* constructBoolean(ExecState*, const ArgList&); +JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSGlobalObject*, JSValue); +JSObject* constructBoolean(ExecState*, const ArgList&); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanObject.h b/Source/JavaScriptCore/runtime/BooleanObject.h index bd0f66944..b6fcccdcf 100644 --- a/Source/JavaScriptCore/runtime/BooleanObject.h +++ b/Source/JavaScriptCore/runtime/BooleanObject.h @@ -25,36 +25,36 @@ namespace JSC { - class BooleanObject : public JSWrapperObject { - protected: - JS_EXPORT_PRIVATE BooleanObject(JSGlobalData&, Structure*); - JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&); - - public: - typedef JSWrapperObject Base; - - static BooleanObject* create(JSGlobalData& globalData, Structure* structure) - { - BooleanObject* boolean = new (NotNull, allocateCell<BooleanObject>(globalData.heap)) BooleanObject(globalData, structure); - boolean->finishCreation(globalData); - return boolean; - } - - static JS_EXPORTDATA const ClassInfo s_info; - - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } - }; +class BooleanObject : public JSWrapperObject { +protected: + JS_EXPORT_PRIVATE BooleanObject(JSGlobalData&, Structure*); + JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&); - BooleanObject* asBooleanObject(JSValue); +public: + typedef JSWrapperObject Base; - inline BooleanObject* asBooleanObject(JSValue value) + static BooleanObject* create(JSGlobalData& globalData, Structure* structure) + { + BooleanObject* boolean = new (NotNull, allocateCell<BooleanObject>(globalData.heap)) BooleanObject(globalData, structure); + boolean->finishCreation(globalData); + return boolean; + } + + static JS_EXPORTDATA const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) { - ASSERT(asObject(value)->inherits(&BooleanObject::s_info)); - return static_cast<BooleanObject*>(asObject(value)); + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } +}; + +BooleanObject* asBooleanObject(JSValue); + +inline BooleanObject* asBooleanObject(JSValue value) +{ + ASSERT(asObject(value)->inherits(&BooleanObject::s_info)); + return static_cast<BooleanObject*>(asObject(value)); +} } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.h b/Source/JavaScriptCore/runtime/BooleanPrototype.h index 3767f76ed..05790a755 100644 --- a/Source/JavaScriptCore/runtime/BooleanPrototype.h +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.h @@ -25,34 +25,34 @@ namespace JSC { - class BooleanPrototype : public BooleanObject { - public: - typedef BooleanObject Base; - - static BooleanPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) - { - BooleanPrototype* prototype = new (NotNull, allocateCell<BooleanPrototype>(*exec->heap())) BooleanPrototype(exec, structure); - prototype->finishCreation(exec, globalObject); - return prototype; - } +class BooleanPrototype : public BooleanObject { +public: + typedef BooleanObject Base; + + static BooleanPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + BooleanPrototype* prototype = new (NotNull, allocateCell<BooleanPrototype>(*exec->heap())) BooleanPrototype(exec, structure); + prototype->finishCreation(exec, globalObject); + return prototype; + } - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } - protected: - void finishCreation(ExecState*, JSGlobalObject*); - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | BooleanObject::StructureFlags; +protected: + void finishCreation(ExecState*, JSGlobalObject*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | BooleanObject::StructureFlags; - private: - BooleanPrototype(ExecState*, Structure*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); +private: + BooleanPrototype(ExecState*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - }; + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Butterfly.h b/Source/JavaScriptCore/runtime/Butterfly.h index cb93aea8a..4b8d53f7e 100644 --- a/Source/JavaScriptCore/runtime/Butterfly.h +++ b/Source/JavaScriptCore/runtime/Butterfly.h @@ -88,12 +88,18 @@ public: template<typename T> T* indexingPayload() { return reinterpret_cast<T*>(this); } ArrayStorage* arrayStorage() { return indexingPayload<ArrayStorage>(); } + WriteBarrier<Unknown>* contiguousInt32() { return indexingPayload<WriteBarrier<Unknown> >(); } + double* contiguousDouble() { return indexingPayload<double>(); } WriteBarrier<Unknown>* contiguous() { return indexingPayload<WriteBarrier<Unknown> >(); } static Butterfly* fromContiguous(WriteBarrier<Unknown>* contiguous) { return reinterpret_cast<Butterfly*>(contiguous); } + static Butterfly* fromContiguous(double* contiguous) + { + return reinterpret_cast<Butterfly*>(contiguous); + } static ptrdiff_t offsetOfPropertyStorage() { return -static_cast<ptrdiff_t>(sizeof(IndexingHeader)); } static int indexOfPropertyStorage() diff --git a/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h b/Source/JavaScriptCore/runtime/ButterflyInlines.h index 86a836bef..9167497a4 100644 --- a/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h +++ b/Source/JavaScriptCore/runtime/ButterflyInlines.h @@ -23,12 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ButterflyInlineMethods_h -#define ButterflyInlineMethods_h +#ifndef ButterflyInlines_h +#define ButterflyInlines_h #include "ArrayStorage.h" #include "Butterfly.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "CopyVisitor.h" #include "JSGlobalData.h" #include "Structure.h" @@ -61,8 +61,9 @@ inline Butterfly* Butterfly::create(JSGlobalData& globalData, Structure* structu inline Butterfly* Butterfly::createUninitializedDuringCollection(CopyVisitor& visitor, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) { + size_t size = totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); Butterfly* result = fromBase( - visitor.allocateNewSpace(totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes)), + visitor.allocateNewSpace(size), preCapacity, propertyCapacity); return result; } @@ -175,5 +176,5 @@ inline Butterfly* Butterfly::shift(Structure* structure, size_t numberOfSlots) } // namespace JSC -#endif // ButterflyInlineMethods_h +#endif // ButterflyInlines_h diff --git a/Source/JavaScriptCore/runtime/CallData.h b/Source/JavaScriptCore/runtime/CallData.h index 77478304c..7df36c72a 100644 --- a/Source/JavaScriptCore/runtime/CallData.h +++ b/Source/JavaScriptCore/runtime/CallData.h @@ -33,31 +33,31 @@ namespace JSC { - class ArgList; - class ExecState; - class FunctionExecutable; - class JSObject; - class JSScope; +class ArgList; +class ExecState; +class FunctionExecutable; +class JSObject; +class JSScope; - enum CallType { - CallTypeNone, - CallTypeHost, - CallTypeJS - }; +enum CallType { + CallTypeNone, + CallTypeHost, + CallTypeJS +}; - typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); +typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); - union CallData { - struct { - NativeFunction function; - } native; - struct { - FunctionExecutable* functionExecutable; - JSScope* scope; - } js; - }; +union CallData { + struct { + NativeFunction function; + } native; + struct { + FunctionExecutable* functionExecutable; + JSScope* scope; + } js; +}; - JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); +JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h index c918621fe..af1d71e13 100644 --- a/Source/JavaScriptCore/runtime/ClassInfo.h +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -29,90 +29,90 @@ namespace JSC { - class HashEntry; - struct HashTable; +class HashEntry; +struct HashTable; - struct MethodTable { - typedef void (*DestroyFunctionPtr)(JSCell*); - DestroyFunctionPtr destroy; +struct MethodTable { + typedef void (*DestroyFunctionPtr)(JSCell*); + DestroyFunctionPtr destroy; - typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); - VisitChildrenFunctionPtr visitChildren; + typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); + VisitChildrenFunctionPtr visitChildren; - typedef void (*CopyBackingStoreFunctionPtr)(JSCell*, CopyVisitor&); - CopyBackingStoreFunctionPtr copyBackingStore; + typedef void (*CopyBackingStoreFunctionPtr)(JSCell*, CopyVisitor&); + CopyBackingStoreFunctionPtr copyBackingStore; - typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); - GetCallDataFunctionPtr getCallData; + typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); + GetCallDataFunctionPtr getCallData; - typedef ConstructType (*GetConstructDataFunctionPtr)(JSCell*, ConstructData&); - GetConstructDataFunctionPtr getConstructData; + typedef ConstructType (*GetConstructDataFunctionPtr)(JSCell*, ConstructData&); + GetConstructDataFunctionPtr getConstructData; - typedef void (*PutFunctionPtr)(JSCell*, ExecState*, PropertyName propertyName, JSValue, PutPropertySlot&); - PutFunctionPtr put; + typedef void (*PutFunctionPtr)(JSCell*, ExecState*, PropertyName propertyName, JSValue, PutPropertySlot&); + PutFunctionPtr put; - typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); - PutByIndexFunctionPtr putByIndex; + typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + PutByIndexFunctionPtr putByIndex; - typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, PropertyName); - DeletePropertyFunctionPtr deleteProperty; + typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, PropertyName); + DeletePropertyFunctionPtr deleteProperty; - typedef bool (*DeletePropertyByIndexFunctionPtr)(JSCell*, ExecState*, unsigned); - DeletePropertyByIndexFunctionPtr deletePropertyByIndex; + typedef bool (*DeletePropertyByIndexFunctionPtr)(JSCell*, ExecState*, unsigned); + DeletePropertyByIndexFunctionPtr deletePropertyByIndex; - typedef bool (*GetOwnPropertySlotFunctionPtr)(JSCell*, ExecState*, PropertyName, PropertySlot&); - GetOwnPropertySlotFunctionPtr getOwnPropertySlot; + typedef bool (*GetOwnPropertySlotFunctionPtr)(JSCell*, ExecState*, PropertyName, PropertySlot&); + GetOwnPropertySlotFunctionPtr getOwnPropertySlot; - typedef bool (*GetOwnPropertySlotByIndexFunctionPtr)(JSCell*, ExecState*, unsigned, PropertySlot&); - GetOwnPropertySlotByIndexFunctionPtr getOwnPropertySlotByIndex; + typedef bool (*GetOwnPropertySlotByIndexFunctionPtr)(JSCell*, ExecState*, unsigned, PropertySlot&); + GetOwnPropertySlotByIndexFunctionPtr getOwnPropertySlotByIndex; - typedef JSObject* (*ToThisObjectFunctionPtr)(JSCell*, ExecState*); - ToThisObjectFunctionPtr toThisObject; + typedef JSObject* (*ToThisObjectFunctionPtr)(JSCell*, ExecState*); + ToThisObjectFunctionPtr toThisObject; - typedef JSValue (*DefaultValueFunctionPtr)(const JSObject*, ExecState*, PreferredPrimitiveType); - DefaultValueFunctionPtr defaultValue; + typedef JSValue (*DefaultValueFunctionPtr)(const JSObject*, ExecState*, PreferredPrimitiveType); + DefaultValueFunctionPtr defaultValue; - typedef void (*GetOwnPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; + typedef void (*GetOwnPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; - typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames; + typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames; - typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - GetPropertyNamesFunctionPtr getPropertyNames; + typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetPropertyNamesFunctionPtr getPropertyNames; - typedef String (*ClassNameFunctionPtr)(const JSObject*); - ClassNameFunctionPtr className; + typedef String (*ClassNameFunctionPtr)(const JSObject*); + ClassNameFunctionPtr className; - typedef bool (*CustomHasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue); - CustomHasInstanceFunctionPtr customHasInstance; + typedef bool (*CustomHasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue); + CustomHasInstanceFunctionPtr customHasInstance; - typedef void (*PutWithAttributesFunctionPtr)(JSObject*, ExecState*, PropertyName propertyName, JSValue, unsigned attributes); - PutWithAttributesFunctionPtr putDirectVirtual; + typedef void (*PutWithAttributesFunctionPtr)(JSObject*, ExecState*, PropertyName propertyName, JSValue, unsigned attributes); + PutWithAttributesFunctionPtr putDirectVirtual; - typedef bool (*DefineOwnPropertyFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool); - DefineOwnPropertyFunctionPtr defineOwnProperty; + typedef bool (*DefineOwnPropertyFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool); + DefineOwnPropertyFunctionPtr defineOwnProperty; - typedef bool (*GetOwnPropertyDescriptorFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - GetOwnPropertyDescriptorFunctionPtr getOwnPropertyDescriptor; - }; + typedef bool (*GetOwnPropertyDescriptorFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + GetOwnPropertyDescriptorFunctionPtr getOwnPropertyDescriptor; +}; #define CREATE_MEMBER_CHECKER(member) \ -template <typename T> \ -struct MemberCheck##member { \ - struct Fallback { \ - void member(...); \ - }; \ - struct Derived : T, Fallback { }; \ - template <typename U, U> struct Check; \ - typedef char Yes[2]; \ - typedef char No[1]; \ - template <typename U> \ - static No &func(Check<void (Fallback::*)(...), &U::member>*); \ - template <typename U> \ - static Yes &func(...); \ - enum { has = sizeof(func<Derived>(0)) == sizeof(Yes) }; \ -} + template <typename T> \ + struct MemberCheck##member { \ + struct Fallback { \ + void member(...); \ + }; \ + struct Derived : T, Fallback { }; \ + template <typename U, U> struct Check; \ + typedef char Yes[2]; \ + typedef char No[1]; \ + template <typename U> \ + static No &func(Check<void (Fallback::*)(...), &U::member>*); \ + template <typename U> \ + static Yes &func(...); \ + enum { has = sizeof(func<Derived>(0)) == sizeof(Yes) }; \ + } #define HAS_MEMBER_NAMED(klass, name) (MemberCheck##name<klass>::has) @@ -141,54 +141,54 @@ struct MemberCheck##member { \ }, \ ClassName::TypedArrayStorageType - struct ClassInfo { - /** - * A string denoting the class name. Example: "Window". - */ - const char* className; - - /** - * Pointer to the class information of the base class. - * 0L if there is none. - */ - const ClassInfo* parentClass; - /** - * Static hash-table of properties. - * For classes that can be used from multiple threads, it is accessed via a getter function that would typically return a pointer to thread-specific value. - */ - const HashTable* propHashTable(ExecState* exec) const - { - if (classPropHashTableGetterFunction) - return classPropHashTableGetterFunction(exec); - return staticPropHashTable; - } +struct ClassInfo { + /** + * A string denoting the class name. Example: "Window". + */ + const char* className; + + /** + * Pointer to the class information of the base class. + * 0L if there is none. + */ + const ClassInfo* parentClass; + /** + * Static hash-table of properties. + * For classes that can be used from multiple threads, it is accessed via a getter function that would typically return a pointer to thread-specific value. + */ + const HashTable* propHashTable(ExecState* exec) const + { + if (classPropHashTableGetterFunction) + return classPropHashTableGetterFunction(exec); + return staticPropHashTable; + } - bool isSubClassOf(const ClassInfo* other) const - { - for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { - if (ci == other) - return true; - } - return false; + bool isSubClassOf(const ClassInfo* other) const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci == other) + return true; } - - bool hasStaticProperties() const - { - for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { - if (ci->staticPropHashTable || ci->classPropHashTableGetterFunction) - return true; - } - return false; + return false; + } + + bool hasStaticProperties() const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci->staticPropHashTable || ci->classPropHashTableGetterFunction) + return true; } + return false; + } - const HashTable* staticPropHashTable; - typedef const HashTable* (*ClassPropHashTableGetterFunction)(ExecState*); - const ClassPropHashTableGetterFunction classPropHashTableGetterFunction; + const HashTable* staticPropHashTable; + typedef const HashTable* (*ClassPropHashTableGetterFunction)(ExecState*); + const ClassPropHashTableGetterFunction classPropHashTableGetterFunction; - MethodTable methodTable; + MethodTable methodTable; - TypedArrayType typedArrayStorageType; - }; + TypedArrayType typedArrayStorageType; +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CodeCache.cpp b/Source/JavaScriptCore/runtime/CodeCache.cpp index 4de760e49..068919528 100644 --- a/Source/JavaScriptCore/runtime/CodeCache.cpp +++ b/Source/JavaScriptCore/runtime/CodeCache.cpp @@ -36,7 +36,6 @@ namespace JSC { CodeCache::CodeCache() - : m_randomGenerator(static_cast<uint32_t>(randomNumber() * UINT32_MAX)) { } @@ -67,9 +66,9 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa CodeBlockKey key = makeCodeBlockKey(source, CacheTypes<UnlinkedCodeBlockType>::codeType, strictness); bool storeInCache = false; if (debuggerMode == DebuggerOff && profilerMode == ProfilerOff) { - CodeBlockIndicesMap::iterator result = m_cachedCodeBlockIndices.find(key); - if (result != m_cachedCodeBlockIndices.end()) { - UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(m_cachedCodeBlocks[result->value].second.get()); + const Strong<UnlinkedCodeBlock>* result = m_cachedCodeBlocks.find(key); + if (result) { + UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(result->get()); unsigned firstLine = source.firstLine() + unlinkedCode->firstLine(); executable->recordParse(unlinkedCode->codeFeatures(), unlinkedCode->hasCapturedVariables(), firstLine, firstLine + unlinkedCode->lineCount()); return unlinkedCode; @@ -91,14 +90,8 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa if (error.m_type != ParserError::ErrorNone) return 0; - if (storeInCache) { - size_t index = m_randomGenerator.getUint32() % kMaxCodeBlockEntries; - if (m_cachedCodeBlocks[index].second) - m_cachedCodeBlockIndices.remove(m_cachedCodeBlocks[index].first); - m_cachedCodeBlockIndices.set(key, index); - m_cachedCodeBlocks[index].second.set(globalData, unlinkedCode); - m_cachedCodeBlocks[index].first = key; - } + if (storeInCache) + m_cachedCodeBlocks.add(key, Strong<UnlinkedCodeBlock>(globalData, unlinkedCode)); return unlinkedCode; } @@ -133,6 +126,7 @@ UnlinkedFunctionCodeBlock* CodeCache::generateFunctionCodeBlock(JSGlobalData& gl body->destroyData(); if (error.m_type != ParserError::ErrorNone) return 0; + m_cachedFunctionCode.add(result, Strong<UnlinkedFunctionCodeBlock>(globalData, result)); return result; } @@ -149,9 +143,9 @@ CodeCache::GlobalFunctionKey CodeCache::makeGlobalFunctionKey(const SourceCode& UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error) { GlobalFunctionKey key = makeGlobalFunctionKey(source, name.string()); - GlobalFunctionIndicesMap::iterator result = m_cachedGlobalFunctionIndices.find(key); - if (result != m_cachedGlobalFunctionIndices.end()) - return m_cachedGlobalFunctions[result->value].second.get(); + const Strong<UnlinkedFunctionExecutable>* result = m_cachedGlobalFunctions.find(key); + if (result) + return result->get(); RefPtr<ProgramNode> program = parse<ProgramNode>(&globalData, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error); if (!program) { @@ -173,14 +167,13 @@ UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlo UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&globalData, source, body); functionExecutable->m_nameValue.set(globalData, functionExecutable, jsString(&globalData, name.string())); - size_t index = m_randomGenerator.getUint32() % kMaxGlobalFunctionEntries; - if (m_cachedGlobalFunctions[index].second) - m_cachedGlobalFunctionIndices.remove(m_cachedGlobalFunctions[index].first); - m_cachedGlobalFunctionIndices.set(key, index); - m_cachedGlobalFunctions[index].second.set(globalData, functionExecutable); - m_cachedGlobalFunctions[index].first = key; - + m_cachedGlobalFunctions.add(key, Strong<UnlinkedFunctionExecutable>(globalData, functionExecutable)); return functionExecutable; } +void CodeCache::usedFunctionCode(JSGlobalData& globalData, UnlinkedFunctionCodeBlock* codeBlock) +{ + m_cachedFunctionCode.add(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock)); +} + } diff --git a/Source/JavaScriptCore/runtime/CodeCache.h b/Source/JavaScriptCore/runtime/CodeCache.h index 4d4617189..733de42d6 100644 --- a/Source/JavaScriptCore/runtime/CodeCache.h +++ b/Source/JavaScriptCore/runtime/CodeCache.h @@ -34,6 +34,7 @@ #include <wtf/FixedArray.h> #include <wtf/Forward.h> #include <wtf/PassOwnPtr.h> +#include <wtf/RandomNumber.h> #include <wtf/text/WTFString.h> namespace JSC { @@ -51,6 +52,41 @@ struct ParserError; class SourceCode; class SourceProvider; +template <typename KeyType, typename EntryType, int CacheSize> class CacheMap { + typedef typename HashMap<KeyType, unsigned>::iterator iterator; +public: + CacheMap() + : m_randomGenerator((static_cast<uint32_t>(randomNumber() * std::numeric_limits<uint32_t>::max()))) + { + } + const EntryType* find(const KeyType& key) + { + iterator result = m_map.find(key); + if (result == m_map.end()) + return 0; + return &m_data[result->value].second; + } + void add(const KeyType& key, const EntryType& value) + { + iterator result = m_map.find(key); + if (result != m_map.end()) { + m_data[result->value].second = value; + return; + } + size_t newIndex = m_randomGenerator.getUint32() % CacheSize; + if (m_data[newIndex].second) + m_map.remove(m_data[newIndex].first); + m_map.add(key, newIndex); + m_data[newIndex].first = key; + m_data[newIndex].second = value; + ASSERT(m_map.size() <= CacheSize); + } +private: + HashMap<KeyType, unsigned> m_map; + FixedArray<std::pair<KeyType, EntryType>, CacheSize> m_data; + WeakRandom m_randomGenerator; +}; + class CodeCache { public: static PassOwnPtr<CodeCache> create() { return adoptPtr(new CodeCache); } @@ -59,13 +95,12 @@ public: UnlinkedEvalCodeBlock* getEvalCodeBlock(JSGlobalData&, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); UnlinkedFunctionCodeBlock* getFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&); UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(JSGlobalData&, const Identifier&, const SourceCode&, ParserError&); + void usedFunctionCode(JSGlobalData&, UnlinkedFunctionCodeBlock*); ~CodeCache(); enum CodeType { EvalType, ProgramType, FunctionType }; typedef std::pair<String, unsigned> CodeBlockKey; - typedef HashMap<CodeBlockKey, unsigned> CodeBlockIndicesMap; typedef std::pair<String, String> GlobalFunctionKey; - typedef HashMap<GlobalFunctionKey, unsigned> GlobalFunctionIndicesMap; private: CodeCache(); @@ -74,18 +109,17 @@ private: template <class UnlinkedCodeBlockType, class ExecutableType> inline UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); CodeBlockKey makeCodeBlockKey(const SourceCode&, CodeType, JSParserStrictness); - CodeBlockIndicesMap m_cachedCodeBlockIndices; GlobalFunctionKey makeGlobalFunctionKey(const SourceCode&, const String&); - GlobalFunctionIndicesMap m_cachedGlobalFunctionIndices; enum { kMaxCodeBlockEntries = 1024, - kMaxGlobalFunctionEntries = 1024 + kMaxGlobalFunctionEntries = 1024, + kMaxFunctionCodeBlocks = 1024 }; - FixedArray<std::pair<CodeBlockKey, Strong<UnlinkedCodeBlock> >, kMaxCodeBlockEntries> m_cachedCodeBlocks; - FixedArray<std::pair<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable> >, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions; - WeakRandom m_randomGenerator; + CacheMap<CodeBlockKey, Strong<UnlinkedCodeBlock>, kMaxCodeBlockEntries> m_cachedCodeBlocks; + CacheMap<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable>, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions; + CacheMap<UnlinkedFunctionCodeBlock*, Strong<UnlinkedFunctionCodeBlock>, kMaxFunctionCodeBlocks> m_cachedFunctionCode; }; } diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp index 20a2e2acb..49a0e256d 100644 --- a/Source/JavaScriptCore/runtime/Executable.cpp +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -620,18 +620,17 @@ void FunctionExecutable::clearCodeIfNotCompiling() clearCode(); } -void FunctionExecutable::clearUnlinkedCodeIfNotCompiling() +void FunctionExecutable::clearUnlinkedCodeForRecompilationIfNotCompiling() { if (isCompiling()) return; - m_unlinkedExecutable->clearCode(); + m_unlinkedExecutable->clearCodeForRecompilation(); } void FunctionExecutable::clearCode() { m_codeBlockForCall.clear(); m_codeBlockForConstruct.clear(); - m_unlinkedExecutable->clearCode(); Base::clearCode(); } diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h index 74b4add75..c1c044b0e 100644 --- a/Source/JavaScriptCore/runtime/Executable.h +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -704,7 +704,7 @@ namespace JSC { SharedSymbolTable* symbolTable(CodeSpecializationKind kind) const { return m_unlinkedExecutable->symbolTable(kind); } void clearCodeIfNotCompiling(); - void clearUnlinkedCodeIfNotCompiling(); + void clearUnlinkedCodeForRecompilationIfNotCompiling(); static void visitChildren(JSCell*, SlotVisitor&); static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { @@ -752,6 +752,7 @@ namespace JSC { : Base(globalData, scope->globalObject()->functionStructure()) , m_executable(globalData, this, executable) , m_scope(globalData, this, scope) + , m_inheritorIDWatchpoint(InitializedBlind) // See comment in JSFunction.cpp concerning the reason for using InitializedBlind as opposed to InitializedWatching. { } diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp index a4b2202c1..8e4390b1b 100644 --- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -186,7 +186,7 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec) // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order. size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0; - JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->globalData(), globalObject->arrayStructure(), numBoundArgs); + JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->globalData(), globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs); if (!boundArgs) return JSValue::encode(throwOutOfMemoryError(exec)); diff --git a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h b/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h index 22785ce24..cfad1c8c2 100644 --- a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h +++ b/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IndexingHeaderInlineMethods_h -#define IndexingHeaderInlineMethods_h +#ifndef IndexingHeaderInlines_h +#define IndexingHeaderInlines_h #include "ArrayStorage.h" #include "IndexingHeader.h" @@ -43,6 +43,9 @@ inline size_t IndexingHeader::preCapacity(Structure* structure) inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) { switch (structure->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return vectorLength() * sizeof(EncodedJSValue); @@ -57,5 +60,5 @@ inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) } // namespace JSC -#endif // IndexingHeaderInlineMethods_h +#endif // IndexingHeaderInlines_h diff --git a/Source/JavaScriptCore/runtime/IndexingType.cpp b/Source/JavaScriptCore/runtime/IndexingType.cpp index 7261847a2..dc2733ad1 100644 --- a/Source/JavaScriptCore/runtime/IndexingType.cpp +++ b/Source/JavaScriptCore/runtime/IndexingType.cpp @@ -31,6 +31,46 @@ namespace JSC { +IndexingType leastUpperBoundOfIndexingTypes(IndexingType a, IndexingType b) +{ + // It doesn't make sense to LUB something that is an array with something that isn't. + ASSERT((a & IsArray) == (b & IsArray)); + + // Boy, this sure is easy right now. + return std::max(a, b); +} + +IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType indexingType, SpeculatedType type) +{ + if (!type) + return indexingType; + switch (indexingType) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + if (isInt32Speculation(type)) + return (indexingType & ~IndexingShapeMask) | Int32Shape; + if (isNumberSpeculation(type)) + return (indexingType & ~IndexingShapeMask) | DoubleShape; + return (indexingType & ~IndexingShapeMask) | ContiguousShape; + case ALL_DOUBLE_INDEXING_TYPES: + if (isNumberSpeculation(type)) + return indexingType; + return (indexingType & ~IndexingShapeMask) | ContiguousShape; + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return indexingType; + default: + CRASH(); + return 0; + } +} + +IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType indexingType, JSValue value) +{ + return leastUpperBoundOfIndexingTypeAndType(indexingType, speculationFromValue(value)); +} + const char* indexingTypeToString(IndexingType indexingType) { static char result[128]; @@ -39,6 +79,12 @@ const char* indexingTypeToString(IndexingType indexingType) case NonArray: basicName = "NonArray"; break; + case NonArrayWithInt32: + basicName = "NonArrayWithInt32"; + break; + case NonArrayWithDouble: + basicName = "NonArrayWithDouble"; + break; case NonArrayWithContiguous: basicName = "NonArrayWithContiguous"; break; @@ -51,6 +97,15 @@ const char* indexingTypeToString(IndexingType indexingType) case ArrayClass: basicName = "ArrayClass"; break; + case ArrayWithUndecided: + basicName = "ArrayWithUndecided"; + break; + case ArrayWithInt32: + basicName = "ArrayWithInt32"; + break; + case ArrayWithDouble: + basicName = "ArrayWithDouble"; + break; case ArrayWithContiguous: basicName = "ArrayWithContiguous"; break; diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h index 4bbe3cfa0..ab253be1e 100644 --- a/Source/JavaScriptCore/runtime/IndexingType.h +++ b/Source/JavaScriptCore/runtime/IndexingType.h @@ -26,6 +26,7 @@ #ifndef IndexingType_h #define IndexingType_h +#include "SpeculatedType.h" #include <wtf/StdLibExtras.h> namespace JSC { @@ -37,21 +38,32 @@ static const IndexingType IsArray = 1; // The shape of the indexed property storage. static const IndexingType IndexingShapeMask = 30; -static const IndexingType NoIndexingShape = 0; +static const IndexingType NoIndexingShape = 0; +static const IndexingType UndecidedShape = 2; // Only useful for arrays. +static const IndexingType Int32Shape = 20; +static const IndexingType DoubleShape = 22; static const IndexingType ContiguousShape = 26; static const IndexingType ArrayStorageShape = 28; static const IndexingType SlowPutArrayStorageShape = 30; +static const IndexingType IndexingShapeShift = 1; +static const IndexingType NumberOfIndexingShapes = 16; + // Additional flags for tracking the history of the type. These are usually // masked off unless you ask for them directly. static const IndexingType MayHaveIndexedAccessors = 32; // List of acceptable array types. static const IndexingType NonArray = 0; +static const IndexingType NonArrayWithInt32 = Int32Shape; +static const IndexingType NonArrayWithDouble = DoubleShape; static const IndexingType NonArrayWithContiguous = ContiguousShape; static const IndexingType NonArrayWithArrayStorage = ArrayStorageShape; static const IndexingType NonArrayWithSlowPutArrayStorage = SlowPutArrayStorageShape; static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution. +static const IndexingType ArrayWithUndecided = IsArray | UndecidedShape; +static const IndexingType ArrayWithInt32 = IsArray | Int32Shape; +static const IndexingType ArrayWithDouble = IsArray | DoubleShape; static const IndexingType ArrayWithContiguous = IsArray | ContiguousShape; static const IndexingType ArrayWithArrayStorage = IsArray | ArrayStorageShape; static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | SlowPutArrayStorageShape; @@ -60,6 +72,17 @@ static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | SlowPutArr NonArray: \ case ArrayClass +#define ALL_UNDECIDED_INDEXING_TYPES \ + ArrayWithUndecided + +#define ALL_INT32_INDEXING_TYPES \ + NonArrayWithInt32: \ + case ArrayWithInt32 + +#define ALL_DOUBLE_INDEXING_TYPES \ + NonArrayWithDouble: \ + case ArrayWithDouble + #define ALL_CONTIGUOUS_INDEXING_TYPES \ NonArrayWithContiguous: \ case ArrayWithContiguous @@ -83,6 +106,21 @@ static inline bool hasIndexingHeader(IndexingType type) return hasIndexedProperties(type); } +static inline bool hasUndecided(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == UndecidedShape; +} + +static inline bool hasInt32(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == Int32Shape; +} + +static inline bool hasDouble(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == DoubleShape; +} + static inline bool hasContiguous(IndexingType indexingType) { return (indexingType & IndexingShapeMask) == ContiguousShape; @@ -105,6 +143,12 @@ static inline bool shouldUseSlowPut(IndexingType indexingType) return (indexingType & IndexingShapeMask) == SlowPutArrayStorageShape; } +// Return an indexing type that can handle all of the elements of both indexing types. +IndexingType leastUpperBoundOfIndexingTypes(IndexingType, IndexingType); + +IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType, SpeculatedType); +IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType, JSValue); + const char* indexingTypeToString(IndexingType); // Mask of all possible types. diff --git a/Source/JavaScriptCore/runtime/JSActivation.h b/Source/JavaScriptCore/runtime/JSActivation.h index fc6336463..b8f5621af 100644 --- a/Source/JavaScriptCore/runtime/JSActivation.h +++ b/Source/JavaScriptCore/runtime/JSActivation.h @@ -30,7 +30,7 @@ #define JSActivation_h #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "JSVariableObject.h" #include "Nodes.h" #include "SymbolTable.h" diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index d1ece1a36..4ba5cc2bd 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -24,14 +24,14 @@ #include "JSArray.h" #include "ArrayPrototype.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" #include "CachedCall.h" +#include "CopiedSpace.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "Executable.h" #include "GetterSetter.h" -#include "IndexingHeaderInlineMethods.h" +#include "IndexingHeaderInlines.h" #include "PropertyNameArray.h" #include "Reject.h" #include <wtf/AVLTree.h> @@ -410,25 +410,33 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException exec, newLength, throwException, convertContiguousToArrayStorage(exec->globalData())); } - createInitialContiguous(exec->globalData(), newLength); + createInitialUndecided(exec->globalData(), newLength); return true; + case ArrayWithUndecided: + case ArrayWithInt32: + case ArrayWithDouble: case ArrayWithContiguous: if (newLength == m_butterfly->publicLength()) return true; if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push. || (newLength >= MIN_SPARSE_ARRAY_INDEX - && !isDenseEnoughForVector(newLength, countElementsInContiguous(m_butterfly)))) { + && !isDenseEnoughForVector(newLength, countElements()))) { return setLengthWithArrayStorage( exec, newLength, throwException, - convertContiguousToArrayStorage(exec->globalData())); + ensureArrayStorage(exec->globalData())); } if (newLength > m_butterfly->publicLength()) { - ensureContiguousLength(exec->globalData(), newLength); + ensureLength(exec->globalData(), newLength); return true; } - for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) - m_butterfly->contiguous()[i].clear(); + if (structure()->indexingType() == ArrayWithDouble) { + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguousDouble()[i] = QNaN; + } else { + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguous()[i].clear(); + } m_butterfly->setPublicLength(newLength); return true; @@ -448,6 +456,13 @@ JSValue JSArray::pop(ExecState* exec) case ArrayClass: return jsUndefined(); + case ArrayWithUndecided: + if (!m_butterfly->publicLength()) + return jsUndefined(); + // We have nothing but holes. So, drop down to the slow version. + break; + + case ArrayWithInt32: case ArrayWithContiguous: { unsigned length = m_butterfly->publicLength(); @@ -464,6 +479,22 @@ JSValue JSArray::pop(ExecState* exec) break; } + case ArrayWithDouble: { + unsigned length = m_butterfly->publicLength(); + + if (!length--) + return jsUndefined(); + + ASSERT(length < m_butterfly->vectorLength()); + double value = m_butterfly->contiguousDouble()[length]; + if (value == value) { + m_butterfly->contiguousDouble()[length] = QNaN; + m_butterfly->setPublicLength(length); + return JSValue(JSValue::EncodeAsDouble, value); + } + break; + } + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -518,10 +549,42 @@ void JSArray::push(ExecState* exec, JSValue value) { switch (structure()->indexingType()) { case ArrayClass: { - putByIndexBeyondVectorLengthWithArrayStorage(exec, 0, value, true, createInitialArrayStorage(exec->globalData())); - break; + createInitialUndecided(exec->globalData(), 0); + // Fall through. + } + + case ArrayWithUndecided: { + convertUndecidedForValue(exec->globalData(), value); + push(exec, value); + return; } + case ArrayWithInt32: { + if (!value.isInt32()) { + convertInt32ForValue(exec->globalData(), value); + push(exec, value); + return; + } + + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value); + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable()->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value); + return; + } + case ArrayWithContiguous: { unsigned length = m_butterfly->publicLength(); ASSERT(length <= m_butterfly->vectorLength()); @@ -538,10 +601,42 @@ void JSArray::push(ExecState* exec, JSValue value) return; } - putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, length, value); + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value); return; } + case ArrayWithDouble: { + if (!value.isNumber()) { + convertDoubleToContiguous(exec->globalData()); + push(exec, value); + return; + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + convertDoubleToContiguous(exec->globalData()); + push(exec, value); + return; + } + + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguousDouble()[length] = valueAsDouble; + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable()->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value); + break; + } + case ArrayWithSlowPutArrayStorage: { unsigned oldLength = length(); if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) { @@ -647,6 +742,11 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex case ArrayClass: return true; + case ArrayWithUndecided: + // Don't handle this because it's confusing and it shouldn't come up. + return false; + + case ArrayWithInt32: case ArrayWithContiguous: { unsigned oldLength = m_butterfly->publicLength(); ASSERT(count <= oldLength); @@ -654,7 +754,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex // We may have to walk the entire array to do the shift. We're willing to do // so only if it's not horribly slow. if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) - return shiftCountWithArrayStorage(startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); unsigned end = oldLength - count; for (unsigned i = startIndex; i < end; ++i) { @@ -668,7 +768,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex // about holes (at least for now), but it can detect them quickly. So // we convert to array storage and then allow the array storage path to // figure it out. - return shiftCountWithArrayStorage(startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); } // No need for a barrier since we're just moving data around in the same vector. // This is in line with our standing assumption that we won't have a deletion @@ -682,6 +782,41 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex return true; } + case ArrayWithDouble: { + unsigned oldLength = m_butterfly->publicLength(); + ASSERT(count <= oldLength); + + // We may have to walk the entire array to do the shift. We're willing to do + // so only if it's not horribly slow. + if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); + + unsigned end = oldLength - count; + for (unsigned i = startIndex; i < end; ++i) { + // Storing to a hole is fine since we're still having a good time. But reading + // from a hole is totally not fine, since we might have to read from the proto + // chain. + double v = m_butterfly->contiguousDouble()[i + count]; + if (UNLIKELY(v != v)) { + // The purpose of this path is to ensure that we don't make the same + // mistake in the future: shiftCountWithArrayStorage() can't do anything + // about holes (at least for now), but it can detect them quickly. So + // we convert to array storage and then allow the array storage path to + // figure it out. + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); + } + // No need for a barrier since we're just moving data around in the same vector. + // This is in line with our standing assumption that we won't have a deletion + // barrier. + m_butterfly->contiguousDouble()[i] = v; + } + for (unsigned i = end; i < oldLength; ++i) + m_butterfly->contiguousDouble()[i] = QNaN; + + m_butterfly->setPublicLength(oldLength - count); + return true; + } + case ArrayWithArrayStorage: case ArrayWithSlowPutArrayStorage: return shiftCountWithArrayStorage(startIndex, count, arrayStorage()); @@ -740,23 +875,25 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd { switch (structure()->indexingType()) { case ArrayClass: + case ArrayWithUndecided: // We could handle this. But it shouldn't ever come up, so we won't. return false; - + + case ArrayWithInt32: case ArrayWithContiguous: { unsigned oldLength = m_butterfly->publicLength(); // We may have to walk the entire array to do the unshift. We're willing to do so // only if it's not horribly slow. if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) - return unshiftCountWithArrayStorage(exec, startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); - ensureContiguousLength(exec->globalData(), oldLength + count); + ensureLength(exec->globalData(), oldLength + count); for (unsigned i = oldLength; i-- > startIndex;) { JSValue v = m_butterfly->contiguous()[i].get(); if (UNLIKELY(!v)) - return unshiftCountWithArrayStorage(exec, startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v); } @@ -768,6 +905,31 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd return true; } + case ArrayWithDouble: { + unsigned oldLength = m_butterfly->publicLength(); + + // We may have to walk the entire array to do the unshift. We're willing to do so + // only if it's not horribly slow. + if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); + + ensureLength(exec->globalData(), oldLength + count); + + for (unsigned i = oldLength; i-- > startIndex;) { + double v = m_butterfly->contiguousDouble()[i]; + if (UNLIKELY(v != v)) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); + m_butterfly->contiguousDouble()[i + count] = v; + } + + // NOTE: we're leaving being garbage in the part of the array that we shifted out + // of. This is fine because the caller is required to store over that area, and + // in contiguous mode storing into a hole is guaranteed to behave exactly the same + // as storing over an existing element. + + return true; + } + case ArrayWithArrayStorage: case ArrayWithSlowPutArrayStorage: return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage()); @@ -778,6 +940,20 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd } } +static int compareNumbersForQSortWithInt32(const void* a, const void* b) +{ + int32_t ia = static_cast<const JSValue*>(a)->asInt32(); + int32_t ib = static_cast<const JSValue*>(b)->asInt32(); + return ia - ib; +} + +static int compareNumbersForQSortWithDouble(const void* a, const void* b) +{ + double da = *static_cast<const double*>(a); + double db = *static_cast<const double*>(b); + return (da > db) - (da < db); +} + static int compareNumbersForQSort(const void* a, const void* b) { double da = static_cast<const JSValue*>(a)->asNumber(); @@ -795,7 +971,7 @@ static int compareByStringPairForQSort(const void* a, const void* b) template<IndexingType indexingType> void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) { - ASSERT(indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage); + ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage); unsigned lengthNotIncludingUndefined; unsigned newRelevantLength; @@ -814,11 +990,19 @@ void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallTy return; bool allValuesAreNumbers = true; - for (size_t i = 0; i < newRelevantLength; ++i) { - if (!data[i].isNumber()) { - allValuesAreNumbers = false; - break; + switch (indexingType) { + case ArrayWithInt32: + case ArrayWithDouble: + break; + + default: + for (size_t i = 0; i < newRelevantLength; ++i) { + if (!data[i].isNumber()) { + allValuesAreNumbers = false; + break; + } } + break; } if (!allValuesAreNumbers) @@ -827,7 +1011,23 @@ void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallTy // For numeric comparison, which is fast, qsort is faster than mergesort. We // also don't require mergesort's stability, since there's no user visible // side-effect from swapping the order of equal primitive values. - qsort(data, newRelevantLength, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); + int (*compare)(const void*, const void*); + switch (indexingType) { + case ArrayWithInt32: + compare = compareNumbersForQSortWithInt32; + break; + + case ArrayWithDouble: + compare = compareNumbersForQSortWithDouble; + ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(double)); + break; + + default: + compare = compareNumbersForQSort; + break; + } + + qsort(data, newRelevantLength, sizeof(WriteBarrier<Unknown>), compare); return; } @@ -839,6 +1039,14 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal case ArrayClass: return; + case ArrayWithInt32: + sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData); + break; + + case ArrayWithDouble: + sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData); + break; + case ArrayWithContiguous: sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData); return; @@ -854,7 +1062,7 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal } template<IndexingType indexingType> -void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, unsigned relevantLength) +void JSArray::sortCompactedVector(ExecState* exec, void* begin, unsigned relevantLength) { if (!relevantLength) return; @@ -875,11 +1083,31 @@ void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, Heap::heap(this)->pushTempSortVector(&values); bool isSortingPrimitiveValues = true; - for (size_t i = 0; i < relevantLength; i++) { - JSValue value = begin[i].get(); - ASSERT(!value.isUndefined()); - values[i].first = value; - isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); + switch (indexingType) { + case ArrayWithInt32: + for (size_t i = 0; i < relevantLength; i++) { + JSValue value = static_cast<WriteBarrier<Unknown>*>(begin)[i].get(); + ASSERT(value.isInt32()); + values[i].first = value; + } + break; + + case ArrayWithDouble: + for (size_t i = 0; i < relevantLength; i++) { + double value = static_cast<double*>(begin)[i]; + ASSERT(value == value); + values[i].first = JSValue(JSValue::EncodeAsDouble, value); + } + break; + + default: + for (size_t i = 0; i < relevantLength; i++) { + JSValue value = static_cast<WriteBarrier<Unknown>*>(begin)[i].get(); + ASSERT(!value.isUndefined()); + values[i].first = value; + isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); + } + break; } // FIXME: The following loop continues to call toString on subsequent values even after @@ -910,8 +1138,10 @@ void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, // If the toString function changed the length of the array or vector storage, // increase the length to handle the orignal number of actual values. switch (indexingType) { + case ArrayWithInt32: + case ArrayWithDouble: case ArrayWithContiguous: - ensureContiguousLength(globalData, relevantLength); + ensureLength(globalData, relevantLength); break; case ArrayWithArrayStorage: @@ -927,8 +1157,12 @@ void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, CRASH(); } - for (size_t i = 0; i < relevantLength; i++) - begin[i].set(globalData, this, values[i].first); + for (size_t i = 0; i < relevantLength; i++) { + if (indexingType == ArrayWithDouble) + static_cast<double*>(begin)[i] = values[i].first.asNumber(); + else + static_cast<WriteBarrier<Unknown>*>(begin)[i].set(globalData, this, values[i].first); + } Heap::heap(this)->popTempSortVector(&values); } @@ -939,8 +1173,31 @@ void JSArray::sort(ExecState* exec) switch (structure()->indexingType()) { case ArrayClass: + case ArrayWithUndecided: return; + case ArrayWithInt32: { + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<ArrayWithInt32>( + lengthNotIncludingUndefined, newRelevantLength); + + sortCompactedVector<ArrayWithInt32>( + exec, m_butterfly->contiguousInt32(), lengthNotIncludingUndefined); + return; + } + + case ArrayWithDouble: { + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<ArrayWithDouble>( + lengthNotIncludingUndefined, newRelevantLength); + + sortCompactedVector<ArrayWithDouble>( + exec, m_butterfly->contiguousDouble(), lengthNotIncludingUndefined); + return; + } + case ArrayWithContiguous: { unsigned lengthNotIncludingUndefined; unsigned newRelevantLength; @@ -1087,12 +1344,12 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call unsigned numDefined = 0; unsigned numUndefined = 0; - + // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. for (; numDefined < usedVectorLength; ++numDefined) { if (numDefined > m_butterfly->vectorLength()) break; - JSValue v = currentIndexingData()[numDefined].get(); + JSValue v = getHolyIndexQuickly(numDefined); if (!v || v.isUndefined()) break; tree.abstractor().m_nodes[numDefined].value = v; @@ -1101,7 +1358,7 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call for (unsigned i = numDefined; i < usedVectorLength; ++i) { if (i > m_butterfly->vectorLength()) break; - JSValue v = currentIndexingData()[i].get(); + JSValue v = getHolyIndexQuickly(i); if (v) { if (v.isUndefined()) ++numUndefined; @@ -1112,7 +1369,7 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call } } } - + unsigned newUsedVectorLength = numDefined + numUndefined; // The array size may have changed. Figure out the new bounds. @@ -1127,16 +1384,31 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call iter.start_iter_least(tree); JSGlobalData& globalData = exec->globalData(); for (unsigned i = 0; i < elementsToExtractThreshold; ++i) { - currentIndexingData()[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); + if (structure()->indexingType() == ArrayWithDouble) + butterfly()->contiguousDouble()[i] = tree.abstractor().m_nodes[*iter].value.asNumber(); + else + currentIndexingData()[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); ++iter; } // Put undefined values back in. - for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) - currentIndexingData()[i].setUndefined(); + switch (structure()->indexingType()) { + case ArrayWithInt32: + case ArrayWithDouble: + ASSERT(elementsToExtractThreshold == undefinedElementsThreshold); + break; + + default: + for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) + currentIndexingData()[i].setUndefined(); + } // Ensure that unused values in the vector are zeroed out. - for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) - currentIndexingData()[i].clear(); + for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) { + if (structure()->indexingType() == ArrayWithDouble) + butterfly()->contiguousDouble()[i] = QNaN; + else + currentIndexingData()[i].clear(); + } if (hasArrayStorage(structure()->indexingType())) arrayStorage()->m_numValuesInVector = newUsedVectorLength; @@ -1148,8 +1420,17 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, switch (structure()->indexingType()) { case ArrayClass: + case ArrayWithUndecided: return; + case ArrayWithInt32: + sortVector<ArrayWithInt32>(exec, compareFunction, callType, callData); + return; + + case ArrayWithDouble: + sortVector<ArrayWithDouble>(exec, compareFunction, callType, callData); + return; + case ArrayWithContiguous: sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData); return; @@ -1173,11 +1454,30 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) case ArrayClass: return; + case ArrayWithUndecided: { + vector = 0; + vectorEnd = 0; + break; + } + + case ArrayWithInt32: case ArrayWithContiguous: { vectorEnd = m_butterfly->publicLength(); vector = m_butterfly->contiguous(); break; } + + case ArrayWithDouble: { + vector = 0; + vectorEnd = 0; + for (; i < m_butterfly->publicLength(); ++i) { + double v = butterfly()->contiguousDouble()[i]; + if (v != v) + break; + args.append(JSValue(JSValue::EncodeAsDouble, v)); + } + break; + } case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -1216,12 +1516,31 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le case ArrayClass: return; + case ArrayWithUndecided: { + vector = 0; + vectorEnd = 0; + break; + } + + case ArrayWithInt32: case ArrayWithContiguous: { vector = m_butterfly->contiguous(); vectorEnd = m_butterfly->publicLength(); break; } + case ArrayWithDouble: { + vector = 0; + vectorEnd = 0; + for (; i < m_butterfly->publicLength(); ++i) { + double v = m_butterfly->contiguousDouble()[i]; + if (v != v) + break; + callFrame->setArgument(i, JSValue(JSValue::EncodeAsDouble, v)); + } + break; + } + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); vector = storage->m_vector; @@ -1259,12 +1578,40 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt unsigned numUndefined = 0; for (; numDefined < myRelevantLength; ++numDefined) { + if (indexingType == ArrayWithInt32) { + JSValue v = m_butterfly->contiguousInt32()[numDefined].get(); + if (!v) + break; + ASSERT(v.isInt32()); + continue; + } + if (indexingType == ArrayWithDouble) { + double v = m_butterfly->contiguousDouble()[numDefined]; + if (v != v) + break; + continue; + } JSValue v = indexingData<indexingType>()[numDefined].get(); if (!v || v.isUndefined()) break; } for (unsigned i = numDefined; i < myRelevantLength; ++i) { + if (indexingType == ArrayWithInt32) { + JSValue v = m_butterfly->contiguousInt32()[i].get(); + if (!v) + continue; + ASSERT(v.isInt32()); + m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v); + continue; + } + if (indexingType == ArrayWithDouble) { + double v = m_butterfly->contiguousDouble()[i]; + if (v != v) + continue; + m_butterfly->contiguousDouble()[numDefined++] = v; + continue; + } JSValue v = indexingData<indexingType>()[i].get(); if (v) { if (v.isUndefined()) @@ -1279,10 +1626,23 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt if (hasArrayStorage(indexingType)) ASSERT(!arrayStorage()->m_sparseMap); - for (unsigned i = numDefined; i < newRelevantLength; ++i) - indexingData<indexingType>()[i].setUndefined(); - for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) - indexingData<indexingType>()[i].clear(); + switch (indexingType) { + case ArrayWithInt32: + case ArrayWithDouble: + ASSERT(numDefined == newRelevantLength); + break; + + default: + for (unsigned i = numDefined; i < newRelevantLength; ++i) + indexingData<indexingType>()[i].setUndefined(); + break; + } + for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) { + if (indexingType == ArrayWithDouble) + m_butterfly->contiguousDouble()[i] = QNaN; + else + indexingData<indexingType>()[i].clear(); + } if (hasArrayStorage(indexingType)) arrayStorage()->m_numValuesInVector = newRelevantLength; diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index 1d1e64173..ea1ed9047 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -22,7 +22,7 @@ #define JSArray_h #include "ArrayConventions.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "JSObject.h" namespace JSC { @@ -162,7 +162,7 @@ private: void sortNumericVector(ExecState*, JSValue compareFunction, CallType, const CallData&); template<IndexingType indexingType> - void sortCompactedVector(ExecState*, WriteBarrier<Unknown>* begin, unsigned relevantLength); + void sortCompactedVector(ExecState*, void* begin, unsigned relevantLength); template<IndexingType indexingType> void sortVector(ExecState*, JSValue compareFunction, CallType, const CallData&); @@ -174,13 +174,14 @@ private: void compactForSorting(unsigned& numDefined, unsigned& newRelevantLength); }; -inline Butterfly* createContiguousArrayButterfly(JSGlobalData& globalData, unsigned length) +inline Butterfly* createContiguousArrayButterfly(JSGlobalData& globalData, unsigned length, unsigned& vectorLength) { IndexingHeader header; - header.setVectorLength(std::max(length, BASE_VECTOR_LEN)); + vectorLength = std::max(length, BASE_VECTOR_LEN); + header.setVectorLength(vectorLength); header.setPublicLength(length); Butterfly* result = Butterfly::create( - globalData, 0, 0, true, header, header.vectorLength() * sizeof(EncodedJSValue)); + globalData, 0, 0, true, header, vectorLength * sizeof(EncodedJSValue)); return result; } @@ -200,13 +201,23 @@ Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData&, unsigned inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength) { Butterfly* butterfly; - if (LIKELY(structure->indexingType() == ArrayWithContiguous)) { - butterfly = createContiguousArrayButterfly(globalData, initialLength); + if (LIKELY(!hasArrayStorage(structure->indexingType()))) { + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + unsigned vectorLength; + butterfly = createContiguousArrayButterfly(globalData, initialLength, vectorLength); ASSERT(initialLength < MIN_SPARSE_ARRAY_INDEX); + if (hasDouble(structure->indexingType())) { + for (unsigned i = 0; i < vectorLength; ++i) + butterfly->contiguousDouble()[i] = QNaN; + } } else { ASSERT( structure->indexingType() == ArrayWithSlowPutArrayStorage - || (initialLength && structure->indexingType() == ArrayWithArrayStorage)); + || structure->indexingType() == ArrayWithArrayStorage); butterfly = createArrayButterfly(globalData, initialLength); } JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly); @@ -221,8 +232,13 @@ inline JSArray* JSArray::tryCreateUninitialized(JSGlobalData& globalData, Struct return 0; Butterfly* butterfly; - if (LIKELY(structure->indexingType() == ArrayWithContiguous)) { - + if (LIKELY(!hasArrayStorage(structure->indexingType()))) { + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + void* temp; if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, vectorLength * sizeof(EncodedJSValue)), &temp)) return 0; diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp index d8f611477..bb1af9d20 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -31,8 +31,6 @@ namespace JSC { -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSBoundFunction); - const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) @@ -88,6 +86,11 @@ JSBoundFunction* JSBoundFunction::create(ExecState* exec, JSGlobalObject* global return function; } +void JSBoundFunction::destroy(JSCell* cell) +{ + static_cast<JSBoundFunction*>(cell)->JSBoundFunction::~JSBoundFunction(); +} + bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSValue value) { return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(exec, value); diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.h b/Source/JavaScriptCore/runtime/JSBoundFunction.h index 05a6ad8e1..886021940 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.h +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.h @@ -38,6 +38,8 @@ public: typedef JSFunction Base; static JSBoundFunction* create(ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const String&); + + static void destroy(JSCell*); static bool customHasInstance(JSObject*, ExecState*, JSValue); diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 3b37613d1..b28fedd8d 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -28,9 +28,8 @@ #include "ConstructData.h" #include "Heap.h" #include "JSLock.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" #include "SlotVisitor.h" -#include "SlotVisitorInlineMethods.h" #include "TypedArrayDescriptor.h" #include "WriteBarrier.h" #include <wtf/Noncopyable.h> diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp index 891a23930..0a98e7c13 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -48,8 +48,6 @@ EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); } -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFunction); - const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFunction) }; bool JSFunction::isHostFunctionNonInline() const @@ -76,10 +74,25 @@ JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, in return function; } +void JSFunction::destroy(JSCell* cell) +{ + static_cast<JSFunction*>(cell)->JSFunction::~JSFunction(); +} + JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) : Base(exec->globalData(), structure) , m_executable() , m_scope(exec->globalData(), this, globalObject) + // We initialize blind so that changes to the prototype after function creation but before + // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the + // watchpoint will start watching and any changes will both force deoptimization and disable + // future attempts to optimize. This is necessary because we are guaranteed that the + // inheritorID is changed exactly once prior to optimizations kicking in. We could be + // smarter and count the number of times the prototype is clobbered and only optimize if it + // was clobbered exactly once, but that seems like overkill. In almost all cases it will be + // clobbered once, and if it's clobbered more than once, that will probably only occur + // before we started optimizing, anyway. + , m_inheritorIDWatchpoint(InitializedBlind) { } @@ -340,6 +353,7 @@ void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, J PropertySlot slot; thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); thisObject->m_cachedInheritorID.clear(); + thisObject->m_inheritorIDWatchpoint.notifyWrite(); // Don't allow this to be cached, since a [[Put]] must clear m_cachedInheritorID. PutPropertySlot dontCache; Base::put(thisObject, exec, propertyName, value, dontCache); @@ -386,6 +400,7 @@ bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNa PropertySlot slot; thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); thisObject->m_cachedInheritorID.clear(); + thisObject->m_inheritorIDWatchpoint.notifyWrite(); return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); } diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h index c1f066585..322ee58e3 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.h +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -25,7 +25,9 @@ #define JSFunction_h #include "InternalFunction.h" +#include "JSDestructibleObject.h" #include "JSScope.h" +#include "Watchpoint.h" namespace JSC { @@ -46,14 +48,14 @@ namespace JSC { JS_EXPORT_PRIVATE String getCalculatedDisplayName(CallFrame*, JSObject*); - class JSFunction : public JSNonFinalObject { + class JSFunction : public JSDestructibleObject { friend class JIT; friend class DFG::SpeculativeJIT; friend class DFG::JITCompiler; friend class JSGlobalData; public: - typedef JSNonFinalObject Base; + typedef JSDestructibleObject Base; JS_EXPORT_PRIVATE static JSFunction* create(ExecState*, JSGlobalObject*, int length, const String& name, NativeFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor); @@ -66,6 +68,8 @@ namespace JSC { return function; } + static void destroy(JSCell*); + JS_EXPORT_PRIVATE String name(ExecState*); JS_EXPORT_PRIVATE String displayName(ExecState*); const String calculatedDisplayName(ExecState*); @@ -129,6 +133,21 @@ namespace JSC { return m_cachedInheritorID.get(); } + Structure* tryGetKnownInheritorID() + { + if (!m_cachedInheritorID) + return 0; + if (m_inheritorIDWatchpoint.hasBeenInvalidated()) + return 0; + return m_cachedInheritorID.get(); + } + + void addInheritorIDWatchpoint(Watchpoint* watchpoint) + { + ASSERT(tryGetKnownInheritorID()); + m_inheritorIDWatchpoint.add(watchpoint); + } + static size_t offsetOfCachedInheritorID() { return OBJECT_OFFSETOF(JSFunction, m_cachedInheritorID); @@ -169,6 +188,7 @@ namespace JSC { WriteBarrier<ExecutableBase> m_executable; WriteBarrier<JSScope> m_scope; WriteBarrier<Structure> m_cachedInheritorID; + InlineWatchpointSet m_inheritorIDWatchpoint; }; inline bool JSValue::isFunction() const diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index 4dca5b0f6..74429c7a7 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -507,17 +507,17 @@ void JSGlobalData::dumpRegExpTrace() RTTraceList::iterator iter = ++m_rtTraceList->begin(); if (iter != m_rtTraceList->end()) { - dataLog("\nRegExp Tracing\n"); - dataLog(" match() matches\n"); - dataLog("Regular Expression JIT Address calls found\n"); - dataLog("----------------------------------------+----------------+----------+----------\n"); + dataLogF("\nRegExp Tracing\n"); + dataLogF(" match() matches\n"); + dataLogF("Regular Expression JIT Address calls found\n"); + dataLogF("----------------------------------------+----------------+----------+----------\n"); unsigned reCount = 0; for (; iter != m_rtTraceList->end(); ++iter, ++reCount) (*iter)->printTraceData(); - dataLog("%d Regular Expressions\n", reCount); + dataLogF("%d Regular Expressions\n", reCount); } m_rtTraceList->clear(); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index c466a2b04..6f20f0e93 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -230,9 +230,16 @@ void JSGlobalObject::reset(JSValue prototype) m_callbackObjectStructure.set(exec->globalData(), this, JSCallbackObject<JSDestructibleObject>::createStructure(exec->globalData(), this, m_objectPrototype.get())); m_arrayPrototype.set(exec->globalData(), this, ArrayPrototype::create(exec, this, ArrayPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); - m_arrayStructure.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithContiguous)); - m_arrayStructureWithArrayStorage.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithArrayStorage)); - m_arrayStructureForSlowPut.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage)); + + m_originalArrayStructureForIndexingShape[UndecidedShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithUndecided)); + m_originalArrayStructureForIndexingShape[Int32Shape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithInt32)); + m_originalArrayStructureForIndexingShape[DoubleShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithDouble)); + m_originalArrayStructureForIndexingShape[ContiguousShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithContiguous)); + m_originalArrayStructureForIndexingShape[ArrayStorageShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithArrayStorage)); + m_originalArrayStructureForIndexingShape[SlowPutArrayStorageShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage)); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + m_arrayStructureForIndexingShapeDuringAllocation[i] = m_originalArrayStructureForIndexingShape[i]; + m_regExpMatchesArrayStructure.set(exec->globalData(), this, RegExpMatchesArray::createStructure(exec->globalData(), this, m_arrayPrototype.get())); m_stringPrototype.set(exec->globalData(), this, StringPrototype::create(exec, this, StringPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); @@ -252,8 +259,6 @@ void JSGlobalObject::reset(JSValue prototype) m_regExpPrototype.set(exec->globalData(), this, RegExpPrototype::create(exec, this, RegExpPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()), emptyRegex)); m_regExpStructure.set(exec->globalData(), this, RegExpObject::createStructure(exec->globalData(), this, m_regExpPrototype.get())); - m_methodCallDummy.set(exec->globalData(), this, constructEmptyObject(exec)); - m_errorPrototype.set(exec->globalData(), this, ErrorPrototype::create(exec, this, ErrorPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); m_errorStructure.set(exec->globalData(), this, ErrorInstance::createStructure(exec->globalData(), this, m_errorPrototype.get())); @@ -360,7 +365,9 @@ inline bool hasBrokenIndexing(JSObject* object) { // This will change if we have more indexing types. IndexingType type = object->structure()->indexingType(); - return hasContiguous(type) || hasFastArrayStorage(type); + // This could be made obviously more efficient, but isn't made so right now, because + // we expect this to be an unlikely slow path anyway. + return hasUndecided(type) || hasInt32(type) || hasDouble(type) || hasContiguous(type) || hasFastArrayStorage(type); } void ObjectsWithBrokenIndexingFinder::operator()(JSCell* cell) @@ -412,8 +419,8 @@ void JSGlobalObject::haveABadTime(JSGlobalData& globalData) // Make sure that all JSArray allocations that load the appropriate structure from // this object now load a structure that uses SlowPut. - m_arrayStructure.set(globalData, this, m_arrayStructureForSlowPut.get()); - m_arrayStructureWithArrayStorage.set(globalData, this, m_arrayStructureForSlowPut.get()); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + m_arrayStructureForIndexingShapeDuringAllocation[i].set(globalData, this, originalArrayStructureForIndexingType(ArrayWithSlowPutArrayStorage)); // Make sure that all objects that have indexed storage switch to the slow kind of // indexed storage. @@ -428,6 +435,14 @@ void JSGlobalObject::haveABadTime(JSGlobalData& globalData) } } +bool JSGlobalObject::arrayPrototypeChainIsSane() +{ + return !hasIndexedProperties(m_arrayPrototype->structure()->indexingType()) + && m_arrayPrototype->prototype() == m_objectPrototype.get() + && !hasIndexedProperties(m_objectPrototype->structure()->indexingType()) + && m_objectPrototype->prototype().isNull(); +} + void JSGlobalObject::createThrowTypeError(ExecState* exec) { JSFunction* thrower = JSFunction::create(exec, this, 0, String(), globalFuncThrowTypeError); @@ -457,7 +472,6 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Base::visitChildren(thisObject, visitor); visitor.append(&thisObject->m_globalThis); - visitor.append(&thisObject->m_methodCallDummy); visitor.append(&thisObject->m_regExpConstructor); visitor.append(&thisObject->m_errorConstructor); @@ -488,9 +502,10 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_activationStructure); visitor.append(&thisObject->m_nameScopeStructure); visitor.append(&thisObject->m_argumentsStructure); - visitor.append(&thisObject->m_arrayStructure); - visitor.append(&thisObject->m_arrayStructureWithArrayStorage); - visitor.append(&thisObject->m_arrayStructureForSlowPut); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + visitor.append(&thisObject->m_originalArrayStructureForIndexingShape[i]); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + visitor.append(&thisObject->m_arrayStructureForIndexingShapeDuringAllocation[i]); visitor.append(&thisObject->m_booleanObjectStructure); visitor.append(&thisObject->m_callbackConstructorStructure); visitor.append(&thisObject->m_callbackFunctionStructure); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index 3212363ab..e6edd0be7 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -22,6 +22,7 @@ #ifndef JSGlobalObject_h #define JSGlobalObject_h +#include "ArrayAllocationProfile.h" #include "JSArray.h" #include "JSGlobalData.h" #include "JSSegmentedVariableObject.h" @@ -99,7 +100,6 @@ namespace JSC { Register m_globalCallFrame[JSStack::CallFrameHeaderSize]; WriteBarrier<JSObject> m_globalThis; - WriteBarrier<JSObject> m_methodCallDummy; WriteBarrier<RegExpConstructor> m_regExpConstructor; WriteBarrier<ErrorConstructor> m_errorConstructor; @@ -130,9 +130,12 @@ namespace JSC { WriteBarrier<Structure> m_activationStructure; WriteBarrier<Structure> m_nameScopeStructure; WriteBarrier<Structure> m_argumentsStructure; - WriteBarrier<Structure> m_arrayStructure; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. - WriteBarrier<Structure> m_arrayStructureWithArrayStorage; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. - WriteBarrier<Structure> m_arrayStructureForSlowPut; + + // Lists the actual structures used for having these particular indexing shapes. + WriteBarrier<Structure> m_originalArrayStructureForIndexingShape[NumberOfIndexingShapes]; + // Lists the structures we should use during allocation for these particular indexing shapes. + WriteBarrier<Structure> m_arrayStructureForIndexingShapeDuringAllocation[NumberOfIndexingShapes]; + WriteBarrier<Structure> m_booleanObjectStructure; WriteBarrier<Structure> m_callbackConstructorStructure; WriteBarrier<Structure> m_callbackFunctionStructure; @@ -268,21 +271,31 @@ namespace JSC { RegExpPrototype* regExpPrototype() const { return m_regExpPrototype.get(); } ErrorPrototype* errorPrototype() const { return m_errorPrototype.get(); } - JSObject* methodCallDummy() const { return m_methodCallDummy.get(); } - Structure* withScopeStructure() const { return m_withScopeStructure.get(); } Structure* strictEvalActivationStructure() const { return m_strictEvalActivationStructure.get(); } Structure* activationStructure() const { return m_activationStructure.get(); } Structure* nameScopeStructure() const { return m_nameScopeStructure.get(); } Structure* argumentsStructure() const { return m_argumentsStructure.get(); } - Structure* arrayStructure() const { return m_arrayStructure.get(); } - Structure* arrayStructureWithArrayStorage() const { return m_arrayStructureWithArrayStorage.get(); } - void* addressOfArrayStructure() { return &m_arrayStructure; } - void* addressOfArrayStructureWithArrayStorage() { return &m_arrayStructureWithArrayStorage; } + Structure* originalArrayStructureForIndexingType(IndexingType indexingType) const + { + ASSERT(indexingType & IsArray); + return m_originalArrayStructureForIndexingShape[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get(); + } + Structure* arrayStructureForIndexingTypeDuringAllocation(IndexingType indexingType) const + { + ASSERT(indexingType & IsArray); + return m_arrayStructureForIndexingShapeDuringAllocation[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get(); + } + Structure* arrayStructureForProfileDuringAllocation(ArrayAllocationProfile* profile) const + { + return arrayStructureForIndexingTypeDuringAllocation(ArrayAllocationProfile::selectIndexingTypeFor(profile)); + } + bool isOriginalArrayStructure(Structure* structure) { - return structure == m_arrayStructure.get() || structure == m_arrayStructureWithArrayStorage.get(); + return originalArrayStructureForIndexingType(structure->indexingType() | IsArray) == structure; } + Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); } Structure* callbackConstructorStructure() const { return m_callbackConstructorStructure.get(); } Structure* callbackFunctionStructure() const { return m_callbackFunctionStructure.get(); } @@ -317,6 +330,8 @@ namespace JSC { } void haveABadTime(JSGlobalData&); + + bool arrayPrototypeChainIsSane(); void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; } unsigned profileGroup() const @@ -450,22 +465,27 @@ namespace JSC { return prototypeForLookup(exec->lexicalGlobalObject()); } - inline StructureChain* Structure::prototypeChain(ExecState* exec) const + inline StructureChain* Structure::prototypeChain(JSGlobalData& globalData, JSGlobalObject* globalObject) const { // We cache our prototype chain so our clients can share it. - if (!isValid(exec, m_cachedPrototypeChain.get())) { - JSValue prototype = prototypeForLookup(exec); - m_cachedPrototypeChain.set(exec->globalData(), this, StructureChain::create(exec->globalData(), prototype.isNull() ? 0 : asObject(prototype)->structure())); + if (!isValid(globalObject, m_cachedPrototypeChain.get())) { + JSValue prototype = prototypeForLookup(globalObject); + m_cachedPrototypeChain.set(globalData, this, StructureChain::create(globalData, prototype.isNull() ? 0 : asObject(prototype)->structure())); } return m_cachedPrototypeChain.get(); } - inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const + inline StructureChain* Structure::prototypeChain(ExecState* exec) const + { + return prototypeChain(exec->globalData(), exec->lexicalGlobalObject()); + } + + inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain) const { if (!cachedPrototypeChain) return false; - JSValue prototype = prototypeForLookup(exec); + JSValue prototype = prototypeForLookup(globalObject); WriteBarrier<Structure>* cachedStructure = cachedPrototypeChain->head(); while(*cachedStructure && !prototype.isNull()) { if (asObject(prototype)->structure() != cachedStructure->get()) @@ -476,6 +496,11 @@ namespace JSC { return prototype.isNull() && !*cachedStructure; } + inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const + { + return isValid(exec->lexicalGlobalObject(), cachedPrototypeChain); + } + inline JSGlobalObject* ExecState::dynamicGlobalObject() { if (this == lexicalGlobalObject()->globalExec()) @@ -497,34 +522,34 @@ namespace JSC { return constructEmptyObject(exec, exec->lexicalGlobalObject()); } - inline JSArray* constructEmptyArray(ExecState* exec, JSGlobalObject* globalObject, unsigned initialLength = 0) + inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, unsigned initialLength = 0) { - return JSArray::create(exec->globalData(), initialLength >= MIN_SPARSE_ARRAY_INDEX ? globalObject->arrayStructureWithArrayStorage() : globalObject->arrayStructure(), initialLength); + return ArrayAllocationProfile::updateLastAllocationFor(profile, JSArray::create(exec->globalData(), initialLength >= MIN_SPARSE_ARRAY_INDEX ? globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage) : globalObject->arrayStructureForProfileDuringAllocation(profile), initialLength)); } - inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength = 0) + inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, unsigned initialLength = 0) { - return constructEmptyArray(exec, exec->lexicalGlobalObject(), initialLength); + return constructEmptyArray(exec, profile, exec->lexicalGlobalObject(), initialLength); } - inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const ArgList& values) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const ArgList& values) { - return constructArray(exec, globalObject->arrayStructure(), values); + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArray(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values)); } - inline JSArray* constructArray(ExecState* exec, const ArgList& values) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, const ArgList& values) { - return constructArray(exec, exec->lexicalGlobalObject(), values); + return constructArray(exec, profile, exec->lexicalGlobalObject(), values); } - inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const JSValue* values, unsigned length) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const JSValue* values, unsigned length) { - return constructArray(exec, globalObject->arrayStructure(), values, length); + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArray(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values, length)); } - inline JSArray* constructArray(ExecState* exec, const JSValue* values, unsigned length) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, const JSValue* values, unsigned length) { - return constructArray(exec, exec->lexicalGlobalObject(), values, length); + return constructArray(exec, profile, exec->lexicalGlobalObject(), values, length); } class DynamicGlobalObjectScope { diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index 7ac76d350..1010ad2b7 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -52,7 +52,7 @@ namespace JSC { static JSValue encode(ExecState* exec, const char* doNotEscape) { - CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(true); + CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(String::StrictConversion); if (!cstr.data()) return throwError(exec, createURIError(exec, ASCIILiteral("String contained an illegal UTF-16 sequence."))); diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp index 85dbdfedb..5e3d12c92 100644 --- a/Source/JavaScriptCore/runtime/JSLock.cpp +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -74,7 +74,9 @@ JSLockHolder::~JSLockHolder() } JSLock::JSLock() - : m_lockCount(0) + : m_ownerThread(0) + , m_lockCount(0) + , m_lockDropDepth(0) { m_spinLock.Init(); } diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 6a3fb84e4..564093e33 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -24,14 +24,14 @@ #include "config.h" #include "JSObject.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "CopyVisitor.h" -#include "CopyVisitorInlineMethods.h" +#include "CopyVisitorInlines.h" #include "DatePrototype.h" #include "ErrorConstructor.h" #include "GetterSetter.h" -#include "IndexingHeaderInlineMethods.h" +#include "IndexingHeaderInlines.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "Lookup.h" @@ -42,7 +42,7 @@ #include "PropertyDescriptor.h" #include "PropertyNameArray.h" #include "Reject.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" #include <math.h> #include <wtf/Assertions.h> @@ -129,7 +129,16 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt size_t count; switch (structure->indexingType()) { - case ALL_CONTIGUOUS_INDEXING_TYPES: { + case ALL_UNDECIDED_INDEXING_TYPES: { + currentTarget = 0; + currentSource = 0; + count = 0; + break; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: { currentTarget = newButterfly->contiguous(); currentSource = butterfly->contiguous(); ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength()); @@ -152,8 +161,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt break; } - while (count--) - (currentTarget++)->setWithoutWriteBarrier((currentSource++)->get()); + memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue)); } m_butterfly = newButterfly; @@ -272,8 +280,10 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: break; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = thisObject->m_butterfly; if (i >= butterfly->vectorLength()) @@ -288,6 +298,20 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned return false; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + + double value = butterfly->contiguousDouble()[i]; + if (value == value) { + slot.setValue(JSValue(JSValue::EncodeAsDouble, value)); + return true; + } + + return false; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i >= storage->length()) @@ -332,26 +356,30 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV putByIndex(thisObject, exec, i, value, slot.isStrictMode()); return; } - + // Check if there are any setters or getters in the prototype chain JSValue prototype; if (propertyName != exec->propertyNames().underscoreProto) { for (JSObject* obj = thisObject; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { - if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) + ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName)); + if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value)) + && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); return; } } } - for (JSObject* obj = thisObject; ; obj = asObject(prototype)) { + JSObject* obj; + for (obj = thisObject; ; obj = asObject(prototype)) { unsigned attributes; JSCell* specificValue; PropertyOffset offset = obj->structure()->get(globalData, propertyName, attributes, specificValue); if (isValidOffset(offset)) { if (attributes & ReadOnly) { + ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName) || obj == thisObject); if (slot.isStrictMode()) throwError(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); return; @@ -359,6 +387,8 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV JSValue gs = obj->getDirectOffset(offset); if (gs.isGetterSetter()) { + ASSERT(attributes & Accessor); + ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName) || obj == thisObject); JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { if (slot.isStrictMode()) @@ -374,7 +404,8 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV // If this is WebCore's global object then we need to substitute the shell. call(exec, setterFunc, callType, callData, thisObject->methodTable()->toThisObject(thisObject, exec), args); return; - } + } else + ASSERT(!(attributes & Accessor)); // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. @@ -386,6 +417,7 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV break; } + ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName) || obj == thisObject); if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); return; @@ -405,6 +437,22 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, case ALL_BLANK_INDEXING_TYPES: break; + case ALL_UNDECIDED_INDEXING_TYPES: { + thisObject->convertUndecidedForValue(exec->globalData(), value); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + + case ALL_INT32_INDEXING_TYPES: { + if (!value.isInt32()) { + thisObject->convertInt32ForValue(exec->globalData(), value); + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + // Fall through. + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = thisObject->m_butterfly; if (propertyName >= butterfly->vectorLength()) @@ -415,6 +463,29 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, return; } + case ALL_DOUBLE_INDEXING_TYPES: { + if (!value.isNumber()) { + thisObject->convertDoubleToContiguous(exec->globalData()); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + thisObject->convertDoubleToContiguous(exec->globalData()); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + Butterfly* butterfly = thisObject->m_butterfly; + if (propertyName >= butterfly->vectorLength()) + break; + butterfly->contiguousDouble()[propertyName] = valueAsDouble; + if (propertyName >= butterfly->publicLength()) + butterfly->setPublicLength(propertyName + 1); + return; + } + case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -507,10 +578,13 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize // this case if we ever cared. - enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, ensureArrayStorageSlow(globalData)); break; case ALL_ARRAY_STORAGE_INDEXING_TYPES: enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); @@ -534,7 +608,7 @@ void JSObject::notifyPresenceOfIndexedAccessors(JSGlobalData& globalData) globalObject()->haveABadTime(globalData); } -WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalData, unsigned length) +Butterfly* JSObject::createInitialIndexedStorage(JSGlobalData& globalData, unsigned length, size_t elementSize) { ASSERT(length < MAX_ARRAY_INDEX); IndexingType oldType = structure()->indexingType(); @@ -544,9 +618,41 @@ WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalDat unsigned vectorLength = std::max(length, BASE_VECTOR_LEN); Butterfly* newButterfly = m_butterfly->growArrayRight( globalData, structure(), structure()->outOfLineCapacity(), false, 0, - sizeof(EncodedJSValue) * vectorLength); + elementSize * vectorLength); newButterfly->setPublicLength(length); newButterfly->setVectorLength(vectorLength); + return newButterfly; +} + +Butterfly* JSObject::createInitialUndecided(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateUndecided); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly; +} + +WriteBarrier<Unknown>* JSObject::createInitialInt32(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateInt32); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly->contiguousInt32(); +} + +double* JSObject::createInitialDouble(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(double)); + for (unsigned i = newButterfly->vectorLength(); i--;) + newButterfly->contiguousDouble()[i] = QNaN; + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateDouble); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly->contiguousDouble(); +} + +WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(EncodedJSValue)); Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous); setButterfly(globalData, newButterfly, newStructure); return newButterfly->contiguous(); @@ -577,10 +683,33 @@ ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) return createArrayStorage(globalData, 0, BASE_VECTOR_LEN); } -ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +WriteBarrier<Unknown>* JSObject::convertUndecidedToInt32(JSGlobalData& globalData) { - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(hasUndecided(structure()->indexingType())); + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateInt32)); + return m_butterfly->contiguousInt32(); +} + +double* JSObject::convertUndecidedToDouble(JSGlobalData& globalData) +{ + ASSERT(hasUndecided(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) + m_butterfly->contiguousDouble()[i] = QNaN; + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateDouble)); + return m_butterfly->contiguousDouble(); +} + +WriteBarrier<Unknown>* JSObject::convertUndecidedToContiguous(JSGlobalData& globalData) +{ + ASSERT(hasUndecided(structure()->indexingType())); + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(JSGlobalData& globalData, unsigned neededLength) +{ unsigned publicLength = m_butterfly->publicLength(); unsigned propertyCapacity = structure()->outOfLineCapacity(); unsigned propertySize = structure()->outOfLineSize(); @@ -599,7 +728,66 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData newStorage->m_sparseMap.clear(); newStorage->m_indexBias = 0; newStorage->m_numValuesInVector = 0; - for (unsigned i = publicLength; i--;) { + + return newStorage; +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasUndecided(structure()->indexingType())); + + ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + // No need to copy elements. + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, storage->butterfly(), newStructure); + return storage; +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertUndecidedToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(JSGlobalData& globalData) +{ + return convertUndecidedToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +double* JSObject::convertInt32ToDouble(JSGlobalData& globalData) +{ + ASSERT(hasInt32(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i]; + double* currentAsDouble = bitwise_cast<double*>(current); + JSValue v = current->get(); + if (!v) { + *currentAsDouble = QNaN; + continue; + } + ASSERT(v.isInt32()); + *currentAsDouble = v.asInt32(); + } + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateDouble)); + return m_butterfly->contiguousDouble(); +} + +WriteBarrier<Unknown>* JSObject::convertInt32ToContiguous(JSGlobalData& globalData) +{ + ASSERT(hasInt32(structure()->indexingType())); + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasInt32(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { JSValue v = m_butterfly->contiguous()[i].get(); if (!v) continue; @@ -608,7 +796,82 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData } Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); - setButterfly(globalData, newButterfly, newStructure); + setButterfly(globalData, newStorage->butterfly(), newStructure); + return newStorage; +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertInt32ToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(JSGlobalData& globalData) +{ + return convertInt32ToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +WriteBarrier<Unknown>* JSObject::convertDoubleToContiguous(JSGlobalData& globalData) +{ + ASSERT(hasDouble(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + double* current = &m_butterfly->contiguousDouble()[i]; + WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current); + double value = *current; + if (value != value) { + currentAsValue->clear(); + continue; + } + currentAsValue->setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); + } + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasDouble(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); + newStorage->m_numValuesInVector++; + } + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, newStorage->butterfly(), newStructure); + return newStorage; +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertDoubleToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData) +{ + return convertDoubleToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasContiguous(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (!v) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; + } + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, newStorage->butterfly(), newStructure); return newStorage; } @@ -622,48 +885,154 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData return convertContiguousToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); } -WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData) +void JSObject::convertUndecidedForValue(JSGlobalData& globalData, JSValue value) +{ + if (value.isInt32()) { + convertUndecidedToInt32(globalData); + return; + } + + if (value.isDouble()) { + convertUndecidedToDouble(globalData); + return; + } + + convertUndecidedToContiguous(globalData); +} + +void JSObject::convertInt32ForValue(JSGlobalData& globalData, JSValue value) +{ + ASSERT(!value.isInt32()); + + if (value.isDouble()) { + convertInt32ToDouble(globalData); + return; + } + + convertInt32ToContiguous(globalData); +} + +void JSObject::setIndexQuicklyToUndecided(JSGlobalData& globalData, unsigned index, JSValue value) +{ + ASSERT(index < m_butterfly->publicLength()); + ASSERT(index < m_butterfly->vectorLength()); + convertUndecidedForValue(globalData, value); + setIndexQuickly(globalData, index, value); +} + +void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(JSGlobalData& globalData, unsigned index, JSValue value) +{ + ASSERT(!value.isInt32()); + convertInt32ForValue(globalData, value); + setIndexQuickly(globalData, index, value); +} + +void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(JSGlobalData& globalData, unsigned index, JSValue value) +{ + ASSERT(!value.isNumber() || value.asNumber() != value.asNumber()); + convertDoubleToContiguous(globalData); + setIndexQuickly(globalData, index, value); +} + +WriteBarrier<Unknown>* JSObject::ensureInt32Slow(JSGlobalData& globalData) { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) return 0; - return createInitialContiguous(globalData, 0); + return createInitialInt32(globalData, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToInt32(globalData); + + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return 0; default: - ASSERT_NOT_REACHED(); + CRASH(); return 0; } } -ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData) +double* JSObject::ensureDoubleSlow(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + return 0; + return createInitialDouble(globalData, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToDouble(globalData); + + case ALL_INT32_INDEXING_TYPES: + return convertInt32ToDouble(globalData); + case ALL_CONTIGUOUS_INDEXING_TYPES: - ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure()->needsSlowPutIndexing()); - return convertContiguousToArrayStorage(globalData); + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return 0; + default: + CRASH(); + return 0; + } +} + +WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse())) - return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData); - return createInitialArrayStorage(globalData); + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + return 0; + return createInitialContiguous(globalData, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToContiguous(globalData); + + case ALL_INT32_INDEXING_TYPES: + return convertInt32ToContiguous(globalData); + + case ALL_DOUBLE_INDEXING_TYPES: + return convertDoubleToContiguous(globalData); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return 0; default: - ASSERT_NOT_REACHED(); + CRASH(); return 0; } } -Butterfly* JSObject::ensureIndexedStorageSlow(JSGlobalData& globalData) +ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData) { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(structure()->needsSlowPutIndexing())) - return createInitialArrayStorage(globalData)->butterfly(); if (UNLIKELY(indexingShouldBeSparse())) - return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)->butterfly(); - return Butterfly::fromContiguous(createInitialContiguous(globalData, 0)); + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData); + return createInitialArrayStorage(globalData); + + case ALL_UNDECIDED_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertUndecidedToArrayStorage(globalData); + + case ALL_INT32_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertInt32ToArrayStorage(globalData); + + case ALL_DOUBLE_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertDoubleToArrayStorage(globalData); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertContiguousToArrayStorage(globalData); default: ASSERT_NOT_REACHED(); @@ -674,13 +1043,6 @@ Butterfly* JSObject::ensureIndexedStorageSlow(JSGlobalData& globalData) ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ALL_CONTIGUOUS_INDEXING_TYPES: - // FIXME: This could be made way more efficient, if we cared. - return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); - case ALL_BLANK_INDEXING_TYPES: { createArrayStorage(globalData, 0, 0); SparseArrayValueMap* map = allocateSparseIndexMap(globalData); @@ -688,8 +1050,23 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J return arrayStorage(); } + case ALL_UNDECIDED_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertUndecidedToArrayStorage(globalData)); + + case ALL_INT32_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertInt32ToArrayStorage(globalData)); + + case ALL_DOUBLE_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertDoubleToArrayStorage(globalData)); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); + default: - ASSERT_NOT_REACHED(); + CRASH(); return 0; } } @@ -697,10 +1074,21 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ALL_CONTIGUOUS_INDEXING_TYPES: { + case ALL_UNDECIDED_INDEXING_TYPES: + convertUndecidedToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + + case ALL_INT32_INDEXING_TYPES: + convertInt32ToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + + case ALL_DOUBLE_INDEXING_TYPES: + convertDoubleToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + + case ALL_CONTIGUOUS_INDEXING_TYPES: convertContiguousToArrayStorage(globalData, AllocateSlowPutArrayStorage); break; - } case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { @@ -710,7 +1098,7 @@ void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) } default: - ASSERT_NOT_REACHED(); + CRASH(); break; } } @@ -877,8 +1265,10 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return true; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = thisObject->m_butterfly; if (i >= butterfly->vectorLength()) @@ -887,6 +1277,14 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) return true; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return true; + butterfly->contiguousDouble()[i] = QNaN; + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -1058,8 +1456,10 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa // which almost certainly means a different structure for PropertyNameArray. switch (object->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: break; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = object->m_butterfly; unsigned usedLength = butterfly->publicLength(); @@ -1071,6 +1471,18 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa break; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + double value = butterfly->contiguousDouble()[i]; + if (value != value) + continue; + propertyNames.add(Identifier::from(exec, i)); + } + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); @@ -1460,9 +1872,10 @@ bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, J return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow); } -void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState* exec, unsigned i, JSValue value) +template<IndexingType indexingShape> +void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value) { - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT((structure()->indexingType() & IndexingShapeMask) == indexingShape); ASSERT(!indexingShouldBeSparse()); // For us to get here, the index is either greater than the public length, or greater than @@ -1473,9 +1886,9 @@ void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState if (i >= MAX_ARRAY_INDEX - 1 || (i >= MIN_SPARSE_ARRAY_INDEX - && !isDenseEnoughForVector(i, countElementsInContiguous(m_butterfly)))) { + && !isDenseEnoughForVector(i, countElements<indexingShape>(m_butterfly)))) { ASSERT(i <= MAX_ARRAY_INDEX); - convertContiguousToArrayStorage(globalData, AllocateArrayStorage); + ensureArrayStorageSlow(globalData); SparseArrayValueMap* map = allocateSparseIndexMap(globalData); map->putEntry(exec, this, i, value, false); ASSERT(i >= arrayStorage()->length()); @@ -1483,10 +1896,30 @@ void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState return; } - ensureContiguousLength(globalData, i + 1); + ensureLength(globalData, i + 1); ASSERT(i < m_butterfly->vectorLength()); - m_butterfly->contiguous()[i].set(globalData, this, value); + switch (indexingShape) { + case Int32Shape: + ASSERT(value.isInt32()); + m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value); + break; + + case DoubleShape: { + ASSERT(value.isNumber()); + double valueAsDouble = value.asNumber(); + ASSERT(valueAsDouble == valueAsDouble); + m_butterfly->contiguousDouble()[i] = valueAsDouble; + break; + } + + case ContiguousShape: + m_butterfly->contiguous()[i].set(globalData, this, value); + break; + + default: + CRASH(); + } } void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) @@ -1592,8 +2025,23 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue break; } + case ALL_UNDECIDED_INDEXING_TYPES: { + CRASH(); + break; + } + + case ALL_INT32_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value); + break; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { - putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value); break; } @@ -1724,12 +2172,49 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV return true; } + case ALL_UNDECIDED_INDEXING_TYPES: { + convertUndecidedForValue(exec->globalData(), value); + // Reloop. + return putDirectIndex(exec, i, value, attributes, mode); + } + + case ALL_INT32_INDEXING_TYPES: { + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertInt32ToArrayStorage(globalData)); + } + if (!value.isInt32()) { + convertInt32ForValue(globalData, value); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value); + return true; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertDoubleToArrayStorage(globalData)); + } + if (!value.isNumber()) { + convertDoubleToContiguous(globalData); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + convertDoubleToContiguous(globalData); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value); + return true; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { if (attributes & (ReadOnly | Accessor)) { return putDirectIndexBeyondVectorLengthWithArrayStorage( exec, i, value, attributes, mode, convertContiguousToArrayStorage(globalData)); } - putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value); return true; } @@ -1769,33 +2254,65 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) unsigned vectorLength; unsigned length; - switch (structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: - vectorLength = 0; - length = 0; - break; - case ALL_CONTIGUOUS_INDEXING_TYPES: - case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (hasIndexedProperties(structure()->indexingType())) { vectorLength = m_butterfly->vectorLength(); length = m_butterfly->publicLength(); - break; - default: - CRASH(); - return 0; + } else { + vectorLength = 0; + length = 0; } + return getNewVectorLength(vectorLength, length, desiredLength); } -unsigned JSObject::countElementsInContiguous(Butterfly* butterfly) +template<IndexingType indexingShape> +unsigned JSObject::countElements(Butterfly* butterfly) { unsigned numValues = 0; for (unsigned i = butterfly->publicLength(); i--;) { - if (butterfly->contiguous()[i]) - numValues++; + switch (indexingShape) { + case Int32Shape: + case ContiguousShape: + if (butterfly->contiguous()[i]) + numValues++; + break; + + case DoubleShape: { + double value = butterfly->contiguousDouble()[i]; + if (value == value) + numValues++; + break; + } + + default: + CRASH(); + } } return numValues; } +unsigned JSObject::countElements() +{ + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return 0; + + case ALL_INT32_INDEXING_TYPES: + return countElements<Int32Shape>(m_butterfly); + + case ALL_DOUBLE_INDEXING_TYPES: + return countElements<DoubleShape>(m_butterfly); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + return countElements<ContiguousShape>(m_butterfly); + + default: + CRASH(); + return 0; + } +} + bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) { // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map @@ -1839,19 +2356,24 @@ bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength return true; } -void JSObject::ensureContiguousLengthSlow(JSGlobalData& globalData, unsigned length) +void JSObject::ensureLengthSlow(JSGlobalData& globalData, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); ASSERT(length > m_butterfly->vectorLength()); unsigned newVectorLength = std::min( length << 1, MAX_STORAGE_VECTOR_LENGTH); + unsigned oldVectorLength = m_butterfly->vectorLength(); m_butterfly = m_butterfly->growArrayRight( globalData, structure(), structure()->outOfLineCapacity(), true, - m_butterfly->vectorLength() * sizeof(EncodedJSValue), + oldVectorLength * sizeof(EncodedJSValue), newVectorLength * sizeof(EncodedJSValue)); + if (hasDouble(structure()->indexingType())) { + for (unsigned i = oldVectorLength; i < newVectorLength; ++i) + m_butterfly->contiguousDouble()[i] = QNaN; + } m_butterfly->setVectorLength(newVectorLength); } @@ -1881,8 +2403,10 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope switch (object->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = object->m_butterfly; if (i >= butterfly->vectorLength()) @@ -1894,6 +2418,17 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope return true; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + double value = butterfly->contiguousDouble()[i]; + if (value != value) + return false; + descriptor.setDescriptor(JSValue(JSValue::EncodeAsDouble, value), 0); + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); if (i >= storage->length()) diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 82455390f..4f7f4700b 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -39,6 +39,7 @@ #include "Structure.h" #include "JSGlobalData.h" #include "JSString.h" +#include "SlotVisitorInlines.h" #include "SparseArrayValueMap.h" #include <wtf/StdLibExtras.h> @@ -151,30 +152,16 @@ public: unsigned getArrayLength() const { - switch (structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: + if (!hasIndexedProperties(structure()->indexingType())) return 0; - case ALL_CONTIGUOUS_INDEXING_TYPES: - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->publicLength(); - default: - ASSERT_NOT_REACHED(); - return 0; - } + return m_butterfly->publicLength(); } unsigned getVectorLength() { - switch (structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: + if (!hasIndexedProperties(structure()->indexingType())) return 0; - case ALL_CONTIGUOUS_INDEXING_TYPES: - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->vectorLength(); - default: - ASSERT_NOT_REACHED(); - return 0; - } + return m_butterfly->vectorLength(); } JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); @@ -214,9 +201,19 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->vectorLength()) + return false; + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + return false; + return true; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; default: @@ -228,8 +225,11 @@ public: JSValue getIndexQuickly(unsigned i) { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous()[i].get(); + case ALL_DOUBLE_INDEXING_TYPES: + return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->m_vector[i].get(); default: @@ -242,11 +242,21 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: break; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: if (i < m_butterfly->publicLength()) return m_butterfly->contiguous()[i].get(); break; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->publicLength()) + break; + double result = m_butterfly->contiguousDouble()[i]; + if (result != result) + break; + return JSValue(JSValue::EncodeAsDouble, result); + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: if (i < m_butterfly->arrayStorage()->vectorLength()) return m_butterfly->arrayStorage()->m_vector[i].get(); @@ -279,7 +289,10 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case NonArrayWithArrayStorage: case ArrayWithArrayStorage: @@ -298,7 +311,10 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: return i < m_butterfly->vectorLength(); @@ -311,6 +327,14 @@ public: void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + // Fall through to contiguous case. + } case ALL_CONTIGUOUS_INDEXING_TYPES: { ASSERT(i < m_butterfly->vectorLength()); m_butterfly->contiguous()[i].set(globalData, this, v); @@ -318,6 +342,22 @@ public: m_butterfly->setPublicLength(i + 1); break; } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); WriteBarrier<Unknown>& x = storage->m_vector[i]; @@ -338,12 +378,40 @@ public: void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: { + setIndexQuicklyToUndecided(globalData, i, v); + break; + } + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(globalData, i, v); + break; + } + // Fall through. + } case ALL_CONTIGUOUS_INDEXING_TYPES: { ASSERT(i < m_butterfly->publicLength()); ASSERT(i < m_butterfly->vectorLength()); m_butterfly->contiguous()[i].set(globalData, this, v); break; } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + break; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); ASSERT(i < storage->length()); @@ -360,6 +428,9 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -374,6 +445,9 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -571,6 +645,30 @@ public: // foo->attemptToInterceptPutByIndexOnHole(...); bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + // Returns 0 if int32 storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (double, + // contiguous, array storage). + WriteBarrier<Unknown>* ensureInt32(JSGlobalData& globalData) + { + if (LIKELY(hasInt32(structure()->indexingType()))) + return m_butterfly->contiguousInt32(); + + return ensureInt32Slow(globalData); + } + + // Returns 0 if double storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (contiguous, + // or array storage). + double* ensureDouble(JSGlobalData& globalData) + { + if (LIKELY(hasDouble(structure()->indexingType()))) + return m_butterfly->contiguousDouble(); + + return ensureDoubleSlow(globalData); + } + // Returns 0 if contiguous storage cannot be created - either because // indexing should be sparse or because we're having a bad time. WriteBarrier<Unknown>* ensureContiguous(JSGlobalData& globalData) @@ -593,14 +691,6 @@ public: return ensureArrayStorageSlow(globalData); } - Butterfly* ensureIndexedStorage(JSGlobalData& globalData) - { - if (LIKELY(hasIndexedProperties(structure()->indexingType()))) - return m_butterfly; - - return ensureIndexedStorageSlow(globalData); - } - static size_t offsetOfInlineStorage(); static ptrdiff_t butterflyOffset() @@ -661,19 +751,47 @@ protected: return 0; } } - + + Butterfly* createInitialUndecided(JSGlobalData&, unsigned length); + WriteBarrier<Unknown>* createInitialInt32(JSGlobalData&, unsigned length); + double* createInitialDouble(JSGlobalData&, unsigned length); + WriteBarrier<Unknown>* createInitialContiguous(JSGlobalData&, unsigned length); + + void convertUndecidedForValue(JSGlobalData&, JSValue); + void convertInt32ForValue(JSGlobalData&, JSValue); + ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength); ArrayStorage* createInitialArrayStorage(JSGlobalData&); - WriteBarrier<Unknown>* createInitialContiguous(JSGlobalData&, unsigned length); + + WriteBarrier<Unknown>* convertUndecidedToInt32(JSGlobalData&); + double* convertUndecidedToDouble(JSGlobalData&); + WriteBarrier<Unknown>* convertUndecidedToContiguous(JSGlobalData&); + ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&); + + double* convertInt32ToDouble(JSGlobalData&); + WriteBarrier<Unknown>* convertInt32ToContiguous(JSGlobalData&); + ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&); + + WriteBarrier<Unknown>* convertDoubleToContiguous(JSGlobalData&); + ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&); + ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition); ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&); + ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&); bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException); - void putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState*, unsigned propertyName, JSValue); + template<IndexingType indexingShape> + void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); bool increaseVectorLength(JSGlobalData&, unsigned newLength); @@ -687,24 +805,33 @@ protected: // Call this if you want setIndexQuickly to succeed and you're sure that // the array is contiguous. - void ensureContiguousLength(JSGlobalData& globalData, unsigned length) + void ensureLength(JSGlobalData& globalData, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); if (m_butterfly->vectorLength() < length) - ensureContiguousLengthSlow(globalData, length); + ensureLengthSlow(globalData, length); if (m_butterfly->publicLength() < length) m_butterfly->setPublicLength(length); } - unsigned countElementsInContiguous(Butterfly*); + template<IndexingType indexingShape> + unsigned countElements(Butterfly*); + // This is relevant to undecided, int32, double, and contiguous. + unsigned countElements(); + + // This strange method returns a pointer to the start of the indexed data + // as if it contained JSValues. But it won't always contain JSValues. + // Make sure you cast this to the appropriate type before using. template<IndexingType indexingType> WriteBarrier<Unknown>* indexingData() { switch (indexingType) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous(); @@ -720,6 +847,7 @@ protected: WriteBarrier<Unknown>* currentIndexingData() { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous(); @@ -732,10 +860,32 @@ protected: } } + JSValue getHolyIndexQuickly(unsigned i) + { + switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->contiguous()[i].get(); + case ALL_DOUBLE_INDEXING_TYPES: { + double value = m_butterfly->contiguousDouble()[i]; + if (value == value) + return JSValue(JSValue::EncodeAsDouble, value); + return JSValue(); + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->m_vector[i].get(); + default: + CRASH(); + return JSValue(); + } + } + template<IndexingType indexingType> unsigned relevantLength() { switch (indexingType) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->publicLength(); @@ -753,6 +903,8 @@ protected: unsigned currentRelevantLength() { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->publicLength(); @@ -778,6 +930,8 @@ private: void isObject(); void isString(); + Butterfly* createInitialIndexedStorage(JSGlobalData&, unsigned length, size_t elementSize); + ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*); template<PutMode> @@ -800,11 +954,18 @@ private: JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&); - void ensureContiguousLengthSlow(JSGlobalData&, unsigned length); + ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(JSGlobalData&, unsigned neededLength); + + JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(JSGlobalData&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(JSGlobalData&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(JSGlobalData&, unsigned index, JSValue); + + void ensureLengthSlow(JSGlobalData&, unsigned length); + WriteBarrier<Unknown>* ensureInt32Slow(JSGlobalData&); + double* ensureDoubleSlow(JSGlobalData&); WriteBarrier<Unknown>* ensureContiguousSlow(JSGlobalData&); ArrayStorage* ensureArrayStorageSlow(JSGlobalData&); - Butterfly* ensureIndexedStorageSlow(JSGlobalData&); protected: Butterfly* m_butterfly; @@ -1152,6 +1313,8 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p // See comment on setNewProperty call below. if (!specificFunction) slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + structure()->setContainsReadOnlyProperties(); return true; } @@ -1219,6 +1382,8 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p // so leave the slot in an uncachable state. if (!specificFunction) slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); return true; } diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index e7f8cad17..d9253730f 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -214,10 +214,18 @@ char* JSValue::description() const snprintf(description, size, "Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble()); #endif } else if (isCell()) { - snprintf( - description, size, "Cell: %p (%p: %s, %s)", - asCell(), asCell()->structure(), asCell()->structure()->classInfo()->className, - indexingTypeToString(asCell()->structure()->indexingTypeIncludingHistory())); + if (asCell()->inherits(&Structure::s_info)) { + Structure* structure = jsCast<Structure*>(asCell()); + snprintf( + description, size, "Structure: %p: %s, %s", + structure, structure->classInfo()->className, + indexingTypeToString(structure->indexingTypeIncludingHistory())); + } else { + snprintf( + description, size, "Cell: %p -> %p (%p: %s, %s)", + asCell(), isObject() ? asObject(*this)->butterfly() : 0, asCell()->structure(), asCell()->structure()->classInfo()->className, + indexingTypeToString(asCell()->structure()->indexingTypeIncludingHistory())); + } } else if (isTrue()) snprintf(description, size, "True"); else if (isFalse()) diff --git a/Source/JavaScriptCore/runtime/JSValueInlineMethods.h b/Source/JavaScriptCore/runtime/JSValueInlines.h index 224982e9e..c5a42f67f 100644 --- a/Source/JavaScriptCore/runtime/JSValueInlineMethods.h +++ b/Source/JavaScriptCore/runtime/JSValueInlines.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JSValueInlineMethods_h -#define JSValueInlineMethods_h +#ifndef JSValueInlines_h +#define JSValueInlines_h #include "JSValue.h" @@ -493,4 +493,5 @@ namespace JSC { } // namespace JSC -#endif // JSValueInlineMethods_h +#endif // JSValueInlines_h + diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp index cd854417b..bf27327bf 100644 --- a/Source/JavaScriptCore/runtime/LiteralParser.cpp +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -27,8 +27,8 @@ #include "config.h" #include "LiteralParser.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "JSArray.h" #include "JSString.h" #include "Lexer.h" @@ -548,7 +548,7 @@ JSValue LiteralParser<CharType>::parse(ParserState initialState) switch(state) { startParseArray: case StartParseArray: { - JSArray* array = constructEmptyArray(m_exec); + JSArray* array = constructEmptyArray(m_exec, 0); objectStack.append(array); // fallthrough } diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp index 7df047d28..7e74a914b 100644 --- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -21,8 +21,8 @@ #include "config.h" #include "ObjectConstructor.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSFunction.h" @@ -182,7 +182,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exe return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested property names of a value that is not an object."))); PropertyNameArray properties(exec); asObject(exec->argument(0))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, IncludeDontEnumProperties); - JSArray* names = constructEmptyArray(exec); + JSArray* names = constructEmptyArray(exec, 0); size_t numProperties = properties.size(); for (size_t i = 0; i < numProperties; i++) names->push(exec, jsOwnedString(exec, properties[i].string())); @@ -196,7 +196,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested keys of a value that is not an object."))); PropertyNameArray properties(exec); asObject(exec->argument(0))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, ExcludeDontEnumProperties); - JSArray* keys = constructEmptyArray(exec); + JSArray* keys = constructEmptyArray(exec, 0); size_t numProperties = properties.size(); for (size_t i = 0; i < numProperties; i++) keys->push(exec, jsOwnedString(exec, properties[i].string())); diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h index 01df7e98c..7301bf6ec 100644 --- a/Source/JavaScriptCore/runtime/Operations.h +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -26,7 +26,7 @@ #include "Interpreter.h" #include "JSProxy.h" #include "JSString.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" namespace JSC { @@ -86,6 +86,7 @@ namespace JSC { if (ropeBuilder.length() < oldLength) // True for overflow return throwOutOfMemoryError(exec); + oldLength = ropeBuilder.length(); } return ropeBuilder.release(); @@ -105,6 +106,7 @@ namespace JSC { if (ropeBuilder.length() < oldLength) // True for overflow return throwOutOfMemoryError(exec); + oldLength = ropeBuilder.length(); } return ropeBuilder.release(); @@ -356,6 +358,23 @@ namespace JSC { } } + inline bool isPrototypeChainNormalized(JSGlobalObject* globalObject, Structure* structure) + { + for (;;) { + if (structure->typeInfo().type() == ProxyType) + return false; + + JSValue v = structure->prototypeForLookup(globalObject); + if (v.isNull()) + return true; + + structure = v.asCell()->structure(); + + if (structure->isDictionary()) + return false; + } + } + } // namespace JSC #endif // Operations_h diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h index d6d8c66c8..99a5f85a2 100644 --- a/Source/JavaScriptCore/runtime/Options.h +++ b/Source/JavaScriptCore/runtime/Options.h @@ -66,6 +66,8 @@ namespace JSC { v(bool, useDFGJIT, true) \ v(bool, useRegExpJIT, true) \ \ + v(bool, forceDFGCodeBlockLiveness, false) \ + \ /* showDisassembly implies showDFGDisassembly. */ \ v(bool, showDisassembly, false) \ v(bool, showDFGDisassembly, false) \ diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp index 7757274f1..3229f5207 100644 --- a/Source/JavaScriptCore/runtime/RegExp.cpp +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -520,24 +520,24 @@ void RegExp::matchCompareWithInterpreter(const String& s, int startOffset, int* differences++; if (differences) { - dataLog("RegExp Discrepency for /%s/\n string input ", pattern().utf8().data()); + dataLogF("RegExp Discrepency for /%s/\n string input ", pattern().utf8().data()); unsigned segmentLen = s.length() - static_cast<unsigned>(startOffset); - dataLog((segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset); + dataLogF((segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset); if (jitResult != interpreterResult) { - dataLog(" JIT result = %d, blah interpreted result = %d\n", jitResult, interpreterResult); + dataLogF(" JIT result = %d, blah interpreted result = %d\n", jitResult, interpreterResult); differences--; } else { - dataLog(" Correct result = %d\n", jitResult); + dataLogF(" Correct result = %d\n", jitResult); } if (differences) { for (unsigned j = 2, i = 0; i < m_numSubpatterns; j +=2, i++) { if (offsetVector[j] != interpreterOffsetVector[j]) - dataLog(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j, offsetVector[j], j, interpreterOffsetVector[j]); + dataLogF(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j, offsetVector[j], j, interpreterOffsetVector[j]); if ((offsetVector[j] >= 0) && (offsetVector[j+1] != interpreterOffsetVector[j+1])) - dataLog(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j+1, offsetVector[j+1], j+1, interpreterOffsetVector[j+1]); + dataLogF(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j+1, offsetVector[j+1], j+1, interpreterOffsetVector[j+1]); } } } diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp index ce9c2d2db..19f3b81ad 100644 --- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "RegExpMatchesArray.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index 35de40912..00dd1ed74 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -21,8 +21,8 @@ #include "config.h" #include "RegExpObject.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" diff --git a/Source/JavaScriptCore/runtime/SamplingCounter.cpp b/Source/JavaScriptCore/runtime/SamplingCounter.cpp index abed763ca..9826b88e4 100644 --- a/Source/JavaScriptCore/runtime/SamplingCounter.cpp +++ b/Source/JavaScriptCore/runtime/SamplingCounter.cpp @@ -35,10 +35,10 @@ void AbstractSamplingCounter::dump() { #if ENABLE(SAMPLING_COUNTERS) if (s_abstractSamplingCounterChain != &s_abstractSamplingCounterChainEnd) { - dataLog("\nSampling Counter Values:\n"); + dataLogF("\nSampling Counter Values:\n"); for (AbstractSamplingCounter* currCounter = s_abstractSamplingCounterChain; (currCounter != &s_abstractSamplingCounterChainEnd); currCounter = currCounter->m_next) - dataLog("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); - dataLog("\n\n"); + dataLogF("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); + dataLogF("\n\n"); } s_completed = true; #endif diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp index 5aafe8bb3..93009d806 100644 --- a/Source/JavaScriptCore/runtime/StringPrototype.cpp +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -22,9 +22,9 @@ #include "config.h" #include "StringPrototype.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CachedCall.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "Executable.h" #include "JSGlobalObjectFunctions.h" @@ -870,7 +870,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) return JSValue::encode(jsNull()); } - return JSValue::encode(constructArray(exec, list)); + return JSValue::encode(constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list)); } EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) @@ -973,7 +973,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // 3. Let A be a new array created as if by the expression new Array() // where Array is the standard built-in constructor with that name. - JSArray* result = constructEmptyArray(exec); + JSArray* result = constructEmptyArray(exec, 0); // 4. Let lengthA be 0. unsigned resultLength = 0; @@ -1388,7 +1388,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec) return throwVMTypeError(exec); String s = thisValue.toString(exec)->value(exec); JSValue a0 = exec->argument(0); - return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec)->value(exec), "\">", s, "</font>")); + String color = a0.toWTFString(exec); + color.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>")); } EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) @@ -1433,7 +1436,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) return JSValue::encode(jsNontrivialString(exec, impl)); } - return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec)->value(exec), "\">", s, "</font>")); + String fontSize = a0.toWTFString(exec); + fontSize.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>")); } EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) @@ -1443,7 +1449,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) return throwVMTypeError(exec); String s = thisValue.toString(exec)->value(exec); JSValue a0 = exec->argument(0); - return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec)->value(exec), "\">", s, "</a>")); + String anchor = a0.toWTFString(exec); + anchor.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>")); } EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) @@ -1453,7 +1462,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) return throwVMTypeError(exec); String s = thisValue.toString(exec)->value(exec); JSValue a0 = exec->argument(0); - String linkText = a0.toString(exec)->value(exec); + String linkText = a0.toWTFString(exec); + linkText.replaceWithLiteral('"', """); unsigned linkTextSize = linkText.length(); unsigned stringSize = s.length(); diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index e733c7e23..9ffe3b060 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -135,17 +135,17 @@ void Structure::dumpStatistics() } } - dataLog("Number of live Structures: %d\n", liveStructureSet.size()); - dataLog("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); - dataLog("Number of Structures that are leaf nodes: %d\n", numberLeaf); - dataLog("Number of Structures that singletons: %d\n", numberSingletons); - dataLog("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); - - dataLog("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); - dataLog("Size of sum of all property maps: %d\n", totalPropertyMapsSize); - dataLog("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); + dataLogF("Number of live Structures: %d\n", liveStructureSet.size()); + dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); + dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf); + dataLogF("Number of Structures that singletons: %d\n", numberSingletons); + dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); + + dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); + dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize); + dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); #else - dataLog("Dumping Structure statistics is not enabled.\n"); + dataLogF("Dumping Structure statistics is not enabled.\n"); #endif } @@ -543,12 +543,13 @@ Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* unsigned attributes = toAttributes(transitionKind); IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); - JSGlobalObject* globalObject = structure->globalObject(); - if (structure == globalObject->arrayStructure()) { - Structure* transition = globalObject->arrayStructureWithArrayStorage(); - if (transition->indexingTypeIncludingHistory() == indexingType) { - structure->notifyTransitionFromThisStructure(); - return transition; + if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { + if (globalObject->isOriginalArrayStructure(structure)) { + Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); + if (result->indexingTypeIncludingHistory() == indexingType) { + structure->notifyTransitionFromThisStructure(); + return result; + } } } @@ -694,11 +695,11 @@ static PropertyMapStatisticsExitLogger logger; PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() { - dataLog("\nJSC::PropertyMap statistics\n\n"); - dataLog("%d probes\n", numProbes); - dataLog("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); - dataLog("%d rehashes\n", numRehashes); - dataLog("%d removes\n", numRemoves); + dataLogF("\nJSC::PropertyMap statistics\n\n"); + dataLogF("%d probes\n", numProbes); + dataLogF("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); + dataLogF("%d rehashes\n", numRehashes); + dataLogF("%d removes\n", numRemoves); } #endif @@ -861,6 +862,32 @@ void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_objectToStringValue); } +bool Structure::prototypeChainMayInterceptStoreTo(JSGlobalData& globalData, PropertyName propertyName) +{ + unsigned i = propertyName.asIndex(); + if (i != PropertyName::NotAnIndex) + return anyObjectInChainMayInterceptIndexedAccesses(); + + for (Structure* current = this; ;) { + JSValue prototype = current->storedPrototype(); + if (prototype.isNull()) + return false; + + current = prototype.asCell()->structure(); + + unsigned attributes; + JSCell* specificValue; + PropertyOffset offset = current->get(globalData, propertyName, attributes, specificValue); + if (!JSC::isValidOffset(offset)) + continue; + + if (attributes & (ReadOnly | Accessor)) + return true; + + return false; + } +} + #if DO_PROPERTYMAP_CONSTENCY_CHECK void PropertyTable::checkConsistency() diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index 2b25803a6..6e4402c52 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -166,9 +166,15 @@ namespace JSC { JSValue prototypeForLookup(ExecState*) const; JSValue prototypeForLookup(JSGlobalObject*) const; JSValue prototypeForLookup(CodeBlock*) const; + StructureChain* prototypeChain(JSGlobalData&, JSGlobalObject*) const; StructureChain* prototypeChain(ExecState*) const; static void visitChildren(JSCell*, SlotVisitor&); - + + // Will just the prototype chain intercept this property access? + bool prototypeChainMayInterceptStoreTo(JSGlobalData&, PropertyName); + + bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; } + Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); @@ -397,6 +403,7 @@ namespace JSC { return numberOfSlotsForLastOffset(m_offset, m_typeInfo.type()); } + bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const; bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; void pin(); diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h index 3ab7b2014..5291ed540 100644 --- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -43,6 +43,9 @@ static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions th // Support for attributes used to indicate transitions not related to properties. // If any of these are used, the string portion of the key should be 0. enum NonPropertyTransition { + AllocateUndecided, + AllocateInt32, + AllocateDouble, AllocateContiguous, AllocateArrayStorage, AllocateSlowPutArrayStorage, @@ -58,14 +61,23 @@ inline unsigned toAttributes(NonPropertyTransition transition) inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition) { switch (transition) { - case AllocateContiguous: + case AllocateUndecided: ASSERT(!hasIndexedProperties(oldType)); - return oldType | ContiguousShape; + return oldType | UndecidedShape; + case AllocateInt32: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType)); + return (oldType & ~IndexingShapeMask) | Int32Shape; + case AllocateDouble: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType)); + return (oldType & ~IndexingShapeMask) | DoubleShape; + case AllocateContiguous: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType)); + return (oldType & ~IndexingShapeMask) | ContiguousShape; case AllocateArrayStorage: - ASSERT(!hasIndexedProperties(oldType) || hasContiguous(oldType)); + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType)); return (oldType & ~IndexingShapeMask) | ArrayStorageShape; case AllocateSlowPutArrayStorage: - ASSERT(!hasIndexedProperties(oldType) || hasContiguous(oldType)); + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType)); return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; case SwitchToSlowPutArrayStorage: ASSERT(hasFastArrayStorage(oldType)); |