]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
f9bf01c6 | 3 | * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
9dae56ea A |
4 | * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) |
5 | * Copyright (C) 2007 Maks Orlovich | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Library General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Library General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Library General Public License | |
18 | * along with this library; see the file COPYING.LIB. If not, write to | |
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
20 | * Boston, MA 02110-1301, USA. | |
21 | * | |
22 | */ | |
23 | ||
24 | #ifndef Arguments_h | |
25 | #define Arguments_h | |
26 | ||
6fe7ccc8 | 27 | #include "CodeOrigin.h" |
9dae56ea A |
28 | #include "JSActivation.h" |
29 | #include "JSFunction.h" | |
30 | #include "JSGlobalObject.h" | |
31 | #include "Interpreter.h" | |
f9bf01c6 | 32 | #include "ObjectConstructor.h" |
81345200 A |
33 | #include "WriteBarrierInlines.h" |
34 | #include <wtf/StdLibExtras.h> | |
9dae56ea A |
35 | |
36 | namespace JSC { | |
37 | ||
81345200 | 38 | class Arguments : public JSNonFinalObject { |
93a37866 | 39 | friend class JIT; |
81345200 | 40 | friend class JSArgumentsIterator; |
93a37866 | 41 | public: |
81345200 | 42 | typedef JSNonFinalObject Base; |
9dae56ea | 43 | |
93a37866 A |
44 | static Arguments* create(VM& vm, CallFrame* callFrame) |
45 | { | |
46 | Arguments* arguments = new (NotNull, allocateCell<Arguments>(vm.heap)) Arguments(callFrame); | |
47 | arguments->finishCreation(callFrame); | |
48 | return arguments; | |
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 | arguments->finishCreation(callFrame, inlineCallFrame); | |
55 | return arguments; | |
56 | } | |
6fe7ccc8 | 57 | |
93a37866 | 58 | enum { MaxArguments = 0x10000 }; |
fb8617cd | 59 | |
93a37866 A |
60 | private: |
61 | enum NoParametersType { NoParameters }; | |
62 | ||
63 | Arguments(CallFrame*); | |
64 | Arguments(CallFrame*, NoParametersType); | |
6fe7ccc8 | 65 | |
93a37866 | 66 | public: |
81345200 | 67 | DECLARE_INFO; |
9dae56ea | 68 | |
93a37866 | 69 | static void visitChildren(JSCell*, SlotVisitor&); |
81345200 | 70 | static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); |
9dae56ea | 71 | |
93a37866 | 72 | void fillArgList(ExecState*, MarkedArgumentBuffer&); |
9dae56ea | 73 | |
93a37866 | 74 | uint32_t length(ExecState* exec) const |
9dae56ea | 75 | { |
93a37866 A |
76 | if (UNLIKELY(m_overrodeLength)) |
77 | return get(exec, exec->propertyNames().length).toUInt32(exec); | |
78 | return m_numArguments; | |
9dae56ea | 79 | } |
93a37866 | 80 | |
81345200 | 81 | void copyToArguments(ExecState*, CallFrame*, uint32_t copyLength, int32_t firstArgumentOffset); |
93a37866 A |
82 | void tearOff(CallFrame*); |
83 | void tearOff(CallFrame*, InlineCallFrame*); | |
81345200 | 84 | bool isTornOff() const { return m_registerArray.get(); } |
93a37866 A |
85 | void didTearOffActivation(ExecState*, JSActivation*); |
86 | ||
87 | static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) | |
88 | { | |
81345200 | 89 | return Structure::create(vm, globalObject, prototype, TypeInfo(ArgumentsType, StructureFlags), info()); |
9dae56ea | 90 | } |
81345200 A |
91 | |
92 | static ptrdiff_t offsetOfActivation() { return OBJECT_OFFSETOF(Arguments, m_activation); } | |
93 | static ptrdiff_t offsetOfNumArguments() { return OBJECT_OFFSETOF(Arguments, m_numArguments); } | |
94 | static ptrdiff_t offsetOfOverrodeLength() { return OBJECT_OFFSETOF(Arguments, m_overrodeLength); } | |
95 | static ptrdiff_t offsetOfIsStrictMode() { return OBJECT_OFFSETOF(Arguments, m_isStrictMode); } | |
96 | static ptrdiff_t offsetOfRegisters() { return OBJECT_OFFSETOF(Arguments, m_registers); } | |
97 | static ptrdiff_t offsetOfRegisterArray() { return OBJECT_OFFSETOF(Arguments, m_registerArray); } | |
98 | static ptrdiff_t offsetOfSlowArgumentData() { return OBJECT_OFFSETOF(Arguments, m_slowArgumentData); } | |
99 | static ptrdiff_t offsetOfCallee() { return OBJECT_OFFSETOF(Arguments, m_callee); } | |
100 | ||
101 | static size_t allocationSize(size_t inlineCapacity) | |
102 | { | |
103 | ASSERT_UNUSED(inlineCapacity, !inlineCapacity); | |
104 | return sizeof(Arguments); | |
105 | } | |
106 | ||
93a37866 A |
107 | protected: |
108 | static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; | |
109 | ||
110 | void finishCreation(CallFrame*); | |
111 | void finishCreation(CallFrame*, InlineCallFrame*); | |
112 | ||
113 | private: | |
81345200 A |
114 | static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); |
115 | static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); | |
93a37866 A |
116 | static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
117 | static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); | |
118 | static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); | |
119 | static bool deleteProperty(JSCell*, ExecState*, PropertyName); | |
120 | static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); | |
81345200 | 121 | static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); |
93a37866 A |
122 | void createStrictModeCallerIfNecessary(ExecState*); |
123 | void createStrictModeCalleeIfNecessary(ExecState*); | |
124 | ||
81345200 A |
125 | size_t registerArraySizeInBytes() const { return sizeof(WriteBarrier<Unknown>) * m_numArguments; } |
126 | void allocateRegisterArray(VM&); | |
93a37866 A |
127 | bool isArgument(size_t); |
128 | bool trySetArgument(VM&, size_t argument, JSValue); | |
129 | JSValue tryGetArgument(size_t argument); | |
130 | bool isDeletedArgument(size_t); | |
81345200 | 131 | bool tryDeleteArgument(VM&, size_t); |
93a37866 | 132 | WriteBarrierBase<Unknown>& argument(size_t); |
81345200 | 133 | void allocateSlowArguments(VM&); |
93a37866 A |
134 | |
135 | void init(CallFrame*); | |
136 | ||
137 | WriteBarrier<JSActivation> m_activation; | |
138 | ||
139 | unsigned m_numArguments; | |
140 | ||
141 | // We make these full byte booleans to make them easy to test from the JIT, | |
142 | // and because even if they were single-bit booleans we still wouldn't save | |
143 | // any space. | |
144 | bool m_overrodeLength; | |
145 | bool m_overrodeCallee; | |
146 | bool m_overrodeCaller; | |
147 | bool m_isStrictMode; | |
148 | ||
149 | WriteBarrierBase<Unknown>* m_registers; | |
81345200 | 150 | CopyWriteBarrier<WriteBarrier<Unknown>> m_registerArray; |
93a37866 | 151 | |
81345200 A |
152 | public: |
153 | struct SlowArgumentData { | |
154 | public: | |
155 | SlowArgumentData() | |
156 | : m_bytecodeToMachineCaptureOffset(0) | |
157 | { | |
158 | } | |
159 | ||
160 | SlowArgument* slowArguments() | |
161 | { | |
162 | return reinterpret_cast<SlowArgument*>(WTF::roundUpToMultipleOf<8>(reinterpret_cast<size_t>(this + 1))); | |
163 | } | |
164 | ||
165 | int bytecodeToMachineCaptureOffset() const { return m_bytecodeToMachineCaptureOffset; } | |
166 | void setBytecodeToMachineCaptureOffset(int newOffset) { m_bytecodeToMachineCaptureOffset = newOffset; } | |
167 | ||
168 | static size_t sizeForNumArguments(unsigned numArguments) | |
169 | { | |
170 | return WTF::roundUpToMultipleOf<8>(sizeof(SlowArgumentData)) + sizeof(SlowArgument) * numArguments; | |
171 | } | |
172 | ||
173 | private: | |
174 | int m_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. | |
175 | }; | |
176 | ||
177 | private: | |
178 | CopyWriteBarrier<SlowArgumentData> m_slowArgumentData; | |
93a37866 A |
179 | |
180 | WriteBarrier<JSFunction> m_callee; | |
181 | }; | |
182 | ||
183 | Arguments* asArguments(JSValue); | |
184 | ||
185 | inline Arguments* asArguments(JSValue value) | |
186 | { | |
81345200 | 187 | ASSERT(asObject(value)->inherits(Arguments::info())); |
93a37866 A |
188 | return static_cast<Arguments*>(asObject(value)); |
189 | } | |
190 | ||
191 | inline Arguments::Arguments(CallFrame* callFrame) | |
81345200 | 192 | : Base(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure()) |
93a37866 A |
193 | { |
194 | } | |
195 | ||
196 | inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) | |
81345200 | 197 | : Base(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure()) |
93a37866 A |
198 | { |
199 | } | |
200 | ||
81345200 | 201 | inline void Arguments::allocateSlowArguments(VM& vm) |
93a37866 | 202 | { |
81345200 | 203 | if (!!m_slowArgumentData) |
93a37866 | 204 | return; |
81345200 A |
205 | |
206 | void* backingStore; | |
207 | if (!vm.heap.tryAllocateStorage(this, SlowArgumentData::sizeForNumArguments(m_numArguments), &backingStore)) | |
208 | RELEASE_ASSERT_NOT_REACHED(); | |
209 | m_slowArgumentData.set(vm, this, static_cast<SlowArgumentData*>(backingStore)); | |
210 | ||
93a37866 | 211 | for (size_t i = 0; i < m_numArguments; ++i) { |
81345200 A |
212 | ASSERT(m_slowArgumentData->slowArguments()[i].status == SlowArgument::Normal); |
213 | m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i); | |
9dae56ea | 214 | } |
93a37866 A |
215 | } |
216 | ||
81345200 | 217 | inline bool Arguments::tryDeleteArgument(VM& vm, size_t argument) |
93a37866 A |
218 | { |
219 | if (!isArgument(argument)) | |
220 | return false; | |
81345200 A |
221 | allocateSlowArguments(vm); |
222 | m_slowArgumentData->slowArguments()[argument].status = SlowArgument::Deleted; | |
93a37866 A |
223 | return true; |
224 | } | |
225 | ||
226 | inline bool Arguments::trySetArgument(VM& vm, size_t argument, JSValue value) | |
227 | { | |
228 | if (!isArgument(argument)) | |
229 | return false; | |
230 | this->argument(argument).set(vm, this, value); | |
231 | return true; | |
232 | } | |
233 | ||
234 | inline JSValue Arguments::tryGetArgument(size_t argument) | |
235 | { | |
236 | if (!isArgument(argument)) | |
237 | return JSValue(); | |
238 | return this->argument(argument).get(); | |
239 | } | |
240 | ||
241 | inline bool Arguments::isDeletedArgument(size_t argument) | |
242 | { | |
243 | if (argument >= m_numArguments) | |
244 | return false; | |
81345200 | 245 | if (!m_slowArgumentData) |
93a37866 | 246 | return false; |
81345200 | 247 | if (m_slowArgumentData->slowArguments()[argument].status != SlowArgument::Deleted) |
93a37866 A |
248 | return false; |
249 | return true; | |
250 | } | |
251 | ||
252 | inline bool Arguments::isArgument(size_t argument) | |
253 | { | |
254 | if (argument >= m_numArguments) | |
255 | return false; | |
81345200 | 256 | if (m_slowArgumentData && m_slowArgumentData->slowArguments()[argument].status == SlowArgument::Deleted) |
93a37866 A |
257 | return false; |
258 | return true; | |
259 | } | |
260 | ||
261 | inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) | |
262 | { | |
263 | ASSERT(isArgument(argument)); | |
81345200 | 264 | if (!m_slowArgumentData) |
93a37866 A |
265 | return m_registers[CallFrame::argumentOffset(argument)]; |
266 | ||
81345200 A |
267 | int index = m_slowArgumentData->slowArguments()[argument].index; |
268 | if (!m_activation || m_slowArgumentData->slowArguments()[argument].status != SlowArgument::Captured) | |
93a37866 A |
269 | return m_registers[index]; |
270 | ||
81345200 | 271 | return m_activation->registerAt(index - m_slowArgumentData->bytecodeToMachineCaptureOffset()); |
93a37866 A |
272 | } |
273 | ||
274 | inline void Arguments::finishCreation(CallFrame* callFrame) | |
275 | { | |
276 | Base::finishCreation(callFrame->vm()); | |
81345200 | 277 | ASSERT(inherits(info())); |
93a37866 A |
278 | |
279 | JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); | |
280 | m_numArguments = callFrame->argumentCount(); | |
281 | m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); | |
282 | m_callee.set(callFrame->vm(), this, callee); | |
283 | m_overrodeLength = false; | |
284 | m_overrodeCallee = false; | |
285 | m_overrodeCaller = false; | |
286 | m_isStrictMode = callFrame->codeBlock()->isStrictMode(); | |
287 | ||
81345200 A |
288 | CodeBlock* codeBlock = callFrame->codeBlock(); |
289 | if (codeBlock->hasSlowArguments()) { | |
290 | SymbolTable* symbolTable = codeBlock->symbolTable(); | |
291 | const SlowArgument* slowArguments = codeBlock->machineSlowArguments(); | |
292 | allocateSlowArguments(callFrame->vm()); | |
93a37866 A |
293 | size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount()); |
294 | for (size_t i = 0; i < count; ++i) | |
81345200 A |
295 | m_slowArgumentData->slowArguments()[i] = slowArguments[i]; |
296 | m_slowArgumentData->setBytecodeToMachineCaptureOffset( | |
297 | codeBlock->framePointerOffsetToGetActivationRegisters()); | |
9dae56ea A |
298 | } |
299 | ||
93a37866 A |
300 | // The bytecode generator omits op_tear_off_activation in cases of no |
301 | // declared parameters, so we need to tear off immediately. | |
302 | if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) | |
303 | tearOff(callFrame); | |
304 | } | |
305 | ||
306 | inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) | |
307 | { | |
308 | Base::finishCreation(callFrame->vm()); | |
81345200 | 309 | ASSERT(inherits(info())); |
93a37866 A |
310 | |
311 | JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); | |
312 | m_numArguments = inlineCallFrame->arguments.size() - 1; | |
81345200 A |
313 | |
314 | if (m_numArguments) { | |
315 | int offsetForArgumentOne = inlineCallFrame->arguments[1].virtualRegister().offset(); | |
316 | m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + offsetForArgumentOne - virtualRegisterForArgument(1).offset(); | |
317 | } else | |
318 | m_registers = 0; | |
93a37866 A |
319 | m_callee.set(callFrame->vm(), this, callee); |
320 | m_overrodeLength = false; | |
321 | m_overrodeCallee = false; | |
322 | m_overrodeCaller = false; | |
323 | m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); | |
324 | ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments()); | |
325 | ||
326 | // The bytecode generator omits op_tear_off_activation in cases of no | |
327 | // declared parameters, so we need to tear off immediately. | |
328 | if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) | |
329 | tearOff(callFrame, inlineCallFrame); | |
330 | } | |
ba379fdc | 331 | |
9dae56ea A |
332 | } // namespace JSC |
333 | ||
334 | #endif // Arguments_h |