X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/14957cd040308e3eeec43d26bae5d76da13fcd85..93a3786624b2768d89bfa27e46598dc64e2fb70a:/runtime/Arguments.h diff --git a/runtime/Arguments.h b/runtime/Arguments.h index 9686d8b..58ac782 100644 --- a/runtime/Arguments.h +++ b/runtime/Arguments.h @@ -24,7 +24,9 @@ #ifndef Arguments_h #define Arguments_h +#include "CodeOrigin.h" #include "JSActivation.h" +#include "JSDestructibleObject.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "Interpreter.h" @@ -32,225 +34,246 @@ namespace JSC { - struct ArgumentsData { - WTF_MAKE_NONCOPYABLE(ArgumentsData); WTF_MAKE_FAST_ALLOCATED; - public: - ArgumentsData() { } - WriteBarrier activation; +class Arguments : public JSDestructibleObject { + friend class JIT; + friend class DFG::SpeculativeJIT; +public: + typedef JSDestructibleObject Base; - unsigned numParameters; - ptrdiff_t firstParameterIndex; - unsigned numArguments; - - WriteBarrier* registers; - OwnArrayPtr > registerArray; - - WriteBarrier* extraArguments; - OwnArrayPtr deletedArguments; - WriteBarrier extraArgumentsFixedBuffer[4]; - - WriteBarrier callee; - bool overrodeLength : 1; - bool overrodeCallee : 1; - bool overrodeCaller : 1; - bool isStrictMode : 1; - }; - - - class Arguments : public JSNonFinalObject { - public: - // Use an enum because otherwise gcc insists on doing a memory - // read. - enum { MaxArguments = 0x10000 }; - - enum NoParametersType { NoParameters }; - - Arguments(CallFrame*); - Arguments(CallFrame*, NoParametersType); - virtual ~Arguments(); - - static const ClassInfo s_info; - - virtual void visitChildren(SlotVisitor&); - - void fillArgList(ExecState*, MarkedArgumentBuffer&); - - uint32_t numProvidedArguments(ExecState* exec) const - { - if (UNLIKELY(d->overrodeLength)) - return get(exec, exec->propertyNames().length).toUInt32(exec); - return d->numArguments; - } - - void copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize); - void copyRegisters(JSGlobalData&); - bool isTornOff() const { return d->registerArray; } - void setActivation(JSGlobalData& globalData, JSActivation* activation) - { - ASSERT(!d->registerArray); - d->activation.set(globalData, this, activation); - d->registers = &activation->registerAt(0); - } - - static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) - { - return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); - } - - protected: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; - - private: - void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); - virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); - virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); - virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); - virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); - virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - virtual void put(ExecState*, unsigned propertyName, JSValue); - virtual bool deleteProperty(ExecState*, const Identifier& propertyName); - virtual bool deleteProperty(ExecState*, unsigned propertyName); - void createStrictModeCallerIfNecessary(ExecState*); - void createStrictModeCalleeIfNecessary(ExecState*); - - void init(CallFrame*); - - OwnPtr d; - }; - - Arguments* asArguments(JSValue); - - inline Arguments* asArguments(JSValue value) - { - ASSERT(asObject(value)->inherits(&Arguments::s_info)); - return static_cast(asObject(value)); - } - - ALWAYS_INLINE void Arguments::getArgumentsData(CallFrame* callFrame, JSFunction*& function, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc) + static Arguments* create(VM& vm, CallFrame* callFrame) { - function = asFunction(callFrame->callee()); - - int numParameters = function->jsExecutable()->parameterCount(); - argc = callFrame->argumentCountIncludingThis(); - - if (argc <= numParameters) - argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters; - else - argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters - argc; - - argc -= 1; // - 1 to skip "this" - firstParameterIndex = -RegisterFile::CallFrameHeaderSize - numParameters; + Arguments* arguments = new (NotNull, allocateCell(vm.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame); + return arguments; } - - inline Arguments::Arguments(CallFrame* callFrame) - : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) - , d(adoptPtr(new ArgumentsData)) + + static Arguments* create(VM& vm, CallFrame* callFrame, InlineCallFrame* inlineCallFrame) { - ASSERT(inherits(&s_info)); - - JSFunction* callee; - ptrdiff_t firstParameterIndex; - Register* argv; - int numArguments; - getArgumentsData(callFrame, callee, firstParameterIndex, argv, numArguments); - - d->numParameters = callee->jsExecutable()->parameterCount(); - d->firstParameterIndex = firstParameterIndex; - d->numArguments = numArguments; - - d->registers = reinterpret_cast*>(callFrame->registers()); - - WriteBarrier* extraArguments; - if (d->numArguments <= d->numParameters) - extraArguments = 0; - else { - unsigned numExtraArguments = d->numArguments - d->numParameters; - if (numExtraArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(WriteBarrier)) - extraArguments = new WriteBarrier[numExtraArguments]; - else - extraArguments = d->extraArgumentsFixedBuffer; - for (unsigned i = 0; i < numExtraArguments; ++i) - extraArguments[i].set(callFrame->globalData(), this, argv[d->numParameters + i].jsValue()); - } - - d->extraArguments = extraArguments; - - d->callee.set(callFrame->globalData(), this, callee); - d->overrodeLength = false; - d->overrodeCallee = false; - d->overrodeCaller = false; - d->isStrictMode = callFrame->codeBlock()->isStrictMode(); - if (d->isStrictMode) - copyRegisters(callFrame->globalData()); + Arguments* arguments = new (NotNull, allocateCell(vm.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame, inlineCallFrame); + return arguments; } - inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) - : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) - , d(adoptPtr(new ArgumentsData)) - { - ASSERT(inherits(&s_info)); - ASSERT(!asFunction(callFrame->callee())->jsExecutable()->parameterCount()); - - unsigned numArguments = callFrame->argumentCount(); - - d->numParameters = 0; - d->numArguments = numArguments; + enum { MaxArguments = 0x10000 }; - WriteBarrier* extraArguments; - if (numArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(Register)) - extraArguments = new WriteBarrier[numArguments]; - else - extraArguments = d->extraArgumentsFixedBuffer; +private: + enum NoParametersType { NoParameters }; + + Arguments(CallFrame*); + Arguments(CallFrame*, NoParametersType); + + void tearOffForInlineCallFrame(VM& vm, Register*, InlineCallFrame*); - Register* argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numArguments - 1; - for (unsigned i = 0; i < numArguments; ++i) - extraArguments[i].set(callFrame->globalData(), this, argv[i].jsValue()); +public: + static const ClassInfo s_info; - d->extraArguments = extraArguments; + static void visitChildren(JSCell*, SlotVisitor&); - d->callee.set(callFrame->globalData(), this, asFunction(callFrame->callee())); - d->overrodeLength = false; - d->overrodeCallee = false; - d->overrodeCaller = false; - d->isStrictMode = callFrame->codeBlock()->isStrictMode(); - if (d->isStrictMode) - copyRegisters(callFrame->globalData()); - } + void fillArgList(ExecState*, MarkedArgumentBuffer&); - inline void Arguments::copyRegisters(JSGlobalData& globalData) + uint32_t length(ExecState* exec) const { - ASSERT(!isTornOff()); - - if (!d->numParameters) - return; - - int registerOffset = d->numParameters + RegisterFile::CallFrameHeaderSize; - size_t registerArraySize = d->numParameters; - - OwnArrayPtr > registerArray = adoptArrayPtr(new WriteBarrier[registerArraySize]); - for (size_t i = 0; i < registerArraySize; i++) - registerArray[i].set(globalData, this, d->registers[i - registerOffset].get()); - d->registers = registerArray.get() + registerOffset; - d->registerArray = registerArray.release(); + if (UNLIKELY(m_overrodeLength)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return m_numArguments; } - - // This JSActivation function is defined here so it can get at Arguments::setRegisters. - inline void JSActivation::copyRegisters(JSGlobalData& globalData) - { - ASSERT(!m_registerArray); - - size_t numLocals = m_numCapturedVars + m_numParametersMinusThis; - - if (!numLocals) - return; - - int registerOffset = m_numParametersMinusThis + RegisterFile::CallFrameHeaderSize; - size_t registerArraySize = numLocals + RegisterFile::CallFrameHeaderSize; - - OwnArrayPtr > registerArray = copyRegisterArray(globalData, m_registers - registerOffset, registerArraySize, m_numParametersMinusThis + 1); - WriteBarrier* registers = registerArray.get() + registerOffset; - setRegisters(registers, registerArray.release()); + + void copyToArguments(ExecState*, CallFrame*, uint32_t length); + void tearOff(CallFrame*); + void tearOff(CallFrame*, InlineCallFrame*); + bool isTornOff() const { return m_registerArray; } + void didTearOffActivation(ExecState*, JSActivation*); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + +protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + void finishCreation(CallFrame*); + void finishCreation(CallFrame*, InlineCallFrame*); + +private: + static void destroy(JSCell*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); + void createStrictModeCallerIfNecessary(ExecState*); + void createStrictModeCalleeIfNecessary(ExecState*); + + bool isArgument(size_t); + bool trySetArgument(VM&, size_t argument, JSValue); + JSValue tryGetArgument(size_t argument); + bool isDeletedArgument(size_t); + bool tryDeleteArgument(size_t); + WriteBarrierBase& argument(size_t); + void allocateSlowArguments(); + + void init(CallFrame*); + + WriteBarrier 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* m_registers; + OwnArrayPtr > m_registerArray; + + OwnArrayPtr m_slowArguments; + + WriteBarrier m_callee; +}; + +Arguments* asArguments(JSValue); + +inline Arguments* asArguments(JSValue value) +{ + ASSERT(asObject(value)->inherits(&Arguments::s_info)); + return static_cast(asObject(value)); +} + +inline Arguments::Arguments(CallFrame* callFrame) + : JSDestructibleObject(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure()) +{ +} + +inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) + : JSDestructibleObject(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure()) +{ +} + +inline void Arguments::allocateSlowArguments() +{ + if (m_slowArguments) + return; + m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); + for (size_t i = 0; i < m_numArguments; ++i) { + ASSERT(m_slowArguments[i].status == SlowArgument::Normal); + m_slowArguments[i].index = CallFrame::argumentOffset(i); } +} + +inline bool Arguments::tryDeleteArgument(size_t argument) +{ + if (!isArgument(argument)) + return false; + allocateSlowArguments(); + m_slowArguments[argument].status = SlowArgument::Deleted; + return true; +} + +inline bool Arguments::trySetArgument(VM& vm, size_t argument, JSValue value) +{ + if (!isArgument(argument)) + return false; + this->argument(argument).set(vm, 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& Arguments::argument(size_t argument) +{ + ASSERT(isArgument(argument)); + if (!m_slowArguments) + return m_registers[CallFrame::argumentOffset(argument)]; + + int index = m_slowArguments[argument].index; + if (!m_activation || m_slowArguments[argument].status != SlowArgument::Captured) + return m_registers[index]; + + return m_activation->registerAt(index); +} + +inline void Arguments::finishCreation(CallFrame* callFrame) +{ + Base::finishCreation(callFrame->vm()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = jsCast(callFrame->callee()); + m_numArguments = callFrame->argumentCount(); + m_registers = reinterpret_cast*>(callFrame->registers()); + m_callee.set(callFrame->vm(), this, callee); + m_overrodeLength = false; + m_overrodeCallee = false; + m_overrodeCaller = false; + m_isStrictMode = callFrame->codeBlock()->isStrictMode(); + + SharedSymbolTable* symbolTable = callFrame->codeBlock()->symbolTable(); + const SlowArgument* slowArguments = symbolTable->slowArguments(); + if (slowArguments) { + allocateSlowArguments(); + size_t count = std::min(m_numArguments, symbolTable->parameterCount()); + for (size_t i = 0; i < count; ++i) + m_slowArguments[i] = slowArguments[i]; + } + + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame); +} + +inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) +{ + Base::finishCreation(callFrame->vm()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); + m_numArguments = inlineCallFrame->arguments.size() - 1; + m_registers = reinterpret_cast*>(callFrame->registers()) + inlineCallFrame->stackOffset; + m_callee.set(callFrame->vm(), this, callee); + m_overrodeLength = false; + m_overrodeCallee = false; + m_overrodeCaller = false; + m_isStrictMode = jsCast(inlineCallFrame->executable.get())->isStrictMode(); + ASSERT(!jsCast(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments()); + + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame, inlineCallFrame); +} } // namespace JSC