Arguments.h 11 KB
Newer Older
weinig@apple.com's avatar
weinig@apple.com committed
1 2
/*
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3
 *  Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
weinig@apple.com's avatar
weinig@apple.com committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
 *  Copyright (C) 2007 Maks Orlovich
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#ifndef Arguments_h
#define Arguments_h

27
#include "CodeOrigin.h"
28
#include "JSActivation.h"
29
#include "JSDestructibleObject.h"
30 31
#include "JSFunction.h"
#include "JSGlobalObject.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
32
#include "Interpreter.h"
33
#include "ObjectConstructor.h"
34
#include <wtf/StdLibExtras.h>
weinig@apple.com's avatar
weinig@apple.com committed
35

36
namespace JSC {
weinig@apple.com's avatar
weinig@apple.com committed
37

38 39
class Arguments : public JSDestructibleObject {
    friend class JIT;
40
    friend class JSArgumentsIterator;
41 42
public:
    typedef JSDestructibleObject Base;
43

44
    static Arguments* create(VM& vm, CallFrame* callFrame)
darin@apple.com's avatar
darin@apple.com committed
45
    {
46
        Arguments* arguments = new (NotNull, allocateCell<Arguments>(vm.heap)) Arguments(callFrame);
47 48
        arguments->finishCreation(callFrame);
        return arguments;
darin@apple.com's avatar
darin@apple.com committed
49
    }
50
        
51
    static Arguments* create(VM& vm, CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
52
    {
53
        Arguments* arguments = new (NotNull, allocateCell<Arguments>(vm.heap)) Arguments(callFrame);
54 55
        arguments->finishCreation(callFrame, inlineCallFrame);
        return arguments;
56 57
    }

58
    enum { MaxArguments = 0x10000 };
59

60 61 62 63 64 65 66
private:
    enum NoParametersType { NoParameters };
        
    Arguments(CallFrame*);
    Arguments(CallFrame*, NoParametersType);
        
public:
67
    DECLARE_INFO;
68

69
    static void visitChildren(JSCell*, SlotVisitor&);
70

71
    void fillArgList(ExecState*, MarkedArgumentBuffer&);
72

73
    uint32_t length(ExecState* exec) const 
74
    {
75 76 77
        if (UNLIKELY(m_overrodeLength))
            return get(exec, exec->propertyNames().length).toUInt32(exec);
        return m_numArguments; 
78
    }
79 80 81 82
        
    void copyToArguments(ExecState*, CallFrame*, uint32_t length);
    void tearOff(CallFrame*);
    void tearOff(CallFrame*, InlineCallFrame*);
83
    bool isTornOff() const { return m_registerArray.get(); }
84 85
    void didTearOffActivation(ExecState*, JSActivation*);

86
    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 
87
    { 
88
        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); 
89
    }
90 91 92 93 94 95
    
    static ptrdiff_t offsetOfNumArguments() { return OBJECT_OFFSETOF(Arguments, m_numArguments); }
    static ptrdiff_t offsetOfRegisters() { return OBJECT_OFFSETOF(Arguments, m_registers); }
    static ptrdiff_t offsetOfSlowArgumentData() { return OBJECT_OFFSETOF(Arguments, m_slowArgumentData); }
    static ptrdiff_t offsetOfOverrodeLength() { return OBJECT_OFFSETOF(Arguments, m_overrodeLength); }
    
96 97 98 99 100 101 102 103
protected:
    static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;

    void finishCreation(CallFrame*);
    void finishCreation(CallFrame*, InlineCallFrame*);

private:
    static void destroy(JSCell*);
104 105
    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
    static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
106 107 108 109 110
    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);
111
    static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
112 113 114 115
    void createStrictModeCallerIfNecessary(ExecState*);
    void createStrictModeCalleeIfNecessary(ExecState*);

    bool isArgument(size_t);
116
    bool trySetArgument(VM&, size_t argument, JSValue);
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    JSValue tryGetArgument(size_t argument);
    bool isDeletedArgument(size_t);
    bool tryDeleteArgument(size_t);
    WriteBarrierBase<Unknown>& argument(size_t);
    void allocateSlowArguments();

    void init(CallFrame*);

    WriteBarrier<JSActivation> m_activation;

    unsigned m_numArguments;

    // We make these full byte booleans to make them easy to test from the JIT,
    // and because even if they were single-bit booleans we still wouldn't save
    // any space.
    bool m_overrodeLength; 
    bool m_overrodeCallee;
    bool m_overrodeCaller;
    bool m_isStrictMode;

    WriteBarrierBase<Unknown>* m_registers;
138
    std::unique_ptr<WriteBarrier<Unknown>[]> m_registerArray;
139

fpizlo@apple.com's avatar
fpizlo@apple.com committed
140 141 142 143 144 145
    struct SlowArgumentData {
        std::unique_ptr<SlowArgument[]> slowArguments;
        int bytecodeToMachineCaptureOffset; // Add this if you have a bytecode offset into captured registers and you want the machine offset instead. Subtract if you want to do the opposite.
    };
    
    std::unique_ptr<SlowArgumentData> m_slowArgumentData;
146 147 148 149 150 151 152 153

    WriteBarrier<JSFunction> m_callee;
};

Arguments* asArguments(JSValue);

inline Arguments* asArguments(JSValue value)
{
154
    ASSERT(asObject(value)->inherits(Arguments::info()));
155 156 157 158
    return static_cast<Arguments*>(asObject(value));
}

inline Arguments::Arguments(CallFrame* callFrame)
159
    : JSDestructibleObject(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure())
160 161 162 163
{
}

inline Arguments::Arguments(CallFrame* callFrame, NoParametersType)
164
    : JSDestructibleObject(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure())
165 166 167 168 169
{
}

inline void Arguments::allocateSlowArguments()
{
fpizlo@apple.com's avatar
fpizlo@apple.com committed
170
    if (m_slowArgumentData)
171
        return;
fpizlo@apple.com's avatar
fpizlo@apple.com committed
172 173 174
    m_slowArgumentData = std::make_unique<SlowArgumentData>();
    m_slowArgumentData->bytecodeToMachineCaptureOffset = 0;
    m_slowArgumentData->slowArguments = std::make_unique<SlowArgument[]>(m_numArguments);
175
    for (size_t i = 0; i < m_numArguments; ++i) {
fpizlo@apple.com's avatar
fpizlo@apple.com committed
176 177
        ASSERT(m_slowArgumentData->slowArguments[i].status == SlowArgument::Normal);
        m_slowArgumentData->slowArguments[i].index = CallFrame::argumentOffset(i);
178
    }
179 180 181 182 183 184 185
}

inline bool Arguments::tryDeleteArgument(size_t argument)
{
    if (!isArgument(argument))
        return false;
    allocateSlowArguments();
fpizlo@apple.com's avatar
fpizlo@apple.com committed
186
    m_slowArgumentData->slowArguments[argument].status = SlowArgument::Deleted;
187 188 189
    return true;
}

190
inline bool Arguments::trySetArgument(VM& vm, size_t argument, JSValue value)
191 192 193
{
    if (!isArgument(argument))
        return false;
194
    this->argument(argument).set(vm, this, value);
195 196 197 198 199 200 201 202 203 204 205 206 207 208
    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;
fpizlo@apple.com's avatar
fpizlo@apple.com committed
209
    if (!m_slowArgumentData)
210
        return false;
fpizlo@apple.com's avatar
fpizlo@apple.com committed
211
    if (m_slowArgumentData->slowArguments[argument].status != SlowArgument::Deleted)
212 213 214 215 216 217 218 219
        return false;
    return true;
}

inline bool Arguments::isArgument(size_t argument)
{
    if (argument >= m_numArguments)
        return false;
fpizlo@apple.com's avatar
fpizlo@apple.com committed
220
    if (m_slowArgumentData && m_slowArgumentData->slowArguments[argument].status == SlowArgument::Deleted)
221 222 223 224 225 226 227
        return false;
    return true;
}

inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument)
{
    ASSERT(isArgument(argument));
fpizlo@apple.com's avatar
fpizlo@apple.com committed
228
    if (!m_slowArgumentData)
229 230
        return m_registers[CallFrame::argumentOffset(argument)];

fpizlo@apple.com's avatar
fpizlo@apple.com committed
231 232
    int index = m_slowArgumentData->slowArguments[argument].index;
    if (!m_activation || m_slowArgumentData->slowArguments[argument].status != SlowArgument::Captured)
233 234
        return m_registers[index];

fpizlo@apple.com's avatar
fpizlo@apple.com committed
235
    return m_activation->registerAt(index - m_slowArgumentData->bytecodeToMachineCaptureOffset);
236 237 238 239
}

inline void Arguments::finishCreation(CallFrame* callFrame)
{
240
    Base::finishCreation(callFrame->vm());
241
    ASSERT(inherits(info()));
242 243 244 245

    JSFunction* callee = jsCast<JSFunction*>(callFrame->callee());
    m_numArguments = callFrame->argumentCount();
    m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers());
246
    m_callee.set(callFrame->vm(), this, callee);
247 248 249 250 251
    m_overrodeLength = false;
    m_overrodeCallee = false;
    m_overrodeCaller = false;
    m_isStrictMode = callFrame->codeBlock()->isStrictMode();

fpizlo@apple.com's avatar
fpizlo@apple.com committed
252 253
    CodeBlock* codeBlock = callFrame->codeBlock();
    if (codeBlock->hasSlowArguments()) {
254
        SymbolTable* symbolTable = codeBlock->symbolTable();
fpizlo@apple.com's avatar
fpizlo@apple.com committed
255
        const SlowArgument* slowArguments = codeBlock->machineSlowArguments();
256 257 258
        allocateSlowArguments();
        size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount());
        for (size_t i = 0; i < count; ++i)
fpizlo@apple.com's avatar
fpizlo@apple.com committed
259 260 261
            m_slowArgumentData->slowArguments[i] = slowArguments[i];
        m_slowArgumentData->bytecodeToMachineCaptureOffset =
            codeBlock->framePointerOffsetToGetActivationRegisters();
262 263
    }

264 265 266 267 268 269 270 271
    // 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)
{
272
    Base::finishCreation(callFrame->vm());
273
    ASSERT(inherits(info()));
274

275
    JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
276
    m_numArguments = inlineCallFrame->arguments.size() - 1;
fpizlo@apple.com's avatar
fpizlo@apple.com committed
277 278 279 280 281 282
    
    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;
283
    m_callee.set(callFrame->vm(), this, callee);
284 285 286 287 288 289 290 291 292 293 294
    m_overrodeLength = false;
    m_overrodeCallee = false;
    m_overrodeCaller = false;
    m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode();
    ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments());

    // The bytecode generator omits op_tear_off_activation in cases of no
    // declared parameters, so we need to tear off immediately.
    if (m_isStrictMode || !callee->jsExecutable()->parameterCount())
        tearOff(callFrame, inlineCallFrame);
}
295

296
} // namespace JSC
weinig@apple.com's avatar
weinig@apple.com committed
297 298

#endif // Arguments_h