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 "JSActivation.h"
29 #include "JSFunction.h"
30 #include "JSGlobalObject.h"
31 #include "Operations.h"
37 const ClassInfo
Arguments::s_info
= { "Arguments", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(Arguments
) };
39 void Arguments::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
41 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
42 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
43 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
44 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
45 JSObject::visitChildren(thisObject
, visitor
);
47 if (thisObject
->m_registerArray
)
48 visitor
.appendValues(thisObject
->m_registerArray
.get(), thisObject
->m_numArguments
);
49 visitor
.append(&thisObject
->m_callee
);
50 visitor
.append(&thisObject
->m_activation
);
53 void Arguments::destroy(JSCell
* cell
)
55 static_cast<Arguments
*>(cell
)->Arguments::~Arguments();
58 void Arguments::copyToArguments(ExecState
* exec
, CallFrame
* callFrame
, uint32_t length
)
60 if (UNLIKELY(m_overrodeLength
)) {
61 length
= min(get(exec
, exec
->propertyNames().length
).toUInt32(exec
), length
);
62 for (unsigned i
= 0; i
< length
; i
++)
63 callFrame
->setArgument(i
, get(exec
, i
));
66 ASSERT(length
== this->length(exec
));
67 for (size_t i
= 0; i
< length
; ++i
) {
68 if (JSValue value
= tryGetArgument(i
))
69 callFrame
->setArgument(i
, value
);
71 callFrame
->setArgument(i
, get(exec
, i
));
75 void Arguments::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
77 if (UNLIKELY(m_overrodeLength
)) {
78 unsigned length
= get(exec
, exec
->propertyNames().length
).toUInt32(exec
);
79 for (unsigned i
= 0; i
< length
; i
++)
80 args
.append(get(exec
, i
));
83 uint32_t length
= this->length(exec
);
84 for (size_t i
= 0; i
< length
; ++i
) {
85 if (JSValue value
= tryGetArgument(i
))
88 args
.append(get(exec
, i
));
92 bool Arguments::getOwnPropertySlotByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
94 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
95 if (JSValue value
= thisObject
->tryGetArgument(i
)) {
100 return JSObject::getOwnPropertySlot(thisObject
, exec
, Identifier(exec
, String::number(i
)), slot
);
103 void Arguments::createStrictModeCallerIfNecessary(ExecState
* exec
)
105 if (m_overrodeCaller
)
108 m_overrodeCaller
= true;
109 PropertyDescriptor descriptor
;
110 descriptor
.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec
), DontEnum
| DontDelete
| Accessor
);
111 methodTable()->defineOwnProperty(this, exec
, exec
->propertyNames().caller
, descriptor
, false);
114 void Arguments::createStrictModeCalleeIfNecessary(ExecState
* exec
)
116 if (m_overrodeCallee
)
119 m_overrodeCallee
= true;
120 PropertyDescriptor descriptor
;
121 descriptor
.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec
), DontEnum
| DontDelete
| Accessor
);
122 methodTable()->defineOwnProperty(this, exec
, exec
->propertyNames().callee
, descriptor
, false);
125 bool Arguments::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
127 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
128 unsigned i
= propertyName
.asIndex();
129 if (JSValue value
= thisObject
->tryGetArgument(i
)) {
130 RELEASE_ASSERT(i
< PropertyName::NotAnIndex
);
131 slot
.setValue(value
);
135 if (propertyName
== exec
->propertyNames().length
&& LIKELY(!thisObject
->m_overrodeLength
)) {
136 slot
.setValue(jsNumber(thisObject
->m_numArguments
));
140 if (propertyName
== exec
->propertyNames().callee
&& LIKELY(!thisObject
->m_overrodeCallee
)) {
141 if (!thisObject
->m_isStrictMode
) {
142 slot
.setValue(thisObject
->m_callee
.get());
145 thisObject
->createStrictModeCalleeIfNecessary(exec
);
148 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
149 thisObject
->createStrictModeCallerIfNecessary(exec
);
151 return JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
154 bool Arguments::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
156 Arguments
* thisObject
= jsCast
<Arguments
*>(object
);
157 unsigned i
= propertyName
.asIndex();
158 if (JSValue value
= thisObject
->tryGetArgument(i
)) {
159 RELEASE_ASSERT(i
< PropertyName::NotAnIndex
);
160 descriptor
.setDescriptor(value
, None
);
164 if (propertyName
== exec
->propertyNames().length
&& LIKELY(!thisObject
->m_overrodeLength
)) {
165 descriptor
.setDescriptor(jsNumber(thisObject
->m_numArguments
), DontEnum
);
169 if (propertyName
== exec
->propertyNames().callee
&& LIKELY(!thisObject
->m_overrodeCallee
)) {
170 if (!thisObject
->m_isStrictMode
) {
171 descriptor
.setDescriptor(thisObject
->m_callee
.get(), DontEnum
);
174 thisObject
->createStrictModeCalleeIfNecessary(exec
);
177 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
178 thisObject
->createStrictModeCallerIfNecessary(exec
);
180 return JSObject::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
183 void Arguments::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
185 Arguments
* thisObject
= jsCast
<Arguments
*>(object
);
186 for (unsigned i
= 0; i
< thisObject
->m_numArguments
; ++i
) {
187 if (!thisObject
->isArgument(i
))
189 propertyNames
.add(Identifier(exec
, String::number(i
)));
191 if (mode
== IncludeDontEnumProperties
) {
192 propertyNames
.add(exec
->propertyNames().callee
);
193 propertyNames
.add(exec
->propertyNames().length
);
195 JSObject::getOwnPropertyNames(thisObject
, exec
, propertyNames
, mode
);
198 void Arguments::putByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
200 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
201 if (thisObject
->trySetArgument(exec
->vm(), i
, value
))
204 PutPropertySlot
slot(shouldThrow
);
205 JSObject::put(thisObject
, exec
, Identifier(exec
, String::number(i
)), value
, slot
);
208 void Arguments::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
210 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
211 unsigned i
= propertyName
.asIndex();
212 if (thisObject
->trySetArgument(exec
->vm(), i
, value
))
215 if (propertyName
== exec
->propertyNames().length
&& !thisObject
->m_overrodeLength
) {
216 thisObject
->m_overrodeLength
= true;
217 thisObject
->putDirect(exec
->vm(), propertyName
, value
, DontEnum
);
221 if (propertyName
== exec
->propertyNames().callee
&& !thisObject
->m_overrodeCallee
) {
222 if (!thisObject
->m_isStrictMode
) {
223 thisObject
->m_overrodeCallee
= true;
224 thisObject
->putDirect(exec
->vm(), propertyName
, value
, DontEnum
);
227 thisObject
->createStrictModeCalleeIfNecessary(exec
);
230 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
231 thisObject
->createStrictModeCallerIfNecessary(exec
);
233 JSObject::put(thisObject
, exec
, propertyName
, value
, slot
);
236 bool Arguments::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
)
238 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
239 if (i
< thisObject
->m_numArguments
) {
240 if (!Base::deletePropertyByIndex(cell
, exec
, i
))
242 if (thisObject
->tryDeleteArgument(i
))
245 return JSObject::deletePropertyByIndex(thisObject
, exec
, i
);
248 bool Arguments::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
250 if (exec
->vm().isInDefineOwnProperty())
251 return Base::deleteProperty(cell
, exec
, propertyName
);
253 Arguments
* thisObject
= jsCast
<Arguments
*>(cell
);
254 unsigned i
= propertyName
.asIndex();
255 if (i
< thisObject
->m_numArguments
) {
256 RELEASE_ASSERT(i
< PropertyName::NotAnIndex
);
257 if (!Base::deleteProperty(cell
, exec
, propertyName
))
259 if (thisObject
->tryDeleteArgument(i
))
263 if (propertyName
== exec
->propertyNames().length
&& !thisObject
->m_overrodeLength
) {
264 thisObject
->m_overrodeLength
= true;
268 if (propertyName
== exec
->propertyNames().callee
&& !thisObject
->m_overrodeCallee
) {
269 if (!thisObject
->m_isStrictMode
) {
270 thisObject
->m_overrodeCallee
= true;
273 thisObject
->createStrictModeCalleeIfNecessary(exec
);
276 if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
277 thisObject
->createStrictModeCallerIfNecessary(exec
);
279 return JSObject::deleteProperty(thisObject
, exec
, propertyName
);
282 bool Arguments::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
, bool shouldThrow
)
284 Arguments
* thisObject
= jsCast
<Arguments
*>(object
);
285 unsigned i
= propertyName
.asIndex();
286 if (i
< thisObject
->m_numArguments
) {
287 RELEASE_ASSERT(i
< PropertyName::NotAnIndex
);
288 // If the property is not yet present on the object, and is not yet marked as deleted, then add it now.
290 if (!thisObject
->isDeletedArgument(i
) && !JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
)) {
291 JSValue value
= thisObject
->tryGetArgument(i
);
293 object
->putDirectMayBeIndex(exec
, propertyName
, value
);
295 if (!Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
))
298 // From ES 5.1, 10.6 Arguments Object
299 // 5. If the value of isMapped is not undefined, then
300 if (thisObject
->isArgument(i
)) {
301 // a. If IsAccessorDescriptor(Desc) is true, then
302 if (descriptor
.isAccessorDescriptor()) {
303 // i. Call the [[Delete]] internal method of map passing P, and false as the arguments.
304 thisObject
->tryDeleteArgument(i
);
306 // i. If Desc.[[Value]] is present, then
307 // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments.
308 if (descriptor
.value())
309 thisObject
->trySetArgument(exec
->vm(), i
, descriptor
.value());
310 // ii. If Desc.[[Writable]] is present and its value is false, then
311 // 1. Call the [[Delete]] internal method of map passing P and false as arguments.
312 if (descriptor
.writablePresent() && !descriptor
.writable())
313 thisObject
->tryDeleteArgument(i
);
319 if (propertyName
== exec
->propertyNames().length
&& !thisObject
->m_overrodeLength
) {
320 thisObject
->putDirect(exec
->vm(), propertyName
, jsNumber(thisObject
->m_numArguments
), DontEnum
);
321 thisObject
->m_overrodeLength
= true;
322 } else if (propertyName
== exec
->propertyNames().callee
&& !thisObject
->m_overrodeCallee
) {
323 thisObject
->putDirect(exec
->vm(), propertyName
, thisObject
->m_callee
.get(), DontEnum
);
324 thisObject
->m_overrodeCallee
= true;
325 } else if (propertyName
== exec
->propertyNames().caller
&& thisObject
->m_isStrictMode
)
326 thisObject
->createStrictModeCallerIfNecessary(exec
);
328 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
);
331 void Arguments::tearOff(CallFrame
* callFrame
)
339 // Must be called for the same call frame from which it was created.
340 ASSERT(bitwise_cast
<WriteBarrier
<Unknown
>*>(callFrame
) == m_registers
);
342 m_registerArray
= adoptArrayPtr(new WriteBarrier
<Unknown
>[m_numArguments
]);
343 m_registers
= m_registerArray
.get() + CallFrame::offsetFor(m_numArguments
+ 1);
345 // If we have a captured argument that logically aliases activation storage,
346 // but we optimize away the activation, the argument needs to tear off into
347 // our storage. The simplest way to do this is to revert it to Normal status.
348 if (m_slowArguments
&& !m_activation
) {
349 for (size_t i
= 0; i
< m_numArguments
; ++i
) {
350 if (m_slowArguments
[i
].status
!= SlowArgument::Captured
)
352 m_slowArguments
[i
].status
= SlowArgument::Normal
;
353 m_slowArguments
[i
].index
= CallFrame::argumentOffset(i
);
357 if (!callFrame
->isInlineCallFrame()) {
358 for (size_t i
= 0; i
< m_numArguments
; ++i
)
359 trySetArgument(callFrame
->vm(), i
, callFrame
->argumentAfterCapture(i
));
363 tearOffForInlineCallFrame(
364 callFrame
->vm(), callFrame
->registers(), callFrame
->inlineCallFrame());
367 void Arguments::didTearOffActivation(ExecState
* exec
, JSActivation
* activation
)
369 RELEASE_ASSERT(activation
);
376 m_activation
.set(exec
->vm(), this, activation
);
380 void Arguments::tearOff(CallFrame
* callFrame
, InlineCallFrame
* inlineCallFrame
)
388 m_registerArray
= adoptArrayPtr(new WriteBarrier
<Unknown
>[m_numArguments
]);
389 m_registers
= m_registerArray
.get() + CallFrame::offsetFor(m_numArguments
+ 1);
391 tearOffForInlineCallFrame(
392 callFrame
->vm(), callFrame
->registers() + inlineCallFrame
->stackOffset
,
396 void Arguments::tearOffForInlineCallFrame(VM
& vm
, Register
* registers
, InlineCallFrame
* inlineCallFrame
)
398 for (size_t i
= 0; i
< m_numArguments
; ++i
) {
399 ValueRecovery
& recovery
= inlineCallFrame
->arguments
[i
+ 1];
400 // In the future we'll support displaced recoveries (indicating that the
401 // argument was flushed to a different location), but for now we don't do
402 // that so this code will fail if that were to happen. On the other hand,
403 // it's much less likely that we'll support in-register recoveries since
404 // this code does not (easily) have access to registers.
406 Register
* location
= ®isters
[CallFrame::argumentOffset(i
)];
407 switch (recovery
.technique()) {
408 case AlreadyInJSStack
:
409 value
= location
->jsValue();
411 case AlreadyInJSStackAsUnboxedInt32
:
412 value
= jsNumber(location
->unboxedInt32());
414 case AlreadyInJSStackAsUnboxedCell
:
415 value
= location
->unboxedCell();
417 case AlreadyInJSStackAsUnboxedBoolean
:
418 value
= jsBoolean(location
->unboxedBoolean());
420 case AlreadyInJSStackAsUnboxedDouble
:
422 value
= jsNumber(*bitwise_cast
<double*>(location
));
424 value
= location
->jsValue();
428 value
= recovery
.constant();
431 RELEASE_ASSERT_NOT_REACHED();
434 trySetArgument(vm
, i
, value
);