#ifndef Arguments_h
#define Arguments_h
#include "CodeOrigin.h"
#include "JSActivation.h"
#include "JSFunction.h"
#include "JSGlobalObject.h"
#include "Interpreter.h"
#include "ObjectConstructor.h"
#include "WriteBarrierInlines.h"
#include <wtf/StdLibExtras.h>
namespace JSC {
class Arguments : public JSNonFinalObject {
friend class JIT;
friend class JSArgumentsIterator;
public:
typedef JSNonFinalObject Base;
static Arguments* create(VM& vm, CallFrame* callFrame)
{
Arguments* arguments = new (NotNull, allocateCell<Arguments>(vm.heap)) Arguments(callFrame);
arguments->finishCreation(callFrame);
return arguments;
}
static Arguments* create(VM& vm, CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
{
Arguments* arguments = new (NotNull, allocateCell<Arguments>(vm.heap)) Arguments(callFrame);
arguments->finishCreation(callFrame, inlineCallFrame);
return arguments;
}
enum { MaxArguments = 0x10000 };
private:
enum NoParametersType { NoParameters };
Arguments(CallFrame*);
Arguments(CallFrame*, NoParametersType);
public:
DECLARE_INFO;
static void visitChildren(JSCell*, SlotVisitor&);
static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken);
void fillArgList(ExecState*, MarkedArgumentBuffer&);
uint32_t length(ExecState* exec) const
{
if (UNLIKELY(m_overrodeLength))
return get(exec, exec->propertyNames().length).toUInt32(exec);
return m_numArguments;
}
void copyToArguments(ExecState*, CallFrame*, uint32_t copyLength, int32_t firstArgumentOffset);
void tearOff(CallFrame*);
void tearOff(CallFrame*, InlineCallFrame*);
bool isTornOff() const { return m_registerArray.get(); }
void didTearOffActivation(ExecState*, JSActivation*);
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(ArgumentsType, StructureFlags), info());
}
static ptrdiff_t offsetOfActivation() { return OBJECT_OFFSETOF(Arguments, m_activation); }
static ptrdiff_t offsetOfNumArguments() { return OBJECT_OFFSETOF(Arguments, m_numArguments); }
static ptrdiff_t offsetOfOverrodeLength() { return OBJECT_OFFSETOF(Arguments, m_overrodeLength); }
static ptrdiff_t offsetOfIsStrictMode() { return OBJECT_OFFSETOF(Arguments, m_isStrictMode); }
static ptrdiff_t offsetOfRegisters() { return OBJECT_OFFSETOF(Arguments, m_registers); }
static ptrdiff_t offsetOfRegisterArray() { return OBJECT_OFFSETOF(Arguments, m_registerArray); }
static ptrdiff_t offsetOfSlowArgumentData() { return OBJECT_OFFSETOF(Arguments, m_slowArgumentData); }
static ptrdiff_t offsetOfCallee() { return OBJECT_OFFSETOF(Arguments, m_callee); }
static size_t allocationSize(size_t inlineCapacity)
{
ASSERT_UNUSED(inlineCapacity, !inlineCapacity);
return sizeof(Arguments);
}
protected:
static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
void finishCreation(CallFrame*);
void finishCreation(CallFrame*, InlineCallFrame*);
private:
static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
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, const PropertyDescriptor&, bool shouldThrow);
void createStrictModeCallerIfNecessary(ExecState*);
void createStrictModeCalleeIfNecessary(ExecState*);
size_t registerArraySizeInBytes() const { return sizeof(WriteBarrier<Unknown>) * m_numArguments; }
void allocateRegisterArray(VM&);
bool isArgument(size_t);
bool trySetArgument(VM&, size_t argument, JSValue);
JSValue tryGetArgument(size_t argument);
bool isDeletedArgument(size_t);
bool tryDeleteArgument(VM&, size_t);
WriteBarrierBase<Unknown>& argument(size_t);
void allocateSlowArguments(VM&);
void init(CallFrame*);
WriteBarrier<JSActivation> m_activation;
unsigned m_numArguments;
bool m_overrodeLength;
bool m_overrodeCallee;
bool m_overrodeCaller;
bool m_isStrictMode;
WriteBarrierBase<Unknown>* m_registers;
CopyWriteBarrier<WriteBarrier<Unknown>> m_registerArray;
public:
struct SlowArgumentData {
public:
SlowArgumentData()
: m_bytecodeToMachineCaptureOffset(0)
{
}
SlowArgument* slowArguments()
{
return reinterpret_cast<SlowArgument*>(WTF::roundUpToMultipleOf<8>(reinterpret_cast<size_t>(this + 1)));
}
int bytecodeToMachineCaptureOffset() const { return m_bytecodeToMachineCaptureOffset; }
void setBytecodeToMachineCaptureOffset(int newOffset) { m_bytecodeToMachineCaptureOffset = newOffset; }
static size_t sizeForNumArguments(unsigned numArguments)
{
return WTF::roundUpToMultipleOf<8>(sizeof(SlowArgumentData)) + sizeof(SlowArgument) * numArguments;
}
private:
int m_bytecodeToMachineCaptureOffset; };
private:
CopyWriteBarrier<SlowArgumentData> m_slowArgumentData;
WriteBarrier<JSFunction> m_callee;
};
Arguments* asArguments(JSValue);
inline Arguments* asArguments(JSValue value)
{
ASSERT(asObject(value)->inherits(Arguments::info()));
return static_cast<Arguments*>(asObject(value));
}
inline Arguments::Arguments(CallFrame* callFrame)
: Base(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure())
{
}
inline Arguments::Arguments(CallFrame* callFrame, NoParametersType)
: Base(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure())
{
}
inline void Arguments::allocateSlowArguments(VM& vm)
{
if (!!m_slowArgumentData)
return;
void* backingStore;
if (!vm.heap.tryAllocateStorage(this, SlowArgumentData::sizeForNumArguments(m_numArguments), &backingStore))
RELEASE_ASSERT_NOT_REACHED();
m_slowArgumentData.set(vm, this, static_cast<SlowArgumentData*>(backingStore));
for (size_t i = 0; i < m_numArguments; ++i) {
ASSERT(m_slowArgumentData->slowArguments()[i].status == SlowArgument::Normal);
m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i);
}
}
inline bool Arguments::tryDeleteArgument(VM& vm, size_t argument)
{
if (!isArgument(argument))
return false;
allocateSlowArguments(vm);
m_slowArgumentData->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_slowArgumentData)
return false;
if (m_slowArgumentData->slowArguments()[argument].status != SlowArgument::Deleted)
return false;
return true;
}
inline bool Arguments::isArgument(size_t argument)
{
if (argument >= m_numArguments)
return false;
if (m_slowArgumentData && m_slowArgumentData->slowArguments()[argument].status == SlowArgument::Deleted)
return false;
return true;
}
inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument)
{
ASSERT(isArgument(argument));
if (!m_slowArgumentData)
return m_registers[CallFrame::argumentOffset(argument)];
int index = m_slowArgumentData->slowArguments()[argument].index;
if (!m_activation || m_slowArgumentData->slowArguments()[argument].status != SlowArgument::Captured)
return m_registers[index];
return m_activation->registerAt(index - m_slowArgumentData->bytecodeToMachineCaptureOffset());
}
inline void Arguments::finishCreation(CallFrame* callFrame)
{
Base::finishCreation(callFrame->vm());
ASSERT(inherits(info()));
JSFunction* callee = jsCast<JSFunction*>(callFrame->callee());
m_numArguments = callFrame->argumentCount();
m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers());
m_callee.set(callFrame->vm(), this, callee);
m_overrodeLength = false;
m_overrodeCallee = false;
m_overrodeCaller = false;
m_isStrictMode = callFrame->codeBlock()->isStrictMode();
CodeBlock* codeBlock = callFrame->codeBlock();
if (codeBlock->hasSlowArguments()) {
SymbolTable* symbolTable = codeBlock->symbolTable();
const SlowArgument* slowArguments = codeBlock->machineSlowArguments();
allocateSlowArguments(callFrame->vm());
size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount());
for (size_t i = 0; i < count; ++i)
m_slowArgumentData->slowArguments()[i] = slowArguments[i];
m_slowArgumentData->setBytecodeToMachineCaptureOffset(
codeBlock->framePointerOffsetToGetActivationRegisters());
}
if (m_isStrictMode || !callee->jsExecutable()->parameterCount())
tearOff(callFrame);
}
inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
{
Base::finishCreation(callFrame->vm());
ASSERT(inherits(info()));
JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
m_numArguments = inlineCallFrame->arguments.size() - 1;
if (m_numArguments) {
int offsetForArgumentOne = inlineCallFrame->arguments[1].virtualRegister().offset();
m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + offsetForArgumentOne - virtualRegisterForArgument(1).offset();
} else
m_registers = 0;
m_callee.set(callFrame->vm(), this, callee);
m_overrodeLength = false;
m_overrodeCallee = false;
m_overrodeCaller = false;
m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode();
ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments());
if (m_isStrictMode || !callee->jsExecutable()->parameterCount())
tearOff(callFrame, inlineCallFrame);
}
}
#endif // Arguments_h