diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-09 09:42:44 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-09 09:42:44 +0100 |
commit | a59391482883479a9b28a6f1ace6d1ebd08a7ecd (patch) | |
tree | fa539db054a20a67bff2fc891c33b0f4ec632916 /Source/JavaScriptCore/runtime | |
parent | cfd86b747d32ac22246a1aa908eaa720c63a88c1 (diff) | |
download | qtwebkit-a59391482883479a9b28a6f1ace6d1ebd08a7ecd.tar.gz |
Imported WebKit commit 7bcdfab9a40db7d16b4b95bb77d78b8a59c9e701 (http://svn.webkit.org/repository/webkit/trunk@134025)
New snapshot with numerious build fixes, including MSVC 2012 and ARM Thumb-2.
Diffstat (limited to 'Source/JavaScriptCore/runtime')
34 files changed, 1937 insertions, 690 deletions
diff --git a/Source/JavaScriptCore/runtime/ArgList.h b/Source/JavaScriptCore/runtime/ArgList.h index 29010b1ee..1fb1ce911 100644 --- a/Source/JavaScriptCore/runtime/ArgList.h +++ b/Source/JavaScriptCore/runtime/ArgList.h @@ -29,153 +29,153 @@ namespace JSC { - class SlotVisitor; - - class MarkedArgumentBuffer { - WTF_MAKE_NONCOPYABLE(MarkedArgumentBuffer); - friend class JSGlobalData; - friend class ArgList; - - private: - static const size_t inlineCapacity = 8; - typedef Vector<Register, inlineCapacity> VectorType; - typedef HashSet<MarkedArgumentBuffer*> ListSet; - - public: - // Constructor for a read-write list, to which you may append values. - // FIXME: Remove all clients of this API, then remove this API. - MarkedArgumentBuffer() - : m_size(0) - , m_capacity(inlineCapacity) - , m_buffer(&m_inlineBuffer[m_capacity - 1]) - , m_markSet(0) - { - } - - ~MarkedArgumentBuffer() - { - if (m_markSet) - m_markSet->remove(this); - - if (EncodedJSValue* base = mallocBase()) - delete [] base; - } - - size_t size() const { return m_size; } - bool isEmpty() const { return !m_size; } - - JSValue at(int i) const - { - if (i >= m_size) - return jsUndefined(); - - return JSValue::decode(slotFor(i)); - } - - void clear() - { - m_size = 0; - } - - void append(JSValue v) - { - if (m_size >= m_capacity) - return slowAppend(v); - - slotFor(m_size) = JSValue::encode(v); - ++m_size; - } - - void removeLast() - { - ASSERT(m_size); - m_size--; - } - - JSValue last() - { - ASSERT(m_size); - return JSValue::decode(slotFor(m_size - 1)); - } +class SlotVisitor; + +class MarkedArgumentBuffer { + WTF_MAKE_NONCOPYABLE(MarkedArgumentBuffer); + friend class JSGlobalData; + friend class ArgList; + +private: + static const size_t inlineCapacity = 8; + typedef Vector<Register, inlineCapacity> VectorType; + typedef HashSet<MarkedArgumentBuffer*> ListSet; + +public: + // Constructor for a read-write list, to which you may append values. + // FIXME: Remove all clients of this API, then remove this API. + MarkedArgumentBuffer() + : m_size(0) + , m_capacity(inlineCapacity) + , m_buffer(&m_inlineBuffer[m_capacity - 1]) + , m_markSet(0) + { + } + + ~MarkedArgumentBuffer() + { + if (m_markSet) + m_markSet->remove(this); + + if (EncodedJSValue* base = mallocBase()) + delete [] base; + } + + size_t size() const { return m_size; } + bool isEmpty() const { return !m_size; } + + JSValue at(int i) const + { + if (i >= m_size) + return jsUndefined(); + + return JSValue::decode(slotFor(i)); + } + + void clear() + { + m_size = 0; + } + + void append(JSValue v) + { + if (m_size >= m_capacity) + return slowAppend(v); + + slotFor(m_size) = JSValue::encode(v); + ++m_size; + } + + void removeLast() + { + ASSERT(m_size); + m_size--; + } + + JSValue last() + { + ASSERT(m_size); + return JSValue::decode(slotFor(m_size - 1)); + } - static void markLists(HeapRootVisitor&, ListSet&); + static void markLists(HeapRootVisitor&, ListSet&); - private: - JS_EXPORT_PRIVATE void slowAppend(JSValue); +private: + JS_EXPORT_PRIVATE void slowAppend(JSValue); - EncodedJSValue& slotFor(int item) const - { - return m_buffer[-item]; - } + EncodedJSValue& slotFor(int item) const + { + return m_buffer[-item]; + } - EncodedJSValue* mallocBase() - { - if (m_capacity == static_cast<int>(inlineCapacity)) - return 0; - return &slotFor(m_capacity - 1); - } + EncodedJSValue* mallocBase() + { + if (m_capacity == static_cast<int>(inlineCapacity)) + return 0; + return &slotFor(m_capacity - 1); + } - int m_size; - int m_capacity; - EncodedJSValue m_inlineBuffer[inlineCapacity]; - EncodedJSValue* m_buffer; - ListSet* m_markSet; - - private: - // Prohibits new / delete, which would break GC. - void* operator new(size_t size) - { - return fastMalloc(size); - } - void operator delete(void* p) - { - fastFree(p); - } - - void* operator new[](size_t); - void operator delete[](void*); - - void* operator new(size_t, void*); - void operator delete(void*, size_t); - }; - - class ArgList { - friend class JIT; - public: - ArgList() - : m_args(0) - , m_argCount(0) - { - } - - ArgList(ExecState* exec) - : m_args(reinterpret_cast<JSValue*>(&exec[CallFrame::argumentOffset(0)])) - , m_argCount(exec->argumentCount()) - { - } - - ArgList(const MarkedArgumentBuffer& args) - : m_args(reinterpret_cast<JSValue*>(args.m_buffer)) - , m_argCount(args.size()) - { - } - - JSValue at(int i) const - { - if (i >= m_argCount) - return jsUndefined(); - return m_args[-i]; - } - - bool isEmpty() const { return !m_argCount; } - size_t size() const { return m_argCount; } + int m_size; + int m_capacity; + EncodedJSValue m_inlineBuffer[inlineCapacity]; + EncodedJSValue* m_buffer; + ListSet* m_markSet; + +private: + // Prohibits new / delete, which would break GC. + void* operator new(size_t size) + { + return fastMalloc(size); + } + void operator delete(void* p) + { + fastFree(p); + } + + void* operator new[](size_t); + void operator delete[](void*); + + void* operator new(size_t, void*); + void operator delete(void*, size_t); +}; + +class ArgList { + friend class JIT; +public: + ArgList() + : m_args(0) + , m_argCount(0) + { + } + + ArgList(ExecState* exec) + : m_args(reinterpret_cast<JSValue*>(&exec[CallFrame::argumentOffset(0)])) + , m_argCount(exec->argumentCount()) + { + } + + ArgList(const MarkedArgumentBuffer& args) + : m_args(reinterpret_cast<JSValue*>(args.m_buffer)) + , m_argCount(args.size()) + { + } + + JSValue at(int i) const + { + if (i >= m_argCount) + return jsUndefined(); + return m_args[-i]; + } + + bool isEmpty() const { return !m_argCount; } + size_t size() const { return m_argCount; } - JS_EXPORT_PRIVATE void getSlice(int startIndex, ArgList& result) const; + JS_EXPORT_PRIVATE void getSlice(int startIndex, ArgList& result) const; - private: - JSValue* m_args; - int m_argCount; - }; +private: + JSValue* m_args; + int m_argCount; +}; } // namespace JSC 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..cc847d8ff 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" @@ -456,7 +456,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 +618,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 +733,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 +748,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 +820,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 +880,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..2b83d39b7 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*, Butterfly*); - 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, ArrayWithArrayStorage); + } - protected: - void finishCreation(JSGlobalObject*); - }; +protected: + void finishCreation(JSGlobalObject*); +}; } // 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/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..740aaa6df 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 Thingy { + typedef typename HashMap<KeyType, unsigned>::iterator iterator; +public: + Thingy() + : m_randomGenerator((static_cast<uint32_t>(randomNumber() * UINT32_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; + Thingy<CodeBlockKey, Strong<UnlinkedCodeBlock>, kMaxCodeBlockEntries> m_cachedCodeBlocks; + Thingy<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable>, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions; + Thingy<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..98471b85b 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) { 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/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 3b37613d1..df31b8542 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -28,9 +28,9 @@ #include "ConstructData.h" #include "Heap.h" #include "JSLock.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" #include "SlotVisitor.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" #include "TypedArrayDescriptor.h" #include "WriteBarrier.h" #include <wtf/Noncopyable.h> diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index c466a2b04..a7c6c8c18 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()))); @@ -360,7 +367,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 +421,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. @@ -488,9 +497,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..121b71b72 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" @@ -130,9 +131,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; @@ -275,14 +279,26 @@ namespace JSC { 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(); } @@ -497,34 +513,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/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 6a3fb84e4..a67896b1a 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()) @@ -405,6 +429,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 +455,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 +570,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 +600,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 +610,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 +675,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 +720,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 +788,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 +877,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 +1035,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 +1042,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 +1066,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 +1090,7 @@ void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) } default: - ASSERT_NOT_REACHED(); + CRASH(); break; } } @@ -877,8 +1257,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 +1269,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 +1448,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 +1463,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 +1864,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 +1878,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 +1888,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 +2017,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 +2164,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 +2246,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 +2348,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 +2395,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 +2410,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..f9ae73ed4 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -151,30 +151,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 +200,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 +224,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: @@ -243,10 +242,19 @@ public: switch (structure()->indexingType()) { case ALL_BLANK_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 +287,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 +309,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 +325,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 +340,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 +376,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 +426,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 +443,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 +643,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 +689,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 +749,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 +803,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 +845,7 @@ protected: WriteBarrier<Unknown>* currentIndexingData() { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous(); @@ -732,10 +858,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 +901,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 +928,8 @@ private: void isObject(); void isString(); + Butterfly* createInitialIndexedStorage(JSGlobalData&, unsigned length, size_t elementSize); + ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*); template<PutMode> @@ -800,11 +952,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; diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index e7f8cad17..8f245f98d 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -215,8 +215,8 @@ char* JSValue::description() const #endif } else if (isCell()) { snprintf( - description, size, "Cell: %p (%p: %s, %s)", - asCell(), asCell()->structure(), asCell()->structure()->classInfo()->className, + 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"); 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..ed14e895f 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 { 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/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..e930c022a 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -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; + } } } 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)); |