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
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.
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.
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.
26 #include "Arguments.h"
28 #include "CopyVisitorInlines.h"
29 #include "JSActivation.h"
30 #include "JSArgumentsIterator.h"
31 #include "JSFunction.h"
32 #include "JSGlobalObject.h"
33 #include "JSCInlines.h"
39 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(Arguments
);
41 const ClassInfo
Arguments::s_info
= { "Arguments", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(Arguments
) };
43 void Arguments::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
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
);
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
);
56 if (thisObject
->m_slowArgumentData
) {
57 visitor
.copyLater(thisObject
, ArgumentsSlowArgumentDataCopyToken
,
58 thisObject
->m_slowArgumentData
.get(), SlowArgumentData::sizeForNumArguments(thisObject
->m_numArguments
));
60 visitor
.append(&thisObject
->m_callee
);
61 visitor
.append(&thisObject
->m_activation
);
64 void Arguments::copyBackingStore(JSCell
* cell
, CopyVisitor
& visitor
, CopyToken token
)
66 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
67 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
71 case ArgumentsRegisterArrayCopyToken
: {
72 WriteBarrier
<Unknown
>* registerArray
= thisObject
->m_registerArray
.get();
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
);
87 case ArgumentsSlowArgumentDataCopyToken
: {
88 SlowArgumentData
* slowArgumentData
= thisObject
->m_slowArgumentData
.get();
89 if (!slowArgumentData
)
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
);
107 static EncodedJSValue JSC_HOST_CALL
argumentsFuncIterator(ExecState
*);
109 void Arguments::copyToArguments(ExecState
* exec
, CallFrame
* callFrame
, uint32_t copyLength
, int32_t firstVarArgOffset
)
111 uint32_t length
= copyLength
+ firstVarArgOffset
;
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
));
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
);
124 callFrame
->setArgument(i
- firstVarArgOffset
, get(exec
, i
));
128 void Arguments::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
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
));
136 uint32_t length
= this->length(exec
);
137 for (size_t i
= 0; i
< length
; ++i
) {
138 if (JSValue value
= tryGetArgument(i
))
141 args
.append(get(exec
, i
));
145 bool Arguments::getOwnPropertySlotByIndex(JSObject
* object
, ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
147 Arguments
* thisObject
= jsCast
<Arguments
*>(object
);
148 if (JSValue value
= thisObject
->tryGetArgument(i
)) {
149 slot
.setValue(thisObject
, None
, value
);
153 return JSObject::getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, i
), slot
);
156 void Arguments::createStrictModeCallerIfNecessary(ExecState
* exec
)
158 if (m_overrodeCaller
)
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);
168 void Arguments::createStrictModeCalleeIfNecessary(ExecState
* exec
)
170 if (m_overrodeCallee
)
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);
180 bool Arguments::getOwnPropertySlot(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
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
);
190 if (propertyName
== exec
->propertyNames().length
&& LIKELY(!thisObject
->m_overrodeLength
)) {
191 slot
.setValue(thisObject
, DontEnum
, jsNumber(thisObject
->m_numArguments
));
195 if (propertyName
== exec
->propertyNames().callee
&& LIKELY(!thisObject
->m_overrodeCallee
)) {
196 if (!thisObject
->m_isStrictMode
) {
197 slot
.setValue(thisObject
, DontEnum
, thisObject
->m_callee
.get());
200 thisObject
->createStrictModeCalleeIfNecessary(exec
);
203 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
204 thisObject
->createStrictModeCallerIfNecessary(exec
);
206 if (JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
))
208 if (propertyName
== exec
->propertyNames().iteratorPrivateName
) {
210 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
211 thisObject
->JSC_NATIVE_FUNCTION(exec
->propertyNames().iteratorPrivateName
, argumentsFuncIterator
, DontEnum
, 0);
212 if (JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
))
218 void Arguments::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
220 Arguments
* thisObject
= jsCast
<Arguments
*>(object
);
221 for (unsigned i
= 0; i
< thisObject
->m_numArguments
; ++i
) {
222 if (!thisObject
->isArgument(i
))
224 propertyNames
.add(Identifier::from(exec
, i
));
226 if (mode
== IncludeDontEnumProperties
) {
227 propertyNames
.add(exec
->propertyNames().callee
);
228 propertyNames
.add(exec
->propertyNames().length
);
230 JSObject::getOwnPropertyNames(thisObject
, exec
, propertyNames
, mode
);
233 void Arguments::putByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
235 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
236 if (thisObject
->trySetArgument(exec
->vm(), i
, value
))
239 PutPropertySlot
slot(thisObject
, shouldThrow
);
240 JSObject::put(thisObject
, exec
, Identifier::from(exec
, i
), value
, slot
);
243 void Arguments::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
245 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
246 unsigned i
= propertyName
.asIndex();
247 if (thisObject
->trySetArgument(exec
->vm(), i
, value
))
250 if (propertyName
== exec
->propertyNames().length
&& !thisObject
->m_overrodeLength
) {
251 thisObject
->m_overrodeLength
= true;
252 thisObject
->putDirect(exec
->vm(), propertyName
, value
, DontEnum
);
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
);
262 thisObject
->createStrictModeCalleeIfNecessary(exec
);
265 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
266 thisObject
->createStrictModeCallerIfNecessary(exec
);
268 JSObject::put(thisObject
, exec
, propertyName
, value
, slot
);
271 bool Arguments::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
)
273 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
274 if (i
< thisObject
->m_numArguments
) {
275 if (!Base::deletePropertyByIndex(cell
, exec
, i
))
277 if (thisObject
->tryDeleteArgument(exec
->vm(), i
))
280 return JSObject::deletePropertyByIndex(thisObject
, exec
, i
);
283 bool Arguments::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
285 if (exec
->vm().isInDefineOwnProperty())
286 return Base::deleteProperty(cell
, exec
, propertyName
);
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
))
294 if (thisObject
->tryDeleteArgument(exec
->vm(), i
))
298 if (propertyName
== exec
->propertyNames().length
&& !thisObject
->m_overrodeLength
) {
299 thisObject
->m_overrodeLength
= true;
303 if (propertyName
== exec
->propertyNames().callee
&& !thisObject
->m_overrodeCallee
) {
304 if (!thisObject
->m_isStrictMode
) {
305 thisObject
->m_overrodeCallee
= true;
308 thisObject
->createStrictModeCalleeIfNecessary(exec
);
311 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
312 thisObject
->createStrictModeCallerIfNecessary(exec
);
314 return JSObject::deleteProperty(thisObject
, exec
, propertyName
);
317 bool Arguments::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool shouldThrow
)
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
);
328 object
->putDirectMayBeIndex(exec
, propertyName
, value
);
330 if (!Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
))
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
);
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
);
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
);
363 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
);
366 void Arguments::allocateRegisterArray(VM
& vm
)
368 ASSERT(!m_registerArray
);
370 if (!vm
.heap
.tryAllocateStorage(this, registerArraySizeInBytes(), &backingStore
))
371 RELEASE_ASSERT_NOT_REACHED();
372 m_registerArray
.set(vm
, this, static_cast<WriteBarrier
<Unknown
>*>(backingStore
));
375 void Arguments::tearOff(CallFrame
* callFrame
)
383 // Must be called for the same call frame from which it was created.
384 ASSERT(bitwise_cast
<WriteBarrier
<Unknown
>*>(callFrame
) == m_registers
);
386 allocateRegisterArray(callFrame
->vm());
387 m_registers
= m_registerArray
.get() - CallFrame::offsetFor(1) - 1;
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
)
396 m_slowArgumentData
->slowArguments()[i
].status
= SlowArgument::Normal
;
397 m_slowArgumentData
->slowArguments()[i
].index
= CallFrame::argumentOffset(i
);
401 for (size_t i
= 0; i
< m_numArguments
; ++i
)
402 trySetArgument(callFrame
->vm(), i
, callFrame
->argumentAfterCapture(i
));
405 void Arguments::didTearOffActivation(ExecState
* exec
, JSActivation
* activation
)
407 RELEASE_ASSERT(activation
);
414 m_activation
.set(exec
->vm(), this, activation
);
418 void Arguments::tearOff(CallFrame
* callFrame
, InlineCallFrame
* inlineCallFrame
)
426 allocateRegisterArray(callFrame
->vm());
427 m_registers
= m_registerArray
.get() - CallFrame::offsetFor(1) - 1;
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
));
435 EncodedJSValue JSC_HOST_CALL
argumentsFuncIterator(ExecState
* exec
)
437 JSObject
* thisObj
= exec
->thisValue().toThis(exec
, StrictMode
).toObject(exec
);
438 Arguments
* arguments
= jsDynamicCast
<Arguments
*>(thisObj
);
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
));