]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Arguments.cpp
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / runtime / Arguments.cpp
1 /*
2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
6 * Copyright (C) 2007 Maks Orlovich
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #include "config.h"
26 #include "Arguments.h"
27
28 #include "CopyVisitorInlines.h"
29 #include "JSActivation.h"
30 #include "JSArgumentsIterator.h"
31 #include "JSFunction.h"
32 #include "JSGlobalObject.h"
33 #include "JSCInlines.h"
34
35 using namespace std;
36
37 namespace JSC {
38
39 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(Arguments);
40
41 const ClassInfo Arguments::s_info = { "Arguments", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Arguments) };
42
43 void Arguments::visitChildren(JSCell* cell, SlotVisitor& visitor)
44 {
45 Arguments* thisObject = jsCast<Arguments*>(cell);
46 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
47 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
48 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
49 JSObject::visitChildren(thisObject, visitor);
50
51 if (thisObject->m_registerArray) {
52 visitor.copyLater(thisObject, ArgumentsRegisterArrayCopyToken,
53 thisObject->m_registerArray.get(), thisObject->registerArraySizeInBytes());
54 visitor.appendValues(thisObject->m_registerArray.get(), thisObject->m_numArguments);
55 }
56 if (thisObject->m_slowArgumentData) {
57 visitor.copyLater(thisObject, ArgumentsSlowArgumentDataCopyToken,
58 thisObject->m_slowArgumentData.get(), SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments));
59 }
60 visitor.append(&thisObject->m_callee);
61 visitor.append(&thisObject->m_activation);
62 }
63
64 void Arguments::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token)
65 {
66 Arguments* thisObject = jsCast<Arguments*>(cell);
67 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
68
69
70 switch (token) {
71 case ArgumentsRegisterArrayCopyToken: {
72 WriteBarrier<Unknown>* registerArray = thisObject->m_registerArray.get();
73 if (!registerArray)
74 return;
75
76 if (visitor.checkIfShouldCopy(registerArray)) {
77 size_t bytes = thisObject->registerArraySizeInBytes();
78 WriteBarrier<Unknown>* newRegisterArray = static_cast<WriteBarrier<Unknown>*>(visitor.allocateNewSpace(bytes));
79 memcpy(newRegisterArray, registerArray, bytes);
80 thisObject->m_registerArray.setWithoutWriteBarrier(newRegisterArray);
81 thisObject->m_registers = newRegisterArray - CallFrame::offsetFor(1) - 1;
82 visitor.didCopy(registerArray, bytes);
83 }
84 return;
85 }
86
87 case ArgumentsSlowArgumentDataCopyToken: {
88 SlowArgumentData* slowArgumentData = thisObject->m_slowArgumentData.get();
89 if (!slowArgumentData)
90 return;
91
92 if (visitor.checkIfShouldCopy(slowArgumentData)) {
93 size_t bytes = SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments);
94 SlowArgumentData* newSlowArgumentData = static_cast<SlowArgumentData*>(visitor.allocateNewSpace(bytes));
95 memcpy(newSlowArgumentData, slowArgumentData, bytes);
96 thisObject->m_slowArgumentData.setWithoutWriteBarrier(newSlowArgumentData);
97 visitor.didCopy(slowArgumentData, bytes);
98 }
99 return;
100 }
101
102 default:
103 return;
104 }
105 }
106
107 static EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState*);
108
109 void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t copyLength, int32_t firstVarArgOffset)
110 {
111 uint32_t length = copyLength + firstVarArgOffset;
112
113 if (UNLIKELY(m_overrodeLength)) {
114 length = min(get(exec, exec->propertyNames().length).toUInt32(exec), length);
115 for (unsigned i = firstVarArgOffset; i < length; i++)
116 callFrame->setArgument(i, get(exec, i));
117 return;
118 }
119 ASSERT(length == this->length(exec));
120 for (size_t i = firstVarArgOffset; i < length; ++i) {
121 if (JSValue value = tryGetArgument(i))
122 callFrame->setArgument(i - firstVarArgOffset, value);
123 else
124 callFrame->setArgument(i - firstVarArgOffset, get(exec, i));
125 }
126 }
127
128 void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
129 {
130 if (UNLIKELY(m_overrodeLength)) {
131 unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec);
132 for (unsigned i = 0; i < length; i++)
133 args.append(get(exec, i));
134 return;
135 }
136 uint32_t length = this->length(exec);
137 for (size_t i = 0; i < length; ++i) {
138 if (JSValue value = tryGetArgument(i))
139 args.append(value);
140 else
141 args.append(get(exec, i));
142 }
143 }
144
145 bool Arguments::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned i, PropertySlot& slot)
146 {
147 Arguments* thisObject = jsCast<Arguments*>(object);
148 if (JSValue value = thisObject->tryGetArgument(i)) {
149 slot.setValue(thisObject, None, value);
150 return true;
151 }
152
153 return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
154 }
155
156 void Arguments::createStrictModeCallerIfNecessary(ExecState* exec)
157 {
158 if (m_overrodeCaller)
159 return;
160
161 VM& vm = exec->vm();
162 m_overrodeCaller = true;
163 PropertyDescriptor descriptor;
164 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(vm), DontEnum | DontDelete | Accessor);
165 methodTable(exec->vm())->defineOwnProperty(this, exec, vm.propertyNames->caller, descriptor, false);
166 }
167
168 void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec)
169 {
170 if (m_overrodeCallee)
171 return;
172
173 VM& vm = exec->vm();
174 m_overrodeCallee = true;
175 PropertyDescriptor descriptor;
176 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(vm), DontEnum | DontDelete | Accessor);
177 methodTable(exec->vm())->defineOwnProperty(this, exec, vm.propertyNames->callee, descriptor, false);
178 }
179
180 bool Arguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
181 {
182 Arguments* thisObject = jsCast<Arguments*>(object);
183 unsigned i = propertyName.asIndex();
184 if (JSValue value = thisObject->tryGetArgument(i)) {
185 RELEASE_ASSERT(i < PropertyName::NotAnIndex);
186 slot.setValue(thisObject, None, value);
187 return true;
188 }
189
190 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) {
191 slot.setValue(thisObject, DontEnum, jsNumber(thisObject->m_numArguments));
192 return true;
193 }
194
195 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) {
196 if (!thisObject->m_isStrictMode) {
197 slot.setValue(thisObject, DontEnum, thisObject->m_callee.get());
198 return true;
199 }
200 thisObject->createStrictModeCalleeIfNecessary(exec);
201 }
202
203 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
204 thisObject->createStrictModeCallerIfNecessary(exec);
205
206 if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
207 return true;
208 if (propertyName == exec->propertyNames().iteratorPrivateName) {
209 VM& vm = exec->vm();
210 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
211 thisObject->JSC_NATIVE_FUNCTION(exec->propertyNames().iteratorPrivateName, argumentsFuncIterator, DontEnum, 0);
212 if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
213 return true;
214 }
215 return false;
216 }
217
218 void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
219 {
220 Arguments* thisObject = jsCast<Arguments*>(object);
221 for (unsigned i = 0; i < thisObject->m_numArguments; ++i) {
222 if (!thisObject->isArgument(i))
223 continue;
224 propertyNames.add(Identifier::from(exec, i));
225 }
226 if (mode == IncludeDontEnumProperties) {
227 propertyNames.add(exec->propertyNames().callee);
228 propertyNames.add(exec->propertyNames().length);
229 }
230 JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
231 }
232
233 void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
234 {
235 Arguments* thisObject = jsCast<Arguments*>(cell);
236 if (thisObject->trySetArgument(exec->vm(), i, value))
237 return;
238
239 PutPropertySlot slot(thisObject, shouldThrow);
240 JSObject::put(thisObject, exec, Identifier::from(exec, i), value, slot);
241 }
242
243 void Arguments::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
244 {
245 Arguments* thisObject = jsCast<Arguments*>(cell);
246 unsigned i = propertyName.asIndex();
247 if (thisObject->trySetArgument(exec->vm(), i, value))
248 return;
249
250 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
251 thisObject->m_overrodeLength = true;
252 thisObject->putDirect(exec->vm(), propertyName, value, DontEnum);
253 return;
254 }
255
256 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
257 if (!thisObject->m_isStrictMode) {
258 thisObject->m_overrodeCallee = true;
259 thisObject->putDirect(exec->vm(), propertyName, value, DontEnum);
260 return;
261 }
262 thisObject->createStrictModeCalleeIfNecessary(exec);
263 }
264
265 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
266 thisObject->createStrictModeCallerIfNecessary(exec);
267
268 JSObject::put(thisObject, exec, propertyName, value, slot);
269 }
270
271 bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
272 {
273 Arguments* thisObject = jsCast<Arguments*>(cell);
274 if (i < thisObject->m_numArguments) {
275 if (!Base::deletePropertyByIndex(cell, exec, i))
276 return false;
277 if (thisObject->tryDeleteArgument(exec->vm(), i))
278 return true;
279 }
280 return JSObject::deletePropertyByIndex(thisObject, exec, i);
281 }
282
283 bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
284 {
285 if (exec->vm().isInDefineOwnProperty())
286 return Base::deleteProperty(cell, exec, propertyName);
287
288 Arguments* thisObject = jsCast<Arguments*>(cell);
289 unsigned i = propertyName.asIndex();
290 if (i < thisObject->m_numArguments) {
291 RELEASE_ASSERT(i < PropertyName::NotAnIndex);
292 if (!Base::deleteProperty(cell, exec, propertyName))
293 return false;
294 if (thisObject->tryDeleteArgument(exec->vm(), i))
295 return true;
296 }
297
298 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
299 thisObject->m_overrodeLength = true;
300 return true;
301 }
302
303 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
304 if (!thisObject->m_isStrictMode) {
305 thisObject->m_overrodeCallee = true;
306 return true;
307 }
308 thisObject->createStrictModeCalleeIfNecessary(exec);
309 }
310
311 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
312 thisObject->createStrictModeCallerIfNecessary(exec);
313
314 return JSObject::deleteProperty(thisObject, exec, propertyName);
315 }
316
317 bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
318 {
319 Arguments* thisObject = jsCast<Arguments*>(object);
320 unsigned i = propertyName.asIndex();
321 if (i < thisObject->m_numArguments) {
322 RELEASE_ASSERT(i < PropertyName::NotAnIndex);
323 // If the property is not yet present on the object, and is not yet marked as deleted, then add it now.
324 PropertySlot slot(thisObject);
325 if (!thisObject->isDeletedArgument(i) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) {
326 JSValue value = thisObject->tryGetArgument(i);
327 ASSERT(value);
328 object->putDirectMayBeIndex(exec, propertyName, value);
329 }
330 if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
331 return false;
332
333 // From ES 5.1, 10.6 Arguments Object
334 // 5. If the value of isMapped is not undefined, then
335 if (thisObject->isArgument(i)) {
336 // a. If IsAccessorDescriptor(Desc) is true, then
337 if (descriptor.isAccessorDescriptor()) {
338 // i. Call the [[Delete]] internal method of map passing P, and false as the arguments.
339 thisObject->tryDeleteArgument(exec->vm(), i);
340 } else { // b. Else
341 // i. If Desc.[[Value]] is present, then
342 // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments.
343 if (descriptor.value())
344 thisObject->trySetArgument(exec->vm(), i, descriptor.value());
345 // ii. If Desc.[[Writable]] is present and its value is false, then
346 // 1. Call the [[Delete]] internal method of map passing P and false as arguments.
347 if (descriptor.writablePresent() && !descriptor.writable())
348 thisObject->tryDeleteArgument(exec->vm(), i);
349 }
350 }
351 return true;
352 }
353
354 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
355 thisObject->putDirect(exec->vm(), propertyName, jsNumber(thisObject->m_numArguments), DontEnum);
356 thisObject->m_overrodeLength = true;
357 } else if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
358 thisObject->putDirect(exec->vm(), propertyName, thisObject->m_callee.get(), DontEnum);
359 thisObject->m_overrodeCallee = true;
360 } else if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
361 thisObject->createStrictModeCallerIfNecessary(exec);
362
363 return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
364 }
365
366 void Arguments::allocateRegisterArray(VM& vm)
367 {
368 ASSERT(!m_registerArray);
369 void* backingStore;
370 if (!vm.heap.tryAllocateStorage(this, registerArraySizeInBytes(), &backingStore))
371 RELEASE_ASSERT_NOT_REACHED();
372 m_registerArray.set(vm, this, static_cast<WriteBarrier<Unknown>*>(backingStore));
373 }
374
375 void Arguments::tearOff(CallFrame* callFrame)
376 {
377 if (isTornOff())
378 return;
379
380 if (!m_numArguments)
381 return;
382
383 // Must be called for the same call frame from which it was created.
384 ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == m_registers);
385
386 allocateRegisterArray(callFrame->vm());
387 m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1;
388
389 // If we have a captured argument that logically aliases activation storage,
390 // but we optimize away the activation, the argument needs to tear off into
391 // our storage. The simplest way to do this is to revert it to Normal status.
392 if (m_slowArgumentData && !m_activation) {
393 for (size_t i = 0; i < m_numArguments; ++i) {
394 if (m_slowArgumentData->slowArguments()[i].status != SlowArgument::Captured)
395 continue;
396 m_slowArgumentData->slowArguments()[i].status = SlowArgument::Normal;
397 m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i);
398 }
399 }
400
401 for (size_t i = 0; i < m_numArguments; ++i)
402 trySetArgument(callFrame->vm(), i, callFrame->argumentAfterCapture(i));
403 }
404
405 void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation)
406 {
407 RELEASE_ASSERT(activation);
408 if (isTornOff())
409 return;
410
411 if (!m_numArguments)
412 return;
413
414 m_activation.set(exec->vm(), this, activation);
415 tearOff(exec);
416 }
417
418 void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
419 {
420 if (isTornOff())
421 return;
422
423 if (!m_numArguments)
424 return;
425
426 allocateRegisterArray(callFrame->vm());
427 m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1;
428
429 for (size_t i = 0; i < m_numArguments; ++i) {
430 ValueRecovery& recovery = inlineCallFrame->arguments[i + 1];
431 trySetArgument(callFrame->vm(), i, recovery.recover(callFrame));
432 }
433 }
434
435 EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState* exec)
436 {
437 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
438 Arguments* arguments = jsDynamicCast<Arguments*>(thisObj);
439 if (!arguments)
440 return JSValue::encode(throwTypeError(exec, "Attempted to use Arguments iterator on non-Arguments object"));
441 return JSValue::encode(JSArgumentsIterator::create(exec->vm(), exec->callee()->globalObject()->argumentsIteratorStructure(), arguments));
442 }
443
444
445 } // namespace JSC