]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
3 | * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | |
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 | ||
27 | #include "CodeOrigin.h" | |
28 | #include "JSActivation.h" | |
29 | #include "JSFunction.h" | |
30 | #include "JSGlobalObject.h" | |
31 | #include "Interpreter.h" | |
32 | #include "ObjectConstructor.h" | |
33 | #include "WriteBarrierInlines.h" | |
34 | #include <wtf/StdLibExtras.h> | |
35 | ||
36 | namespace JSC { | |
37 | ||
38 | class Arguments : public JSNonFinalObject { | |
39 | friend class JIT; | |
40 | friend class JSArgumentsIterator; | |
41 | public: | |
42 | typedef JSNonFinalObject Base; | |
43 | ||
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 | } | |
57 | ||
58 | enum { MaxArguments = 0x10000 }; | |
59 | ||
60 | private: | |
61 | enum NoParametersType { NoParameters }; | |
62 | ||
63 | Arguments(CallFrame*); | |
64 | Arguments(CallFrame*, NoParametersType); | |
65 | ||
66 | public: | |
67 | DECLARE_INFO; | |
68 | ||
69 | static void visitChildren(JSCell*, SlotVisitor&); | |
70 | static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); | |
71 | ||
72 | void fillArgList(ExecState*, MarkedArgumentBuffer&); | |
73 | ||
74 | uint32_t length(ExecState* exec) const | |
75 | { | |
76 | if (UNLIKELY(m_overrodeLength)) | |
77 | return get(exec, exec->propertyNames().length).toUInt32(exec); | |
78 | return m_numArguments; | |
79 | } | |
80 | ||
81 | void copyToArguments(ExecState*, CallFrame*, uint32_t copyLength, int32_t firstArgumentOffset); | |
82 | void tearOff(CallFrame*); | |
83 | void tearOff(CallFrame*, InlineCallFrame*); | |
84 | bool isTornOff() const { return m_registerArray.get(); } | |
85 | void didTearOffActivation(ExecState*, JSActivation*); | |
86 | ||
87 | static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) | |
88 | { | |
89 | return Structure::create(vm, globalObject, prototype, TypeInfo(ArgumentsType, StructureFlags), info()); | |
90 | } | |
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 | ||
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: | |
114 | static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); | |
115 | static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); | |
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); | |
121 | static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); | |
122 | void createStrictModeCallerIfNecessary(ExecState*); | |
123 | void createStrictModeCalleeIfNecessary(ExecState*); | |
124 | ||
125 | size_t registerArraySizeInBytes() const { return sizeof(WriteBarrier<Unknown>) * m_numArguments; } | |
126 | void allocateRegisterArray(VM&); | |
127 | bool isArgument(size_t); | |
128 | bool trySetArgument(VM&, size_t argument, JSValue); | |
129 | JSValue tryGetArgument(size_t argument); | |
130 | bool isDeletedArgument(size_t); | |
131 | bool tryDeleteArgument(VM&, size_t); | |
132 | WriteBarrierBase<Unknown>& argument(size_t); | |
133 | void allocateSlowArguments(VM&); | |
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; | |
150 | CopyWriteBarrier<WriteBarrier<Unknown>> m_registerArray; | |
151 | ||
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; | |
179 | ||
180 | WriteBarrier<JSFunction> m_callee; | |
181 | }; | |
182 | ||
183 | Arguments* asArguments(JSValue); | |
184 | ||
185 | inline Arguments* asArguments(JSValue value) | |
186 | { | |
187 | ASSERT(asObject(value)->inherits(Arguments::info())); | |
188 | return static_cast<Arguments*>(asObject(value)); | |
189 | } | |
190 | ||
191 | inline Arguments::Arguments(CallFrame* callFrame) | |
192 | : Base(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure()) | |
193 | { | |
194 | } | |
195 | ||
196 | inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) | |
197 | : Base(callFrame->vm(), callFrame->lexicalGlobalObject()->argumentsStructure()) | |
198 | { | |
199 | } | |
200 | ||
201 | inline void Arguments::allocateSlowArguments(VM& vm) | |
202 | { | |
203 | if (!!m_slowArgumentData) | |
204 | return; | |
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 | ||
211 | for (size_t i = 0; i < m_numArguments; ++i) { | |
212 | ASSERT(m_slowArgumentData->slowArguments()[i].status == SlowArgument::Normal); | |
213 | m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i); | |
214 | } | |
215 | } | |
216 | ||
217 | inline bool Arguments::tryDeleteArgument(VM& vm, size_t argument) | |
218 | { | |
219 | if (!isArgument(argument)) | |
220 | return false; | |
221 | allocateSlowArguments(vm); | |
222 | m_slowArgumentData->slowArguments()[argument].status = SlowArgument::Deleted; | |
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; | |
245 | if (!m_slowArgumentData) | |
246 | return false; | |
247 | if (m_slowArgumentData->slowArguments()[argument].status != SlowArgument::Deleted) | |
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; | |
256 | if (m_slowArgumentData && m_slowArgumentData->slowArguments()[argument].status == SlowArgument::Deleted) | |
257 | return false; | |
258 | return true; | |
259 | } | |
260 | ||
261 | inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) | |
262 | { | |
263 | ASSERT(isArgument(argument)); | |
264 | if (!m_slowArgumentData) | |
265 | return m_registers[CallFrame::argumentOffset(argument)]; | |
266 | ||
267 | int index = m_slowArgumentData->slowArguments()[argument].index; | |
268 | if (!m_activation || m_slowArgumentData->slowArguments()[argument].status != SlowArgument::Captured) | |
269 | return m_registers[index]; | |
270 | ||
271 | return m_activation->registerAt(index - m_slowArgumentData->bytecodeToMachineCaptureOffset()); | |
272 | } | |
273 | ||
274 | inline void Arguments::finishCreation(CallFrame* callFrame) | |
275 | { | |
276 | Base::finishCreation(callFrame->vm()); | |
277 | ASSERT(inherits(info())); | |
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 | ||
288 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
289 | if (codeBlock->hasSlowArguments()) { | |
290 | SymbolTable* symbolTable = codeBlock->symbolTable(); | |
291 | const SlowArgument* slowArguments = codeBlock->machineSlowArguments(); | |
292 | allocateSlowArguments(callFrame->vm()); | |
293 | size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount()); | |
294 | for (size_t i = 0; i < count; ++i) | |
295 | m_slowArgumentData->slowArguments()[i] = slowArguments[i]; | |
296 | m_slowArgumentData->setBytecodeToMachineCaptureOffset( | |
297 | codeBlock->framePointerOffsetToGetActivationRegisters()); | |
298 | } | |
299 | ||
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()); | |
309 | ASSERT(inherits(info())); | |
310 | ||
311 | JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); | |
312 | m_numArguments = inlineCallFrame->arguments.size() - 1; | |
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; | |
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 | } | |
331 | ||
332 | } // namespace JSC | |
333 | ||
334 | #endif // Arguments_h |