diff options
| author | Simon Hausmann <simon.hausmann@digia.com> | 2012-09-18 15:53:33 +0200 |
|---|---|---|
| committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-09-18 15:53:33 +0200 |
| commit | 6bbb7fbbac94d0f511a7bd0cbd50854ab643bfb2 (patch) | |
| tree | d9c68d1cca0b3e352f1e438561f3e504e641a08f /Source/JavaScriptCore/runtime | |
| parent | d0424a769059c84ae20beb3c217812792ea6726b (diff) | |
| download | qtwebkit-6bbb7fbbac94d0f511a7bd0cbd50854ab643bfb2.tar.gz | |
Imported WebKit commit c7503cef7ecb236730d1309676ab9fc723fd061d (http://svn.webkit.org/repository/webkit/trunk@128886)
New snapshot with various build fixes
Diffstat (limited to 'Source/JavaScriptCore/runtime')
28 files changed, 974 insertions, 625 deletions
diff --git a/Source/JavaScriptCore/runtime/Arguments.cpp b/Source/JavaScriptCore/runtime/Arguments.cpp index 47795edb2..e5e503ee1 100644 --- a/Source/JavaScriptCore/runtime/Arguments.cpp +++ b/Source/JavaScriptCore/runtime/Arguments.cpp @@ -45,10 +45,10 @@ void Arguments::visitChildren(JSCell* cell, SlotVisitor& visitor) ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); JSObject::visitChildren(thisObject, visitor); - if (thisObject->d->registerArray) - visitor.appendValues(thisObject->d->registerArray.get(), thisObject->d->numArguments); - visitor.append(&thisObject->d->callee); - visitor.append(&thisObject->d->activation); + if (thisObject->m_registerArray) + visitor.appendValues(thisObject->m_registerArray.get(), thisObject->m_numArguments); + visitor.append(&thisObject->m_callee); + visitor.append(&thisObject->m_activation); } void Arguments::destroy(JSCell* cell) @@ -58,7 +58,7 @@ void Arguments::destroy(JSCell* cell) void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) { - if (UNLIKELY(d->overrodeLength)) { + if (UNLIKELY(m_overrodeLength)) { length = min(get(exec, exec->propertyNames().length).toUInt32(exec), length); for (unsigned i = 0; i < length; i++) callFrame->setArgument(i, get(exec, i)); @@ -66,8 +66,8 @@ void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t } ASSERT(length == this->length(exec)); for (size_t i = 0; i < length; ++i) { - if (!d->deletedArguments || !d->deletedArguments[i]) - callFrame->setArgument(i, argument(i).get()); + if (JSValue value = tryGetArgument(i)) + callFrame->setArgument(i, value); else callFrame->setArgument(i, get(exec, i)); } @@ -75,7 +75,7 @@ void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) { - if (UNLIKELY(d->overrodeLength)) { + if (UNLIKELY(m_overrodeLength)) { unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec); for (unsigned i = 0; i < length; i++) args.append(get(exec, i)); @@ -83,8 +83,8 @@ void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) } uint32_t length = this->length(exec); for (size_t i = 0; i < length; ++i) { - if (!d->deletedArguments || !d->deletedArguments[i]) - args.append(argument(i).get()); + if (JSValue value = tryGetArgument(i)) + args.append(value); else args.append(get(exec, i)); } @@ -93,8 +93,8 @@ void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) bool Arguments::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) { Arguments* thisObject = jsCast<Arguments*>(cell); - if (i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { - slot.setValue(thisObject->argument(i).get()); + if (JSValue value = thisObject->tryGetArgument(i)) { + slot.setValue(value); return true; } @@ -103,10 +103,10 @@ bool Arguments::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigne void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) { - if (d->overrodeCaller) + if (m_overrodeCaller) return; - d->overrodeCaller = true; + m_overrodeCaller = true; PropertyDescriptor descriptor; descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec), DontEnum | DontDelete | Accessor); methodTable()->defineOwnProperty(this, exec, exec->propertyNames().caller, descriptor, false); @@ -114,10 +114,10 @@ void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec) { - if (d->overrodeCallee) + if (m_overrodeCallee) return; - d->overrodeCallee = true; + m_overrodeCallee = true; PropertyDescriptor descriptor; descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec), DontEnum | DontDelete | Accessor); methodTable()->defineOwnProperty(this, exec, exec->propertyNames().callee, descriptor, false); @@ -127,26 +127,26 @@ bool Arguments::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName p { Arguments* thisObject = jsCast<Arguments*>(cell); unsigned i = propertyName.asIndex(); - if (i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + if (JSValue value = thisObject->tryGetArgument(i)) { ASSERT(i < PropertyName::NotAnIndex); - slot.setValue(thisObject->argument(i).get()); + slot.setValue(value); return true; } - if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->d->overrodeLength)) { - slot.setValue(jsNumber(thisObject->d->numArguments)); + if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) { + slot.setValue(jsNumber(thisObject->m_numArguments)); return true; } - if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->d->overrodeCallee)) { - if (!thisObject->d->isStrictMode) { - slot.setValue(thisObject->d->callee.get()); + if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) { + if (!thisObject->m_isStrictMode) { + slot.setValue(thisObject->m_callee.get()); return true; } thisObject->createStrictModeCalleeIfNecessary(exec); } - if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) thisObject->createStrictModeCallerIfNecessary(exec); return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); @@ -156,26 +156,26 @@ bool Arguments::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prop { Arguments* thisObject = jsCast<Arguments*>(object); unsigned i = propertyName.asIndex(); - if (i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + if (JSValue value = thisObject->tryGetArgument(i)) { ASSERT(i < PropertyName::NotAnIndex); - descriptor.setDescriptor(thisObject->argument(i).get(), None); + descriptor.setDescriptor(value, None); return true; } - if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->d->overrodeLength)) { - descriptor.setDescriptor(jsNumber(thisObject->d->numArguments), DontEnum); + if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) { + descriptor.setDescriptor(jsNumber(thisObject->m_numArguments), DontEnum); return true; } - if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->d->overrodeCallee)) { - if (!thisObject->d->isStrictMode) { - descriptor.setDescriptor(thisObject->d->callee.get(), DontEnum); + if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) { + if (!thisObject->m_isStrictMode) { + descriptor.setDescriptor(thisObject->m_callee.get(), DontEnum); return true; } thisObject->createStrictModeCalleeIfNecessary(exec); } - if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) thisObject->createStrictModeCallerIfNecessary(exec); return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); @@ -184,9 +184,10 @@ bool Arguments::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prop void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { Arguments* thisObject = jsCast<Arguments*>(object); - for (unsigned i = 0; i < thisObject->d->numArguments; ++i) { - if (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) - propertyNames.add(Identifier(exec, String::number(i))); + for (unsigned i = 0; i < thisObject->m_numArguments; ++i) { + if (!thisObject->isArgument(i)) + continue; + propertyNames.add(Identifier(exec, String::number(i))); } if (mode == IncludeDontEnumProperties) { propertyNames.add(exec->propertyNames().callee); @@ -198,10 +199,8 @@ void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyN void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow) { Arguments* thisObject = jsCast<Arguments*>(cell); - if (i < static_cast<unsigned>(thisObject->d->numArguments) && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { - thisObject->argument(i).set(exec->globalData(), thisObject, value); + if (thisObject->trySetArgument(exec->globalData(), i, value)) return; - } PutPropertySlot slot(shouldThrow); JSObject::put(thisObject, exec, Identifier(exec, String::number(i)), value, slot); @@ -211,28 +210,25 @@ void Arguments::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JS { Arguments* thisObject = jsCast<Arguments*>(cell); unsigned i = propertyName.asIndex(); - if (i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { - ASSERT(i < PropertyName::NotAnIndex); - thisObject->argument(i).set(exec->globalData(), thisObject, value); + if (thisObject->trySetArgument(exec->globalData(), i, value)) return; - } - if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { - thisObject->d->overrodeLength = true; + if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { + thisObject->m_overrodeLength = true; thisObject->putDirect(exec->globalData(), propertyName, value, DontEnum); return; } - if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { - if (!thisObject->d->isStrictMode) { - thisObject->d->overrodeCallee = true; + if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { + if (!thisObject->m_isStrictMode) { + thisObject->m_overrodeCallee = true; thisObject->putDirect(exec->globalData(), propertyName, value, DontEnum); return; } thisObject->createStrictModeCalleeIfNecessary(exec); } - if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) thisObject->createStrictModeCallerIfNecessary(exec); JSObject::put(thisObject, exec, propertyName, value, slot); @@ -241,20 +237,12 @@ void Arguments::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JS bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) { Arguments* thisObject = jsCast<Arguments*>(cell); - if (i < thisObject->d->numArguments) { + if (i < thisObject->m_numArguments) { if (!Base::deletePropertyByIndex(cell, exec, i)) return false; - - if (!thisObject->d->deletedArguments) { - thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); - memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); - } - if (!thisObject->d->deletedArguments[i]) { - thisObject->d->deletedArguments[i] = true; + if (thisObject->tryDeleteArgument(i)) return true; - } } - return JSObject::deletePropertyByIndex(thisObject, exec, i); } @@ -265,35 +253,28 @@ bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName prope Arguments* thisObject = jsCast<Arguments*>(cell); unsigned i = propertyName.asIndex(); - if (i < thisObject->d->numArguments) { + if (i < thisObject->m_numArguments) { ASSERT(i < PropertyName::NotAnIndex); if (!Base::deleteProperty(cell, exec, propertyName)) return false; - - if (!thisObject->d->deletedArguments) { - thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); - memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); - } - if (!thisObject->d->deletedArguments[i]) { - thisObject->d->deletedArguments[i] = true; + if (thisObject->tryDeleteArgument(i)) return true; - } } - if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { - thisObject->d->overrodeLength = true; + if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { + thisObject->m_overrodeLength = true; return true; } - if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { - if (!thisObject->d->isStrictMode) { - thisObject->d->overrodeCallee = true; + if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { + if (!thisObject->m_isStrictMode) { + thisObject->m_overrodeCallee = true; return true; } thisObject->createStrictModeCalleeIfNecessary(exec); } - if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) thisObject->createStrictModeCallerIfNecessary(exec); return JSObject::deleteProperty(thisObject, exec, propertyName); @@ -303,47 +284,46 @@ bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNam { Arguments* thisObject = jsCast<Arguments*>(object); unsigned i = propertyName.asIndex(); - if (i < thisObject->d->numArguments) { + if (i < thisObject->m_numArguments) { ASSERT(i < PropertyName::NotAnIndex); // If the property is not yet present on the object, and is not yet marked as deleted, then add it now. PropertySlot slot; - if ((!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) - object->putDirectMayBeIndex(exec, propertyName, thisObject->argument(i).get()); + if (!thisObject->isDeletedArgument(i) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) { + JSValue value = thisObject->tryGetArgument(i); + ASSERT(value); + object->putDirectMayBeIndex(exec, propertyName, value); + } if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow)) return false; - if (!thisObject->d->deletedArguments) { - thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); - memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); - } // From ES 5.1, 10.6 Arguments Object // 5. If the value of isMapped is not undefined, then - if (!thisObject->d->deletedArguments[i]) { + if (thisObject->isArgument(i)) { // a. If IsAccessorDescriptor(Desc) is true, then if (descriptor.isAccessorDescriptor()) { // i. Call the [[Delete]] internal method of map passing P, and false as the arguments. - thisObject->d->deletedArguments[i] = true; + thisObject->tryDeleteArgument(i); } else { // b. Else // i. If Desc.[[Value]] is present, then // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments. if (descriptor.value()) - thisObject->argument(i).set(exec->globalData(), thisObject, descriptor.value()); + thisObject->trySetArgument(exec->globalData(), i, descriptor.value()); // ii. If Desc.[[Writable]] is present and its value is false, then // 1. Call the [[Delete]] internal method of map passing P and false as arguments. if (descriptor.writablePresent() && !descriptor.writable()) - thisObject->d->deletedArguments[i] = true; + thisObject->tryDeleteArgument(i); } } return true; } - if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { - thisObject->putDirect(exec->globalData(), propertyName, jsNumber(thisObject->d->numArguments), DontEnum); - thisObject->d->overrodeLength = true; - } else if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { - thisObject->putDirect(exec->globalData(), propertyName, thisObject->d->callee.get(), DontEnum); - thisObject->d->overrodeCallee = true; - } else if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { + thisObject->putDirect(exec->globalData(), propertyName, jsNumber(thisObject->m_numArguments), DontEnum); + thisObject->m_overrodeLength = true; + } else if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { + thisObject->putDirect(exec->globalData(), propertyName, thisObject->m_callee.get(), DontEnum); + thisObject->m_overrodeCallee = true; + } else if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) thisObject->createStrictModeCallerIfNecessary(exec); return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); @@ -354,18 +334,18 @@ void Arguments::tearOff(CallFrame* callFrame) if (isTornOff()) return; - if (!d->numArguments) + if (!m_numArguments) return; // Must be called for the same call frame from which it was created. - ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == d->registers); + ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == m_registers); - d->registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[d->numArguments]); - d->registers = d->registerArray.get() + CallFrame::offsetFor(d->numArguments + 1); + m_registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[m_numArguments]); + m_registers = m_registerArray.get() + CallFrame::offsetFor(m_numArguments + 1); if (!callFrame->isInlineCallFrame()) { - for (size_t i = 0; i < d->numArguments; ++i) - argument(i).set(callFrame->globalData(), this, callFrame->argument(i)); + for (size_t i = 0; i < m_numArguments; ++i) + trySetArgument(callFrame->globalData(), i, callFrame->argumentAfterCapture(i)); return; } @@ -373,16 +353,41 @@ void Arguments::tearOff(CallFrame* callFrame) callFrame->globalData(), callFrame->registers(), callFrame->inlineCallFrame()); } +void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation) +{ + ASSERT(activation); + if (isTornOff()) + return; + + if (!m_numArguments) + return; + + tearOff(exec); + + SharedSymbolTable* symbolTable = activation->symbolTable(); + const SlowArgument* slowArguments = symbolTable->slowArguments(); + if (!slowArguments) + return; + + ASSERT(symbolTable->captureMode() == SharedSymbolTable::AllOfTheThings); + m_activation.set(exec->globalData(), this, activation); + + allocateSlowArguments(); + size_t count = min<unsigned>(m_numArguments, symbolTable->parameterCount()); + for (size_t i = 0; i < count; ++i) + m_slowArguments[i] = slowArguments[i]; +} + void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) { if (isTornOff()) return; - if (!d->numArguments) + if (!m_numArguments) return; - d->registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[d->numArguments]); - d->registers = d->registerArray.get() + CallFrame::offsetFor(d->numArguments + 1); + m_registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[m_numArguments]); + m_registers = m_registerArray.get() + CallFrame::offsetFor(m_numArguments + 1); tearOffForInlineCallFrame( callFrame->globalData(), callFrame->registers() + inlineCallFrame->stackOffset, @@ -391,7 +396,7 @@ void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) void Arguments::tearOffForInlineCallFrame(JSGlobalData& globalData, Register* registers, InlineCallFrame* inlineCallFrame) { - for (size_t i = 0; i < d->numArguments; ++i) { + for (size_t i = 0; i < m_numArguments; ++i) { ValueRecovery& recovery = inlineCallFrame->arguments[i + 1]; // In the future we'll support displaced recoveries (indicating that the // argument was flushed to a different location), but for now we don't do @@ -427,7 +432,7 @@ void Arguments::tearOffForInlineCallFrame(JSGlobalData& globalData, Register* re ASSERT_NOT_REACHED(); break; } - argument(i).set(globalData, this, value); + trySetArgument(globalData, i, value); } } diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index c3d25f962..ad0e651ea 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -33,31 +33,9 @@ namespace JSC { - struct ArgumentsData { - WTF_MAKE_NONCOPYABLE(ArgumentsData); WTF_MAKE_FAST_ALLOCATED; - public: - ArgumentsData() { } - WriteBarrier<JSActivation> activation; - - unsigned 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 overrodeLength; - bool overrodeCallee; - bool overrodeCaller; - bool isStrictMode; - - WriteBarrierBase<Unknown>* registers; - OwnArrayPtr<WriteBarrier<Unknown> > registerArray; - - OwnArrayPtr<bool> deletedArguments; - - WriteBarrier<JSFunction> callee; - }; - class Arguments : public JSNonFinalObject { + friend class JIT; + friend class DFG::SpeculativeJIT; public: typedef JSNonFinalObject Base; @@ -94,30 +72,22 @@ namespace JSC { uint32_t length(ExecState* exec) const { - if (UNLIKELY(d->overrodeLength)) + if (UNLIKELY(m_overrodeLength)) return get(exec, exec->propertyNames().length).toUInt32(exec); - return d->numArguments; + return m_numArguments; } void copyToArguments(ExecState*, CallFrame*, uint32_t length); void tearOff(CallFrame*); void tearOff(CallFrame*, InlineCallFrame*); - bool isTornOff() const { return d->registerArray; } - void didTearOffActivation(JSGlobalData& globalData, JSActivation* activation) - { - if (isTornOff()) - return; - d->activation.set(globalData, this, activation); - d->registers = &activation->registerAt(0); - } + 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); } - static ptrdiff_t offsetOfData() { return OBJECT_OFFSETOF(Arguments, d); } - protected: static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; @@ -138,11 +108,34 @@ namespace JSC { 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*); - OwnPtr<ArgumentsData> d; + 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); @@ -155,19 +148,76 @@ namespace JSC { inline Arguments::Arguments(CallFrame* callFrame) : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) - , d(adoptPtr(new ArgumentsData)) { } inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) - , d(adoptPtr(new ArgumentsData)) { } - inline WriteBarrierBase<Unknown>& Arguments::argument(size_t i) + inline void Arguments::allocateSlowArguments() + { + if (m_slowArguments) + return; + m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); + } + + inline bool Arguments::tryDeleteArgument(size_t argument) { - return d->registers[CallFrame::argumentOffset(i)]; + 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 || m_slowArguments[argument].status == SlowArgument::Normal) + return m_registers[CallFrame::argumentOffset(argument)]; + + ASSERT(m_slowArguments[argument].status == SlowArgument::Captured); + if (!m_activation) + return m_registers[m_slowArguments[argument].indexIfCaptured]; + + return m_activation->registerAt(m_slowArguments[argument].indexIfCaptured); } inline void Arguments::finishCreation(CallFrame* callFrame) @@ -176,17 +226,17 @@ namespace JSC { ASSERT(inherits(&s_info)); JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); - d->numArguments = callFrame->argumentCount(); - d->registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); - d->callee.set(callFrame->globalData(), this, callee); - d->overrodeLength = false; - d->overrodeCallee = false; - d->overrodeCaller = false; - d->isStrictMode = callFrame->codeBlock()->isStrictMode(); + 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(); // The bytecode generator omits op_tear_off_activation in cases of no // declared parameters, so we need to tear off immediately. - if (d->isStrictMode || !callee->jsExecutable()->parameterCount()) + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) tearOff(callFrame); } @@ -196,17 +246,17 @@ namespace JSC { ASSERT(inherits(&s_info)); JSFunction* callee = inlineCallFrame->callee.get(); - d->numArguments = inlineCallFrame->arguments.size() - 1; - d->registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset; - d->callee.set(callFrame->globalData(), this, callee); - d->overrodeLength = false; - d->overrodeCallee = false; - d->overrodeCaller = false; - d->isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); + 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(); // The bytecode generator omits op_tear_off_activation in cases of no // declared parameters, so we need to tear off immediately. - if (d->isStrictMode || !callee->jsExecutable()->parameterCount()) + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) tearOff(callFrame, inlineCallFrame); } diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index 503aecda8..c70e40d77 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -132,8 +132,10 @@ ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structur void ArrayPrototype::finishCreation(JSGlobalObject* globalObject) { - Base::finishCreation(globalObject->globalData()); + JSGlobalData& globalData = globalObject->globalData(); + Base::finishCreation(globalData); ASSERT(inherits(&s_info)); + notifyUsedAsPrototype(globalData); } bool ArrayPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) @@ -638,7 +640,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) CallData callData; CallType callType = getCallData(function, callData); - if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseIndexingMode()) { + if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseIndexingMode() && !shouldUseSlowPut(thisObj->structure()->indexingType())) { if (isNumericCompareFunction(exec, callType, callData)) asArray(thisObj)->sortNumeric(exec, function, callType, callData); else if (callType != CallTypeNone) diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp index b11220bea..2791c65d4 100644 --- a/Source/JavaScriptCore/runtime/Executable.cpp +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -202,7 +202,7 @@ JSObject* EvalExecutable::compileInternal(ExecState* exec, JSScope* scope, JITCo m_evalCodeBlock = newCodeBlock.release(); } else { if (!lexicalGlobalObject->evalEnabled()) - return throwError(exec, createEvalError(exec, ASCIILiteral("Eval is disabled"))); + return throwError(exec, createEvalError(exec, lexicalGlobalObject->evalDisabledErrorMessage())); RefPtr<EvalNode> evalNode = parse<EvalNode>(globalData, lexicalGlobalObject, m_source, 0, Identifier(), isStrictMode() ? JSParseStrict : JSParseNormal, EvalNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception); if (!evalNode) { ASSERT(exception); diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp index 40507dae1..570444e3c 100644 --- a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -82,7 +82,7 @@ CallType FunctionConstructor::getCallData(JSCell*, CallData& callData) JSObject* constructFunction(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, const Identifier& functionName, const String& sourceURL, const TextPosition& position) { if (!globalObject->evalEnabled()) - return throwError(exec, createEvalError(exec, ASCIILiteral("Function constructor is disabled"))); + return throwError(exec, createEvalError(exec, globalObject->evalDisabledErrorMessage())); return constructFunctionSkippingEvalEnabledCheck(exec, globalObject, args, functionName, sourceURL, position); } diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h index cd8d71dfe..3b97230ea 100644 --- a/Source/JavaScriptCore/runtime/IndexingType.h +++ b/Source/JavaScriptCore/runtime/IndexingType.h @@ -33,28 +33,65 @@ typedef uint8_t IndexingType; // Flags for testing the presence of capabilities. static const IndexingType IsArray = 1; static const IndexingType HasArrayStorage = 8; +static const IndexingType HasSlowPutArrayStorage = 16; // Additional flags for tracking the history of the type. These are usually // masked off unless you ask for them directly. -static const IndexingType HadArrayStorage = 16; // Means that this object did have array storage in the past. +static const IndexingType HadArrayStorage = 32; // Means that this object did have array storage in the past. +static const IndexingType MayHaveIndexedAccessors = 64; // List of acceptable array types. -static const IndexingType NonArray = 0; -static const IndexingType NonArrayWithArrayStorage = HasArrayStorage; -static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution. -static const IndexingType ArrayWithArrayStorage = IsArray | HasArrayStorage; +static const IndexingType NonArray = 0; +static const IndexingType NonArrayWithArrayStorage = HasArrayStorage; +static const IndexingType NonArrayWithSlowPutArrayStorage = HasSlowPutArrayStorage; +static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution. +static const IndexingType ArrayWithArrayStorage = IsArray | HasArrayStorage; +static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | HasSlowPutArrayStorage; -// Mask of all possible types. -static const IndexingType AllArrayTypes = 15; +#define ALL_BLANK_INDEXING_TYPES \ + NonArray: \ + case ArrayClass -// Mask of all possible types including the history. -static const IndexingType AllArrayTypesAndHistory = 31; +#define ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES \ + ArrayWithArrayStorage: \ + case ArrayWithSlowPutArrayStorage + +#define ALL_ARRAY_STORAGE_INDEXING_TYPES \ + NonArrayWithArrayStorage: \ + case NonArrayWithSlowPutArrayStorage: \ + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES + +static inline bool hasIndexedProperties(IndexingType indexingType) +{ + switch (indexingType) { + case ALL_BLANK_INDEXING_TYPES: + return false; + default: + return true; + } +} + +static inline bool hasIndexingHeader(IndexingType type) +{ + return hasIndexedProperties(type); +} -inline bool hasIndexingHeader(IndexingType type) +static inline bool hasArrayStorage(IndexingType indexingType) { - return !!(type & HasArrayStorage); + return !!(indexingType & (HasArrayStorage | HasSlowPutArrayStorage)); } +static inline bool shouldUseSlowPut(IndexingType indexingType) +{ + return !!(indexingType & HasSlowPutArrayStorage); +} + +// Mask of all possible types. +static const IndexingType AllArrayTypes = 31; + +// Mask of all possible types including the history. +static const IndexingType AllArrayTypesAndHistory = 127; + } // namespace JSC #endif // IndexingType_h diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp index 6e7eddbf1..1a7239f60 100644 --- a/Source/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -35,6 +35,7 @@ #include "Identifier.h" #include "JSDateMath.h" #include "JSGlobalObject.h" +#include "JSLock.h" #include "LLIntData.h" #include "WriteBarrier.h" #include <wtf/dtoa.h> @@ -53,6 +54,7 @@ static void initializeThreadingOnce() { WTF::double_conversion::initialize(); WTF::initializeThreading(); + GlobalJSLock::initialize(); Options::initialize(); #if ENABLE(WRITE_BARRIER_PROFILING) WriteBarrierCounters::initialize(); diff --git a/Source/JavaScriptCore/runtime/JSActivation.h b/Source/JavaScriptCore/runtime/JSActivation.h index df59c3d94..8211e7710 100644 --- a/Source/JavaScriptCore/runtime/JSActivation.h +++ b/Source/JavaScriptCore/runtime/JSActivation.h @@ -48,7 +48,7 @@ namespace JSC { static JSActivation* create(JSGlobalData& globalData, CallFrame* callFrame, FunctionExecutable* functionExecutable) { - size_t storageSize = JSActivation::storageSize(callFrame, functionExecutable->symbolTable()); + size_t storageSize = JSActivation::storageSize(functionExecutable->symbolTable()); JSActivation* activation = new ( NotNull, allocateCell<JSActivation>( @@ -98,8 +98,8 @@ namespace JSC { NEVER_INLINE PropertySlot::GetValueFunc getArgumentsGetter(); static size_t allocationSize(size_t storageSize); - static size_t storageSize(CallFrame*, SharedSymbolTable*); - static int captureStart(CallFrame*, SharedSymbolTable*); + static size_t storageSize(SharedSymbolTable*); + static int captureStart(SharedSymbolTable*); int registerOffset(); size_t storageSize(); @@ -142,26 +142,26 @@ namespace JSC { return false; } - inline int JSActivation::captureStart(CallFrame* callFrame, SharedSymbolTable* symbolTable) + inline int JSActivation::captureStart(SharedSymbolTable* symbolTable) { if (symbolTable->captureMode() == SharedSymbolTable::AllOfTheThings) - return -CallFrame::offsetFor(std::max<size_t>(callFrame->argumentCountIncludingThis(), symbolTable->parameterCountIncludingThis())); + return -CallFrame::offsetFor(symbolTable->parameterCountIncludingThis()); return symbolTable->captureStart(); } - inline size_t JSActivation::storageSize(CallFrame* callFrame, SharedSymbolTable* symbolTable) + inline size_t JSActivation::storageSize(SharedSymbolTable* symbolTable) { - return symbolTable->captureEnd() - captureStart(callFrame, symbolTable); + return symbolTable->captureEnd() - captureStart(symbolTable); } inline int JSActivation::registerOffset() { - return -captureStart(CallFrame::create(reinterpret_cast<Register*>(m_registers)), symbolTable()); + return -captureStart(symbolTable()); } inline size_t JSActivation::storageSize() { - return storageSize(CallFrame::create(reinterpret_cast<Register*>(m_registers)), symbolTable()); + return storageSize(symbolTable()); } inline void JSActivation::tearOff(JSGlobalData& globalData) @@ -216,7 +216,7 @@ namespace JSC { inline bool JSActivation::isValid(const SymbolTableEntry& entry) { - if (entry.getIndex() < captureStart(CallFrame::create(reinterpret_cast<Register*>(m_registers)), symbolTable())) + if (entry.getIndex() < captureStart(symbolTable())) return false; if (entry.getIndex() >= symbolTable()->captureEnd()) return false; diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index 241049dce..ebbbd41aa 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -34,7 +34,6 @@ #include "IndexingHeaderInlineMethods.h" #include "PropertyNameArray.h" #include "Reject.h" -#include "SparseArrayValueMapInlineMethods.h" #include <wtf/AVLTree.h> #include <wtf/Assertions.h> #include <wtf/OwnPtr.h> @@ -405,7 +404,7 @@ JSValue JSArray::pop(ExecState* exec) case ArrayClass: return jsUndefined(); - case ArrayWithArrayStorage: { + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); unsigned length = storage->length(); @@ -465,6 +464,16 @@ void JSArray::push(ExecState* exec, JSValue value) break; } + case ArrayWithSlowPutArrayStorage: { + unsigned oldLength = length(); + if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) { + if (!exec->hadException() && oldLength < 0xFFFFFFFFu) + setLength(exec, oldLength + 1, true); + return; + } + // Fall through. + } + case ArrayWithArrayStorage: { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -478,8 +487,8 @@ void JSArray::push(ExecState* exec, JSValue value) return; } - // Pushing to an array of length 2^32-1 stores the property, but throws a range error. - if (UNLIKELY(storage->length() == 0xFFFFFFFFu)) { + // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error. + if (storage->length() > MAX_ARRAY_INDEX) { methodTable()->putByIndex(this, exec, storage->length(), value, true); // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. if (!exec->hadException()) @@ -549,7 +558,9 @@ bool JSArray::unshiftCount(ExecState* exec, unsigned count) storage = m_butterfly->arrayStorage(); storage->m_indexBias -= count; storage->setVectorLength(storage->vectorLength() + count); - } else if (!unshiftCountSlowCase(exec->globalData(), count)) { + } else if (unshiftCountSlowCase(exec->globalData(), count)) + storage = arrayStorage(); + else { throwOutOfMemoryError(exec); return true; } @@ -916,7 +927,7 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) case ArrayClass: return; - case ArrayWithArrayStorage: { + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); WriteBarrier<Unknown>* vector = storage->m_vector; @@ -946,7 +957,7 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le case ArrayClass: return; - case ArrayWithArrayStorage: { + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); unsigned i = 0; WriteBarrier<Unknown>* vector = storage->m_vector; diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index d382f64a9..c46e67863 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -78,9 +78,9 @@ namespace JSC { void fillArgList(ExecState*, MarkedArgumentBuffer&); void copyToArguments(ExecState*, CallFrame*, uint32_t length); - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType) { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage); + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, indexingType); } protected: diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index e409c8219..26f2b8616 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -55,7 +55,6 @@ #include "ParserArena.h" #include "RegExpCache.h" #include "RegExpObject.h" -#include "SparseArrayValueMapInlineMethods.h" #include "StrictEvalActivation.h" #include "StrongInlines.h" #include <wtf/RetainPtr.h> diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index 8ee8e1498..a6993aabc 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -111,6 +111,7 @@ static const int preferredScriptCheckTimeInterval = 1000; JSGlobalObject::JSGlobalObject(JSGlobalData& globalData, Structure* structure, const GlobalObjectMethodTable* globalObjectMethodTable) : Base(globalData, structure, 0) , m_masqueradesAsUndefinedWatchpoint(adoptRef(new WatchpointSet(InitializedWatching))) + , m_havingABadTimeWatchpoint(adoptRef(new WatchpointSet(InitializedWatching))) , m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0))) , m_evalEnabled(true) , m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable) @@ -230,7 +231,8 @@ void JSGlobalObject::reset(JSValue prototype) m_callbackObjectStructure.set(exec->globalData(), this, JSCallbackObject<JSNonFinalObject>::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())); + m_arrayStructure.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_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()))); @@ -329,6 +331,96 @@ void JSGlobalObject::reset(JSValue prototype) resetPrototype(exec->globalData(), prototype); } +// Private namespace for helpers for JSGlobalObject::haveABadTime() +namespace { + +class ObjectsWithBrokenIndexingFinder : public MarkedBlock::VoidFunctor { +public: + ObjectsWithBrokenIndexingFinder(MarkedArgumentBuffer&, JSGlobalObject*); + void operator()(JSCell*); + +private: + MarkedArgumentBuffer& m_foundObjects; + JSGlobalObject* m_globalObject; +}; + +ObjectsWithBrokenIndexingFinder::ObjectsWithBrokenIndexingFinder( + MarkedArgumentBuffer& foundObjects, JSGlobalObject* globalObject) + : m_foundObjects(foundObjects) + , m_globalObject(globalObject) +{ +} + +inline bool hasBrokenIndexing(JSObject* object) +{ + // This will change if we have more indexing types. + return !!(object->structure()->indexingType() & HasArrayStorage); +} + +void ObjectsWithBrokenIndexingFinder::operator()(JSCell* cell) +{ + if (!cell->isObject()) + return; + + JSObject* object = asObject(cell); + + // Run this filter first, since it's cheap, and ought to filter out a lot of objects. + if (!hasBrokenIndexing(object)) + return; + + // We only want to have a bad time in the affected global object, not in the entire + // VM. But we have to be careful, since there may be objects that claim to belong to + // a different global object that have prototypes from our global object. + bool foundGlobalObject = false; + for (JSObject* current = object; ;) { + if (current->unwrappedGlobalObject() == m_globalObject) { + foundGlobalObject = true; + break; + } + + JSValue prototypeValue = current->prototype(); + if (prototypeValue.isNull()) + break; + current = asObject(prototypeValue); + } + if (!foundGlobalObject) + return; + + m_foundObjects.append(object); +} + +} // end private namespace for helpers for JSGlobalObject::haveABadTime() + +void JSGlobalObject::haveABadTime(JSGlobalData& globalData) +{ + ASSERT(&globalData == &this->globalData()); + + if (isHavingABadTime()) + return; + + // Make sure that all allocations or indexed storage transitions that are inlining + // the assumption that it's safe to transition to a non-SlowPut array storage don't + // do so anymore. + m_havingABadTimeWatchpoint->notifyWrite(); + ASSERT(isHavingABadTime()); // The watchpoint is what tells us that we're having a bad time. + + // 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()); + + // Make sure that all objects that have indexed storage switch to the slow kind of + // indexed storage. + MarkedArgumentBuffer foundObjects; // Use MarkedArgumentBuffer because switchToSlowPutArrayStorage() may GC. + ObjectsWithBrokenIndexingFinder finder(foundObjects, this); + globalData.heap.objectSpace().forEachLiveCell(finder); + while (!foundObjects.isEmpty()) { + JSObject* object = asObject(foundObjects.last()); + foundObjects.removeLast(); + ASSERT(hasBrokenIndexing(object)); + object->switchToSlowPutArrayStorage(globalData); + } +} + void JSGlobalObject::createThrowTypeError(ExecState* exec) { JSFunction* thrower = JSFunction::create(exec, this, 0, String(), globalFuncThrowTypeError); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index 406a65b51..ad56783cc 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -126,7 +126,8 @@ namespace JSC { WriteBarrier<Structure> m_activationStructure; WriteBarrier<Structure> m_nameScopeStructure; WriteBarrier<Structure> m_argumentsStructure; - WriteBarrier<Structure> m_arrayStructure; + WriteBarrier<Structure> m_arrayStructure; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. + WriteBarrier<Structure> m_arrayStructureForSlowPut; WriteBarrier<Structure> m_booleanObjectStructure; WriteBarrier<Structure> m_callbackConstructorStructure; WriteBarrier<Structure> m_callbackFunctionStructure; @@ -149,12 +150,14 @@ namespace JSC { Debugger* m_debugger; RefPtr<WatchpointSet> m_masqueradesAsUndefinedWatchpoint; + RefPtr<WatchpointSet> m_havingABadTimeWatchpoint; OwnPtr<JSGlobalObjectRareData> m_rareData; WeakRandom m_weakRandom; bool m_evalEnabled; + String m_evalDisabledErrorMessage; bool m_experimentsEnabled; static JS_EXPORTDATA const GlobalObjectMethodTable s_globalObjectMethodTable; @@ -259,6 +262,7 @@ namespace JSC { Structure* nameScopeStructure() const { return m_nameScopeStructure.get(); } Structure* argumentsStructure() const { return m_argumentsStructure.get(); } Structure* arrayStructure() const { return m_arrayStructure.get(); } + void* addressOfArrayStructure() { return &m_arrayStructure; } Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); } Structure* callbackConstructorStructure() const { return m_callbackConstructorStructure.get(); } Structure* callbackFunctionStructure() const { return m_callbackFunctionStructure.get(); } @@ -279,6 +283,14 @@ namespace JSC { Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); } WatchpointSet* masqueradesAsUndefinedWatchpoint() { return m_masqueradesAsUndefinedWatchpoint.get(); } + WatchpointSet* havingABadTimeWatchpoint() { return m_havingABadTimeWatchpoint.get(); } + + bool isHavingABadTime() const + { + return m_havingABadTimeWatchpoint->hasBeenInvalidated(); + } + + void haveABadTime(JSGlobalData&); void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; } unsigned profileGroup() const @@ -304,8 +316,13 @@ namespace JSC { bool isDynamicScope(bool& requiresDynamicChecks) const; - void setEvalEnabled(bool enabled) { m_evalEnabled = enabled; } - bool evalEnabled() { return m_evalEnabled; } + bool evalEnabled() const { return m_evalEnabled; } + const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; } + void setEvalEnabled(bool enabled, const String& errorMessage = String()) + { + m_evalEnabled = enabled; + m_evalDisabledErrorMessage = errorMessage; + } void resetPrototype(JSGlobalData&, JSValue prototype); diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp index c57c9cdc5..9f02b69b8 100644 --- a/Source/JavaScriptCore/runtime/JSLock.cpp +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -33,21 +33,21 @@ namespace JSC { -// JSLock is only needed to support an obsolete execution model where JavaScriptCore -// automatically protected against concurrent access from multiple threads. -// So it's safe to disable it on non-mac platforms where we don't have native pthreads. -#if (OS(DARWIN) || USE(PTHREADS)) - -static pthread_mutex_t sharedInstanceLock = PTHREAD_MUTEX_INITIALIZER; +Mutex* GlobalJSLock::s_sharedInstanceLock = 0; GlobalJSLock::GlobalJSLock() { - pthread_mutex_lock(&sharedInstanceLock); + s_sharedInstanceLock->lock(); } GlobalJSLock::~GlobalJSLock() { - pthread_mutex_unlock(&sharedInstanceLock); + s_sharedInstanceLock->unlock(); +} + +void GlobalJSLock::initialize() +{ + s_sharedInstanceLock = new Mutex(); } JSLockHolder::JSLockHolder(ExecState* exec) @@ -216,95 +216,4 @@ JSLock::DropAllLocks::~DropAllLocks() m_globalData->apiLock().grabAllLocks(m_lockCount); } -#else // (OS(DARWIN) || USE(PTHREADS)) - -GlobalJSLock::GlobalJSLock() -{ -} - -GlobalJSLock::~GlobalJSLock() -{ -} - -JSLockHolder::JSLockHolder(JSGlobalData*) -{ -} - -JSLockHolder::JSLockHolder(JSGlobalData&) -{ -} - -JSLockHolder::JSLockHolder(ExecState*) -{ -} - -JSLockHolder::~JSLockHolder() -{ -} - -JSLock::JSLock() -{ -} - -JSLock::~JSLock() -{ -} - -bool JSLock::currentThreadIsHoldingLock() -{ - return true; -} - -void JSLock::lock() -{ -} - -void JSLock::unlock() -{ -} - -void JSLock::lock(ExecState*) -{ -} - -void JSLock::unlock(ExecState*) -{ -} - -void JSLock::lock(JSGlobalData&) -{ -} - -void JSLock::unlock(JSGlobalData&) -{ -} - -unsigned JSLock::dropAllLocks() -{ - return 0; -} - -unsigned JSLock::dropAllLocksUnconditionally() -{ - return 0; -} - -void JSLock::grabAllLocks(unsigned) -{ -} - -JSLock::DropAllLocks::DropAllLocks(ExecState*) -{ -} - -JSLock::DropAllLocks::DropAllLocks(JSGlobalData*) -{ -} - -JSLock::DropAllLocks::~DropAllLocks() -{ -} - -#endif // (OS(DARWIN) || USE(PTHREADS)) - } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSLock.h b/Source/JavaScriptCore/runtime/JSLock.h index 94108d013..4c42dc0ae 100644 --- a/Source/JavaScriptCore/runtime/JSLock.h +++ b/Source/JavaScriptCore/runtime/JSLock.h @@ -58,6 +58,10 @@ namespace JSC { public: JS_EXPORT_PRIVATE GlobalJSLock(); JS_EXPORT_PRIVATE ~GlobalJSLock(); + + static void initialize(); + private: + static Mutex* s_sharedInstanceLock; }; class JSLockHolder { @@ -91,12 +95,6 @@ namespace JSC { unsigned dropAllLocksUnconditionally(); void grabAllLocks(unsigned lockCount); - SpinLock m_spinLock; - Mutex m_lock; - ThreadIdentifier m_ownerThread; - intptr_t m_lockCount; - unsigned m_lockDropDepth; - class DropAllLocks { WTF_MAKE_NONCOPYABLE(DropAllLocks); public: @@ -108,6 +106,13 @@ namespace JSC { intptr_t m_lockCount; RefPtr<JSGlobalData> m_globalData; }; + + private: + SpinLock m_spinLock; + Mutex m_lock; + ThreadIdentifier m_ownerThread; + intptr_t m_lockCount; + unsigned m_lockDropDepth; }; } // namespace diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 6eac0d1cb..229d1aea6 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -42,7 +42,6 @@ #include "PropertyNameArray.h" #include "Reject.h" #include "SlotVisitorInlineMethods.h" -#include "SparseArrayValueMapInlineMethods.h" #include <math.h> #include <wtf/Assertions.h> @@ -50,7 +49,8 @@ namespace JSC { // We keep track of the size of the last array after it was grown. We use this // as a simple heuristic for as the value to grow the next array from size 0. -// This value is capped by the constant FIRST_VECTOR_GROW defined above. +// This value is capped by the constant FIRST_VECTOR_GROW defined in +// ArrayConventions.h. static unsigned lastArraySize = 0; JSCell* getCallableObjectSlow(JSCell* cell) @@ -133,8 +133,7 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but // Mark and copy the array if appropriate. switch (structure->indexingType()) { - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage()); WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector; WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector; @@ -160,8 +159,7 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but // Mark the array if appropriate. switch (structure->indexingType()) { - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); if (butterfly->arrayStorage()->m_sparseMap) visitor.append(&butterfly->arrayStorage()->m_sparseMap); @@ -234,12 +232,10 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); switch (thisObject->structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: break; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i >= storage->length()) return false; @@ -301,7 +297,7 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV unsigned attributes; JSCell* specificValue; PropertyOffset offset = obj->structure()->get(globalData, propertyName, attributes, specificValue); - if (offset != invalidOffset) { + if (isValidOffset(offset)) { if (attributes & ReadOnly) { if (slot.isStrictMode()) throwError(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); @@ -347,15 +343,14 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSObject* thisObject = jsCast<JSObject*>(cell); thisObject->checkIndexingConsistency(); - if (UNLIKELY(propertyName > MAX_ARRAY_INDEX)) { + if (propertyName > MAX_ARRAY_INDEX) { PutPropertySlot slot(shouldThrow); thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); return; } switch (thisObject->structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: break; case NonArrayWithArrayStorage: @@ -381,6 +376,34 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, return; } + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: { + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); + + if (propertyName >= storage->vectorLength()) + break; + + WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName]; + unsigned length = storage->length(); + + // Update length & m_numValuesInVector as necessary. + if (propertyName >= length) { + if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow)) + return; + length = propertyName + 1; + storage->setLength(length); + ++storage->m_numValuesInVector; + } else if (!valueSlot) { + if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow)) + return; + ++storage->m_numValuesInVector; + } + + valueSlot.set(exec->globalData(), thisObject, value); + thisObject->checkIndexingConsistency(); + return; + } + default: ASSERT_NOT_REACHED(); } @@ -425,8 +448,7 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); break; @@ -435,10 +457,23 @@ void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) } } +void JSObject::notifyPresenceOfIndexedAccessors(JSGlobalData& globalData) +{ + if (mayInterceptIndexedAccesses()) + return; + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AddIndexedAccessors)); + + if (!mayBeUsedAsPrototype(globalData)) + return; + + globalObject()->haveABadTime(globalData); +} + ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength) { IndexingType oldType = structure()->indexingType(); - ASSERT_UNUSED(oldType, oldType == NonArray || oldType == ArrayClass); + ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); Butterfly* newButterfly = m_butterfly->growArrayRight( globalData, structure(), structure()->outOfLineCapacity(), false, 0, ArrayStorage::sizeFor(vectorLength)); @@ -454,7 +489,7 @@ ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned le result->m_initializationIndex = 0; result->m_inCompactInitialization = 0; #endif - Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateArrayStorage); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), structure()->suggestedIndexingTransition()); setButterfly(globalData, newButterfly, newStructure); return result; } @@ -467,12 +502,10 @@ ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); - case ArrayClass: - case NonArray: { + case ALL_BLANK_INDEXING_TYPES: { createArrayStorage(globalData, 0, 0); SparseArrayValueMap* map = allocateSparseIndexMap(globalData); map->setSparseMode(); @@ -485,6 +518,22 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J } } +void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case NonArrayWithArrayStorage: + case ArrayWithArrayStorage: { + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), SwitchToSlowPutArrayStorage); + setStructure(globalData, newStructure); + break; + } + + default: + ASSERT_NOT_REACHED(); + break; + } +} + void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) { ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); @@ -492,6 +541,33 @@ void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName object->putDirectInternal<PutModeDefineOwnProperty>(exec->globalData(), propertyName, value, attributes, slot, getCallableObject(value)); } +void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) +{ + ASSERT(prototype); + if (prototype.isObject()) + asObject(prototype)->notifyUsedAsPrototype(globalData); + + Structure* newStructure = Structure::changePrototypeTransition(globalData, structure(), prototype); + setStructure(globalData, newStructure); + + if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses()) + return; + + if (mayBeUsedAsPrototype(globalData)) { + newStructure->globalObject()->haveABadTime(globalData); + return; + } + + if (!hasIndexingHeader(structure()->indexingType())) + return; + + if (shouldUseSlowPut(structure()->indexingType())) + return; + + newStructure = Structure::nonPropertyTransition(globalData, newStructure, SwitchToSlowPutArrayStorage); + setStructure(globalData, newStructure); +} + bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype) { JSValue checkFor = this; @@ -508,9 +584,32 @@ bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prot return true; } +void JSObject::resetInheritorID(JSGlobalData& globalData) +{ + PropertyOffset offset = structure()->get(globalData, globalData.m_inheritorIDKey); + if (!isValidOffset(offset)) + return; + + putDirectOffset(globalData, offset, jsUndefined()); +} + +Structure* JSObject::inheritorID(JSGlobalData& globalData) +{ + if (WriteBarrierBase<Unknown>* location = getDirectLocation(globalData, globalData.m_inheritorIDKey)) { + JSValue value = location->get(); + if (value.isCell()) { + Structure* inheritorID = jsCast<Structure*>(value); + ASSERT(inheritorID->isEmpty()); + return inheritorID; + } + ASSERT(value.isUndefined()); + } + return createInheritorID(globalData); +} + bool JSObject::allowsAccessFrom(ExecState* exec) { - JSGlobalObject* globalObject = isGlobalThis() ? jsCast<JSGlobalThis*>(this)->unwrappedObject() : this->globalObject(); + JSGlobalObject* globalObject = unwrappedGlobalObject(); return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec); } @@ -597,12 +696,10 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i)); switch (thisObject->structure()->indexingType()) { - case ArrayClass: - case NonArray: + case ALL_BLANK_INDEXING_TYPES: return true; - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i < storage->vectorLength()) { @@ -762,12 +859,10 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa // is incredibly inefficient for large arrays. We need a different approach, // which almost certainly means a different structure for PropertyNameArray. switch (object->structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: break; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); @@ -857,6 +952,13 @@ void JSObject::preventExtensions(JSGlobalData& globalData) setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure())); } +JSGlobalObject* JSObject::unwrappedGlobalObject() +{ + if (isGlobalThis()) + return jsCast<JSGlobalThis*>(this)->unwrappedObject(); + return structure()->globalObject(); +} + // This presently will flatten to an uncachable dictionary; this is suitable // for use in delete, we may want to do something different elsewhere. void JSObject::reifyStaticFunctionsForDelete(ExecState* exec) @@ -920,18 +1022,35 @@ NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyO slot.setUndefined(); } -Structure* JSObject::createInheritorID(JSGlobalData& globalData) +void JSObject::notifyUsedAsPrototype(JSGlobalData& globalData) { - ASSERT(!getDirectLocation(globalData, globalData.m_inheritorIDKey)); - - JSGlobalObject* globalObject; - if (isGlobalThis()) - globalObject = static_cast<JSGlobalThis*>(this)->unwrappedObject(); - else - globalObject = structure()->globalObject(); - ASSERT(globalObject); + PropertyOffset offset = structure()->get(globalData, globalData.m_inheritorIDKey); + if (isValidOffset(offset)) + return; + + PutPropertySlot slot; + putDirectInternal<PutModeDefineOwnProperty>(globalData, globalData.m_inheritorIDKey, jsUndefined(), DontEnum, slot, 0); + + // Note that this method makes the somewhat odd decision to not check if this + // object currently has indexed accessors. We could do that check here, and if + // indexed accessors were found, we could tell the global object to have a bad + // time. But we avoid this, to allow the following to be always fast: + // + // 1) Create an object. + // 2) Give it a setter or read-only property that happens to have a numeric name. + // 3) Allocate objects that use this object as a prototype. + // + // This avoids anyone having a bad time. Even if the instance objects end up + // having indexed storage, the creation of indexed storage leads to a prototype + // chain walk that detects the presence of indexed setters and then does the + // right thing. As a result, having a bad time only happens if you add an + // indexed setter (or getter, or read-only field) to an object that is already + // used as a prototype. +} - Structure* inheritorID = createEmptyObjectStructure(globalData, globalObject, this); +Structure* JSObject::createInheritorID(JSGlobalData& globalData) +{ + Structure* inheritorID = createEmptyObjectStructure(globalData, unwrappedGlobalObject(), this); ASSERT(inheritorID->isEmpty()); PutPropertySlot slot; @@ -980,7 +1099,10 @@ void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMa // Defined in ES5.1 8.12.9 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) { - ASSERT(index != 0xFFFFFFFF); + ASSERT(index <= MAX_ARRAY_INDEX); + + if (descriptor.attributes() & (ReadOnly | Accessor)) + notifyPresenceOfIndexedAccessors(exec->globalData()); if (!inSparseIndexingMode()) { // Fast case: we're putting a regular property to a regular array @@ -1109,6 +1231,40 @@ void JSObject::deallocateSparseIndexMap() arrayStorage->m_sparseMap.clear(); } +bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow) +{ + for (JSObject* current = this; ;) { + // This has the same behavior with respect to prototypes as JSObject::put(). It only + // allows a prototype to intercept a put if (a) the prototype declares the property + // we're after rather than intercepting it via an override of JSObject::put(), and + // (b) that property is declared as ReadOnly or Accessor. + + ArrayStorage* storage = current->arrayStorageOrNull(); + if (storage && storage->m_sparseMap) { + SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i); + if (iter != storage->m_sparseMap->notFound() && (iter->second.attributes & (Accessor | ReadOnly))) { + iter->second.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow); + return true; + } + } + + JSValue prototypeValue = current->prototype(); + if (prototypeValue.isNull()) + return false; + + current = asObject(prototypeValue); + } +} + +bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) +{ + JSValue prototypeValue = prototype(); + if (prototypeValue.isNull()) + return false; + + return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow); +} + void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) { JSGlobalData& globalData = exec->globalData(); @@ -1189,8 +1345,7 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue ASSERT(i <= MAX_ARRAY_INDEX); switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: { + case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse()) { putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); break; @@ -1205,6 +1360,12 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue storage->m_numValuesInVector = 1; break; } + + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: + if (attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow)) + return; + // Otherwise, fall though. case NonArrayWithArrayStorage: case ArrayWithArrayStorage: @@ -1297,9 +1458,11 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV // i should be a valid array index that is outside of the current vector. ASSERT(i <= MAX_ARRAY_INDEX); + if (attributes & (ReadOnly | Accessor)) + notifyPresenceOfIndexedAccessors(globalData); + switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: { + case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse() || attributes) return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) @@ -1311,8 +1474,7 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV return true; } - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage()); default: @@ -1354,13 +1516,11 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) unsigned length; switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: vectorLength = 0; length = 0; break; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: vectorLength = m_butterfly->arrayStorage()->vectorLength(); length = m_butterfly->arrayStorage()->length(); break; @@ -1478,12 +1638,10 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope return false; switch (object->structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return false; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); if (i >= storage->length()) return false; diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 8df521b75..8b52915b6 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -118,7 +118,18 @@ namespace JSC { bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype); Structure* inheritorID(JSGlobalData&); - + void notifyUsedAsPrototype(JSGlobalData&); + + bool mayBeUsedAsPrototype(JSGlobalData& globalData) + { + return isValidOffset(structure()->get(globalData, globalData.m_inheritorIDKey)); + } + + bool mayInterceptIndexedAccesses() + { + return structure()->mayInterceptIndexedAccesses(); + } + JSValue get(ExecState*, PropertyName) const; JSValue get(ExecState*, unsigned propertyName) const; @@ -135,11 +146,9 @@ namespace JSC { unsigned getArrayLength() const { switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return 0; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->length(); default: ASSERT_NOT_REACHED(); @@ -150,11 +159,9 @@ namespace JSC { unsigned getVectorLength() { switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return 0; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->vectorLength(); default: ASSERT_NOT_REACHED(); @@ -189,11 +196,9 @@ namespace JSC { bool canGetIndexQuickly(unsigned i) { switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return false; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; default: ASSERT_NOT_REACHED(); @@ -204,8 +209,7 @@ namespace JSC { JSValue getIndexQuickly(unsigned i) { switch (structure()->indexingType()) { - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->m_vector[i].get(); default: ASSERT_NOT_REACHED(); @@ -216,12 +220,15 @@ namespace JSC { bool canSetIndexQuickly(unsigned i) { switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return false; case NonArrayWithArrayStorage: case ArrayWithArrayStorage: return i < m_butterfly->arrayStorage()->vectorLength(); + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: + return i < m_butterfly->arrayStorage()->vectorLength() + && !!m_butterfly->arrayStorage()->m_vector[i]; default: ASSERT_NOT_REACHED(); return false; @@ -231,8 +238,7 @@ namespace JSC { void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { WriteBarrier<Unknown>& x = m_butterfly->arrayStorage()->m_vector[i]; if (!x) { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -251,8 +257,7 @@ namespace JSC { void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); #if CHECK_ARRAY_CONSISTENCY ASSERT(storage->m_inCompactInitialization); @@ -276,8 +281,7 @@ namespace JSC { void completeInitialization(unsigned newLength) { switch (structure()->indexingType()) { - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); // Check that we have initialized as meny properties as we think we have. UNUSED_PARAM(storage); @@ -298,11 +302,9 @@ namespace JSC { bool inSparseIndexingMode() { switch (structure()->indexingType()) { - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return false; - case NonArrayWithArrayStorage: - case ArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->inSparseMode(); default: ASSERT_NOT_REACHED(); @@ -487,6 +489,22 @@ namespace JSC { return structure()->globalObject(); } + // Does everything possible to return the global object. If it encounters an object + // that does not have a global object, it returns 0 instead (for example + // JSNotAnObject). + JSGlobalObject* unwrappedGlobalObject(); + + void switchToSlowPutArrayStorage(JSGlobalData&); + + // The receiver is the prototype in this case. The following: + // + // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...) + // + // is equivalent to: + // + // foo->attemptToInterceptPutByIndexOnHole(...); + bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + static size_t offsetOfInlineStorage(); static ptrdiff_t butterflyOffset() @@ -522,10 +540,7 @@ namespace JSC { // To create derived types you likely want JSNonFinalObject, below. JSObject(JSGlobalData&, Structure*, Butterfly* = 0); - void resetInheritorID(JSGlobalData& globalData) - { - removeDirect(globalData, globalData.m_inheritorIDKey); - } + void resetInheritorID(JSGlobalData&); void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize); @@ -533,7 +548,7 @@ namespace JSC { // storage. This will assert otherwise. ArrayStorage* arrayStorage() { - ASSERT(structure()->indexingType() | HasArrayStorage); + ASSERT(hasArrayStorage(structure()->indexingType())); return m_butterfly->arrayStorage(); } @@ -542,8 +557,7 @@ namespace JSC { ArrayStorage* arrayStorageOrNull() { switch (structure()->indexingType()) { - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage(); default: @@ -558,12 +572,10 @@ namespace JSC { ArrayStorage* ensureArrayStorage(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ArrayWithArrayStorage: - case NonArrayWithArrayStorage: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage(); - case NonArray: - case ArrayClass: + case ALL_BLANK_INDEXING_TYPES: return createInitialArrayStorage(globalData); default: @@ -592,6 +604,10 @@ namespace JSC { void deallocateSparseIndexMap(); bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException); SparseArrayValueMap* allocateSparseIndexMap(JSGlobalData&); + + void notifyPresenceOfIndexedAccessors(JSGlobalData&); + + bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow); private: friend class LLIntOffsetsExtractor; @@ -824,22 +840,6 @@ inline JSValue JSObject::prototype() const return structure()->storedPrototype(); } -inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) -{ - ASSERT(prototype); - setStructure(globalData, Structure::changePrototypeTransition(globalData, structure(), prototype)); -} - -inline Structure* JSObject::inheritorID(JSGlobalData& globalData) -{ - if (WriteBarrierBase<Unknown>* location = getDirectLocation(globalData, globalData.m_inheritorIDKey)) { - Structure* inheritorID = jsCast<Structure*>(location->get()); - ASSERT(inheritorID->isEmpty()); - return inheritorID; - } - return createInheritorID(globalData); -} - inline bool JSCell::inherits(const ClassInfo* info) const { return classInfo()->isSubClassOf(info); @@ -1190,8 +1190,7 @@ inline void JSValue::put(ExecState* exec, PropertyName propertyName, JSValue val inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) { if (UNLIKELY(!isCell())) { - PutPropertySlot slot(shouldThrow); - putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot); + putToPrimitiveByIndex(exec, propertyName, value, shouldThrow); return; } asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value, shouldThrow); diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index caff9973b..ac00fad3d 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -113,6 +113,12 @@ void JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue { JSGlobalData& globalData = exec->globalData(); + unsigned index = propertyName.asIndex(); + if (index != PropertyName::NotAnIndex) { + putToPrimitiveByIndex(exec, index, value, slot.isStrictMode()); + return; + } + // Check if there are any setters or getters in the prototype chain JSObject* obj = synthesizePrototype(exec); JSValue prototype; @@ -172,6 +178,21 @@ void JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue return; } +void JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + if (propertyName > MAX_ARRAY_INDEX) { + PutPropertySlot slot(shouldThrow); + putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot); + return; + } + + if (synthesizePrototype(exec)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow)) + return; + + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + char* JSValue::description() const { static const size_t size = 128; diff --git a/Source/JavaScriptCore/runtime/JSValue.h b/Source/JavaScriptCore/runtime/JSValue.h index ce9405817..6e01d8d2d 100644 --- a/Source/JavaScriptCore/runtime/JSValue.h +++ b/Source/JavaScriptCore/runtime/JSValue.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -240,6 +240,7 @@ namespace JSC { JSValue get(ExecState*, unsigned propertyName, PropertySlot&) const; void put(ExecState*, PropertyName, JSValue, PutPropertySlot&); void putToPrimitive(ExecState*, PropertyName, JSValue, PutPropertySlot&); + void putToPrimitiveByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); void putByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); JSObject* toThisObject(ExecState*) const; diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp index 84c60a69c..b1a5b9fb3 100644 --- a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -74,6 +74,7 @@ void ObjectPrototype::finishCreation(JSGlobalData& globalData, JSGlobalObject*) { Base::finishCreation(globalData); ASSERT(inherits(&s_info)); + notifyUsedAsPrototype(globalData); } bool ObjectPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot &slot) diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp index ed8aace66..04fea60e8 100644 --- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp @@ -27,7 +27,6 @@ #include "RegExpMatchesArray.h" #include "ButterflyInlineMethods.h" -#include "SparseArrayValueMapInlineMethods.h" namespace JSC { diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp index 40c4ed26e..3f709b0a7 100644 --- a/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp +++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp @@ -27,11 +27,186 @@ #include "SparseArrayValueMap.h" #include "ClassInfo.h" -#include "SparseArrayValueMapInlineMethods.h" +#include "GetterSetter.h" +#include "JSObject.h" +#include "PropertySlot.h" +#include "Reject.h" +#include "SlotVisitor.h" +#include "Structure.h" namespace JSC { const ClassInfo SparseArrayValueMap::s_info = { "SparseArrayValueMap", 0, 0, 0, CREATE_METHOD_TABLE(SparseArrayValueMap) }; +SparseArrayValueMap::SparseArrayValueMap(JSGlobalData& globalData) + : Base(globalData, globalData.sparseArrayValueMapStructure.get()) + , m_flags(Normal) + , m_reportedCapacity(0) +{ +} + +SparseArrayValueMap::~SparseArrayValueMap() +{ +} + +void SparseArrayValueMap::finishCreation(JSGlobalData& globalData) +{ + Base::finishCreation(globalData); +} + +SparseArrayValueMap* SparseArrayValueMap::create(JSGlobalData& globalData) +{ + SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(globalData.heap)) SparseArrayValueMap(globalData); + result->finishCreation(globalData); + return result; +} + +void SparseArrayValueMap::destroy(JSCell* cell) +{ + static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap(); +} + +Structure* SparseArrayValueMap::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(globalData, globalObject, prototype, TypeInfo(CompoundType, StructureFlags), &s_info); +} + +SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigned i) +{ + SparseArrayEntry entry; + entry.setWithoutWriteBarrier(jsUndefined()); + + AddResult result = m_map.add(i, entry); + size_t capacity = m_map.capacity(); + if (capacity != m_reportedCapacity) { + Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); + m_reportedCapacity = capacity; + } + return result; +} + +void SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, JSValue value, bool shouldThrow) +{ + AddResult result = add(array, i); + SparseArrayEntry& entry = result.iterator->second; + + // To save a separate find & add, we first always add to the sparse map. + // In the uncommon case that this is a new property, and the array is not + // extensible, this is not the right thing to have done - so remove again. + if (result.isNewEntry && !array->isExtensible()) { + remove(result.iterator); + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + entry.put(exec, array, this, value, shouldThrow); +} + +bool SparseArrayValueMap::putDirect(ExecState* exec, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) +{ + AddResult result = add(array, i); + SparseArrayEntry& entry = result.iterator->second; + + // To save a separate find & add, we first always add to the sparse map. + // In the uncommon case that this is a new property, and the array is not + // extensible, this is not the right thing to have done - so remove again. + if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isExtensible()) { + remove(result.iterator); + return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); + } + + entry.attributes = attributes; + entry.set(exec->globalData(), this, value); + return true; +} + +void SparseArrayEntry::get(PropertySlot& slot) const +{ + JSValue value = Base::get(); + ASSERT(value); + + if (LIKELY(!value.isGetterSetter())) { + slot.setValue(value); + return; + } + + JSObject* getter = asGetterSetter(value)->getter(); + if (!getter) { + slot.setUndefined(); + return; + } + + slot.setGetterSlot(getter); +} + +void SparseArrayEntry::get(PropertyDescriptor& descriptor) const +{ + descriptor.setDescriptor(Base::get(), attributes); +} + +JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const +{ + JSValue result = Base::get(); + ASSERT(result); + + if (LIKELY(!result.isGetterSetter())) + return result; + + JSObject* getter = asGetterSetter(result)->getter(); + if (!getter) + return jsUndefined(); + + CallData callData; + CallType callType = getter->methodTable()->getCallData(getter, callData); + return call(exec, getter, callType, callData, array, exec->emptyList()); +} + +void SparseArrayEntry::put(ExecState* exec, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow) +{ + if (!(attributes & Accessor)) { + if (attributes & ReadOnly) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + set(exec->globalData(), map, value); + return; + } + + JSValue accessor = Base::get(); + ASSERT(accessor.isGetterSetter()); + JSObject* setter = asGetterSetter(accessor)->setter(); + + if (!setter) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + CallData callData; + CallType callType = setter->methodTable()->getCallData(setter, callData); + MarkedArgumentBuffer args; + args.append(value); + call(exec, setter, callType, callData, thisValue, args); +} + +JSValue SparseArrayEntry::getNonSparseMode() const +{ + ASSERT(!attributes); + return Base::get(); +} + +void SparseArrayValueMap::visitChildren(JSCell* thisObject, SlotVisitor& visitor) +{ + Base::visitChildren(thisObject, visitor); + + SparseArrayValueMap* thisMap = jsCast<SparseArrayValueMap*>(thisObject); + iterator end = thisMap->m_map.end(); + for (iterator it = thisMap->m_map.begin(); it != end; ++it) + visitor.append(&it->second); +} + } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.h b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h index aafdf974f..5d8d0577a 100644 --- a/Source/JavaScriptCore/runtime/SparseArrayValueMap.h +++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h @@ -36,6 +36,8 @@ namespace JSC { +class SparseArrayValueMap; + struct SparseArrayEntry : public WriteBarrier<Unknown> { typedef WriteBarrier<Unknown> Base; @@ -44,6 +46,7 @@ struct SparseArrayEntry : public WriteBarrier<Unknown> { JSValue get(ExecState*, JSObject*) const; void get(PropertySlot&) const; void get(PropertyDescriptor&) const; + void put(ExecState*, JSValue thisValue, SparseArrayValueMap*, JSValue, bool shouldThrow); JSValue getNonSparseMode() const; unsigned attributes; diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMapInlineMethods.h b/Source/JavaScriptCore/runtime/SparseArrayValueMapInlineMethods.h deleted file mode 100644 index f3ef32f46..000000000 --- a/Source/JavaScriptCore/runtime/SparseArrayValueMapInlineMethods.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SparseArrayValueMapInlineMethods_h -#define SparseArrayValueMapInlineMethods_h - -#include "GetterSetter.h" -#include "Reject.h" -#include "SparseArrayValueMap.h" - -namespace JSC { - -inline SparseArrayValueMap::SparseArrayValueMap(JSGlobalData& globalData) - : Base(globalData, globalData.sparseArrayValueMapStructure.get()) - , m_flags(Normal) - , m_reportedCapacity(0) -{ -} - -inline SparseArrayValueMap::~SparseArrayValueMap() -{ -} - -inline void SparseArrayValueMap::finishCreation(JSGlobalData& globalData) -{ - Base::finishCreation(globalData); -} - -inline SparseArrayValueMap* SparseArrayValueMap::create(JSGlobalData& globalData) -{ - SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(globalData.heap)) SparseArrayValueMap(globalData); - result->finishCreation(globalData); - return result; -} - -inline void SparseArrayValueMap::destroy(JSCell* cell) -{ - static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap(); -} - -inline Structure* SparseArrayValueMap::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) -{ - return Structure::create(globalData, globalObject, prototype, TypeInfo(CompoundType, StructureFlags), &s_info); -} - -inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigned i) -{ - SparseArrayEntry entry; - entry.setWithoutWriteBarrier(jsUndefined()); - - AddResult result = m_map.add(i, entry); - size_t capacity = m_map.capacity(); - if (capacity != m_reportedCapacity) { - Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); - m_reportedCapacity = capacity; - } - return result; -} - -inline void SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, JSValue value, bool shouldThrow) -{ - AddResult result = add(array, i); - SparseArrayEntry& entry = result.iterator->second; - - // To save a separate find & add, we first always add to the sparse map. - // In the uncommon case that this is a new property, and the array is not - // extensible, this is not the right thing to have done - so remove again. - if (result.isNewEntry && !array->isExtensible()) { - remove(result.iterator); - if (shouldThrow) - throwTypeError(exec, StrictModeReadonlyPropertyWriteError); - return; - } - - if (!(entry.attributes & Accessor)) { - if (entry.attributes & ReadOnly) { - if (shouldThrow) - throwTypeError(exec, StrictModeReadonlyPropertyWriteError); - return; - } - - entry.set(exec->globalData(), this, value); - return; - } - - JSValue accessor = entry.SparseArrayEntry::Base::get(); - ASSERT(accessor.isGetterSetter()); - JSObject* setter = asGetterSetter(accessor)->setter(); - - if (!setter) { - if (shouldThrow) - throwTypeError(exec, StrictModeReadonlyPropertyWriteError); - return; - } - - CallData callData; - CallType callType = setter->methodTable()->getCallData(setter, callData); - MarkedArgumentBuffer args; - args.append(value); - call(exec, setter, callType, callData, array, args); -} - -inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) -{ - AddResult result = add(array, i); - SparseArrayEntry& entry = result.iterator->second; - - // To save a separate find & add, we first always add to the sparse map. - // In the uncommon case that this is a new property, and the array is not - // extensible, this is not the right thing to have done - so remove again. - if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isExtensible()) { - remove(result.iterator); - return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); - } - - entry.attributes = attributes; - entry.set(exec->globalData(), this, value); - return true; -} - -inline void SparseArrayEntry::get(PropertySlot& slot) const -{ - JSValue value = Base::get(); - ASSERT(value); - - if (LIKELY(!value.isGetterSetter())) { - slot.setValue(value); - return; - } - - JSObject* getter = asGetterSetter(value)->getter(); - if (!getter) { - slot.setUndefined(); - return; - } - - slot.setGetterSlot(getter); -} - -inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const -{ - descriptor.setDescriptor(Base::get(), attributes); -} - -inline JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const -{ - JSValue result = Base::get(); - ASSERT(result); - - if (LIKELY(!result.isGetterSetter())) - return result; - - JSObject* getter = asGetterSetter(result)->getter(); - if (!getter) - return jsUndefined(); - - CallData callData; - CallType callType = getter->methodTable()->getCallData(getter, callData); - return call(exec, getter, callType, callData, array, exec->emptyList()); -} - -inline JSValue SparseArrayEntry::getNonSparseMode() const -{ - ASSERT(!attributes); - return Base::get(); -} - -inline void SparseArrayValueMap::visitChildren(JSCell* thisObject, SlotVisitor& visitor) -{ - Base::visitChildren(thisObject, visitor); - - SparseArrayValueMap* thisMap = jsCast<SparseArrayValueMap*>(thisObject); - iterator end = thisMap->m_map.end(); - for (iterator it = thisMap->m_map.begin(); it != end; ++it) - visitor.append(&it->second); -} - -} // namespace JSC - -#endif // SparseArrayValueMapInlineMethods_h - diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index c99c6dda4..a59a0860d 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -309,6 +309,30 @@ Structure* Structure::addPropertyTransitionToExistingStructure(Structure* struct return 0; } +bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const +{ + for (const Structure* current = this; ;) { + if (current->mayInterceptIndexedAccesses()) + return true; + + JSValue prototype = current->storedPrototype(); + if (prototype.isNull()) + return false; + + current = asObject(prototype)->structure(); + } +} + +NonPropertyTransition Structure::suggestedIndexingTransition() const +{ + ASSERT(!hasIndexedProperties(indexingType())); + + if (anyObjectInChainMayInterceptIndexedAccesses() || globalObject()->isHavingABadTime()) + return AllocateSlowPutArrayStorage; + + return AllocateArrayStorage; +} + Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) { // If we have a specific function, we may have got to this point if there is diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index 9303a0dbb..e77287b20 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -144,7 +144,16 @@ namespace JSC { IndexingType indexingType() const { return m_indexingType & AllArrayTypes; } IndexingType indexingTypeIncludingHistory() const { return m_indexingType; } - + + bool mayInterceptIndexedAccesses() const + { + return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors); + } + + bool anyObjectInChainMayInterceptIndexedAccesses() const; + + NonPropertyTransition suggestedIndexingTransition() const; + JSGlobalObject* globalObject() const { return m_globalObject.get(); } void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); } diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h index 59e7e94f3..90cb6a4db 100644 --- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -43,7 +43,10 @@ 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 { - AllocateArrayStorage + AllocateArrayStorage, + AllocateSlowPutArrayStorage, + SwitchToSlowPutArrayStorage, + AddIndexedAccessors }; inline unsigned toAttributes(NonPropertyTransition transition) @@ -56,6 +59,13 @@ inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition switch (transition) { case AllocateArrayStorage: return oldType | HasArrayStorage; + case AllocateSlowPutArrayStorage: + return oldType | HasSlowPutArrayStorage; + case SwitchToSlowPutArrayStorage: + ASSERT(oldType & HasArrayStorage); + return (oldType & ~HasArrayStorage) | HasSlowPutArrayStorage; + case AddIndexedAccessors: + return oldType | MayHaveIndexedAccessors; default: ASSERT_NOT_REACHED(); return oldType; diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h index f6f70c6b9..5427a009b 100644 --- a/Source/JavaScriptCore/runtime/SymbolTable.h +++ b/Source/JavaScriptCore/runtime/SymbolTable.h @@ -40,6 +40,23 @@ namespace JSC { class Watchpoint; class WatchpointSet; + struct SlowArgument { + enum Status { + Normal = 0, + Captured = 1, + Deleted = 2 + }; + + SlowArgument() + : status(Normal) + , indexIfCaptured(0) + { + } + + Status status; + int indexIfCaptured; // If status is 'Captured', indexIfCaptured is our index in the CallFrame. + }; + static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); } // The bit twiddling in this class assumes that every register index is a @@ -357,9 +374,14 @@ namespace JSC { int captureEnd() { return m_captureEnd; } void setCaptureEnd(int captureEnd) { m_captureEnd = captureEnd; } + int parameterCount() { return m_parameterCountIncludingThis - 1; } int parameterCountIncludingThis() { return m_parameterCountIncludingThis; } void setParameterCountIncludingThis(int parameterCountIncludingThis) { m_parameterCountIncludingThis = parameterCountIncludingThis; } + // 0 if we don't capture any arguments; parameterCount() in length if we do. + const SlowArgument* slowArguments() { return m_slowArguments.get(); } + void setSlowArguments(PassOwnArrayPtr<SlowArgument> slowArguments) { m_slowArguments = slowArguments; } + static JS_EXPORTDATA const ClassInfo s_info; private: @@ -379,8 +401,9 @@ namespace JSC { CaptureMode m_captureMode; int m_captureStart; int m_captureEnd; + + OwnArrayPtr<SlowArgument> m_slowArguments; }; - } // namespace JSC #endif // SymbolTable_h |
