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, 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 JSObject::visitChildren(thisObject, visitor);
49 if (thisObject->m_registerArray) {
50 visitor.copyLater(thisObject, ArgumentsRegisterArrayCopyToken,
51 thisObject->m_registerArray.get(), thisObject->registerArraySizeInBytes());
52 visitor.appendValues(thisObject->m_registerArray.get(), thisObject->m_numArguments);
54 if (thisObject->m_slowArgumentData) {
55 visitor.copyLater(thisObject, ArgumentsSlowArgumentDataCopyToken,
56 thisObject->m_slowArgumentData.get(), SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments));
58 visitor.append(&thisObject->m_callee);
59 visitor.append(&thisObject->m_activation);
62 void Arguments::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token)
64 Arguments* thisObject = jsCast<Arguments*>(cell);
65 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
69 case ArgumentsRegisterArrayCopyToken: {
70 WriteBarrier<Unknown>* registerArray = thisObject->m_registerArray.get();
74 if (visitor.checkIfShouldCopy(registerArray)) {
75 size_t bytes = thisObject->registerArraySizeInBytes();
76 WriteBarrier<Unknown>* newRegisterArray = static_cast<WriteBarrier<Unknown>*>(visitor.allocateNewSpace(bytes));
77 memcpy(newRegisterArray, registerArray, bytes);
78 thisObject->m_registerArray.setWithoutWriteBarrier(newRegisterArray);
79 thisObject->m_registers = newRegisterArray - CallFrame::offsetFor(1) - 1;
80 visitor.didCopy(registerArray, bytes);
85 case ArgumentsSlowArgumentDataCopyToken: {
86 SlowArgumentData* slowArgumentData = thisObject->m_slowArgumentData.get();
87 if (!slowArgumentData)
90 if (visitor.checkIfShouldCopy(slowArgumentData)) {
91 size_t bytes = SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments);
92 SlowArgumentData* newSlowArgumentData = static_cast<SlowArgumentData*>(visitor.allocateNewSpace(bytes));
93 memcpy(newSlowArgumentData, slowArgumentData, bytes);
94 thisObject->m_slowArgumentData.setWithoutWriteBarrier(newSlowArgumentData);
95 visitor.didCopy(slowArgumentData, bytes);
105 static EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState*);
107 void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t copyLength, int32_t firstVarArgOffset)
109 uint32_t length = copyLength + firstVarArgOffset;
111 if (UNLIKELY(m_overrodeLength)) {
112 length = min(get(exec, exec->propertyNames().length).toUInt32(exec), length);
113 for (unsigned i = firstVarArgOffset; i < length; i++)
114 callFrame->setArgument(i, get(exec, i));
117 ASSERT(length == this->length(exec));
118 for (size_t i = firstVarArgOffset; i < length; ++i) {
119 if (JSValue value = tryGetArgument(i))
120 callFrame->setArgument(i - firstVarArgOffset, value);
122 callFrame->setArgument(i - firstVarArgOffset, get(exec, i));
126 void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
128 if (UNLIKELY(m_overrodeLength)) {
129 unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec);
130 for (unsigned i = 0; i < length; i++)
131 args.append(get(exec, i));
134 uint32_t length = this->length(exec);
135 for (size_t i = 0; i < length; ++i) {
136 if (JSValue value = tryGetArgument(i))
139 args.append(get(exec, i));
143 bool Arguments::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned i, PropertySlot& slot)
145 Arguments* thisObject = jsCast<Arguments*>(object);
146 if (JSValue value = thisObject->tryGetArgument(i)) {
147 slot.setValue(thisObject, None, value);
151 return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
154 void Arguments::createStrictModeCallerIfNecessary(ExecState* exec)
156 if (m_overrodeCaller)
160 m_overrodeCaller = true;
161 PropertyDescriptor descriptor;
162 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(vm), DontEnum | DontDelete | Accessor);
163 methodTable(exec->vm())->defineOwnProperty(this, exec, vm.propertyNames->caller, descriptor, false);
166 void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec)
168 if (m_overrodeCallee)
172 m_overrodeCallee = true;
173 PropertyDescriptor descriptor;
174 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(vm), DontEnum | DontDelete | Accessor);
175 methodTable(exec->vm())->defineOwnProperty(this, exec, vm.propertyNames->callee, descriptor, false);
178 bool Arguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
180 Arguments* thisObject = jsCast<Arguments*>(object);
181 unsigned i = propertyName.asIndex();
182 if (JSValue value = thisObject->tryGetArgument(i)) {
183 RELEASE_ASSERT(i < PropertyName::NotAnIndex);
184 slot.setValue(thisObject, None, value);
188 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) {
189 slot.setValue(thisObject, DontEnum, jsNumber(thisObject->m_numArguments));
193 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) {
194 if (!thisObject->m_isStrictMode) {
195 slot.setValue(thisObject, DontEnum, thisObject->m_callee.get());
198 thisObject->createStrictModeCalleeIfNecessary(exec);
201 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
202 thisObject->createStrictModeCallerIfNecessary(exec);
204 if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
206 if (propertyName == exec->propertyNames().iteratorPrivateName) {
208 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
209 thisObject->JSC_NATIVE_FUNCTION(exec->propertyNames().iteratorPrivateName, argumentsFuncIterator, DontEnum, 0);
210 if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
216 void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
218 Arguments* thisObject = jsCast<Arguments*>(object);
219 for (unsigned i = 0; i < thisObject->m_numArguments; ++i) {
220 if (!thisObject->isArgument(i))
222 propertyNames.add(Identifier::from(exec, i));
224 if (shouldIncludeDontEnumProperties(mode)) {
225 propertyNames.add(exec->propertyNames().callee);
226 propertyNames.add(exec->propertyNames().length);
228 JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
231 void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
233 Arguments* thisObject = jsCast<Arguments*>(cell);
234 if (thisObject->trySetArgument(exec->vm(), i, value))
237 PutPropertySlot slot(thisObject, shouldThrow);
238 JSObject::put(thisObject, exec, Identifier::from(exec, i), value, slot);
241 void Arguments::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
243 Arguments* thisObject = jsCast<Arguments*>(cell);
244 unsigned i = propertyName.asIndex();
245 if (thisObject->trySetArgument(exec->vm(), i, value))
248 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
249 thisObject->m_overrodeLength = true;
250 thisObject->putDirect(exec->vm(), propertyName, value, DontEnum);
254 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
255 if (!thisObject->m_isStrictMode) {
256 thisObject->m_overrodeCallee = true;
257 thisObject->putDirect(exec->vm(), propertyName, value, DontEnum);
260 thisObject->createStrictModeCalleeIfNecessary(exec);
263 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
264 thisObject->createStrictModeCallerIfNecessary(exec);
266 JSObject::put(thisObject, exec, propertyName, value, slot);
269 bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
271 Arguments* thisObject = jsCast<Arguments*>(cell);
272 if (i < thisObject->m_numArguments) {
273 if (!Base::deletePropertyByIndex(cell, exec, i))
275 if (thisObject->tryDeleteArgument(exec->vm(), i))
278 return JSObject::deletePropertyByIndex(thisObject, exec, i);
281 bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
283 if (exec->vm().isInDefineOwnProperty())
284 return Base::deleteProperty(cell, exec, propertyName);
286 Arguments* thisObject = jsCast<Arguments*>(cell);
287 unsigned i = propertyName.asIndex();
288 if (i < thisObject->m_numArguments) {
289 RELEASE_ASSERT(i < PropertyName::NotAnIndex);
290 if (!Base::deleteProperty(cell, exec, propertyName))
292 if (thisObject->tryDeleteArgument(exec->vm(), i))
296 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
297 thisObject->m_overrodeLength = true;
301 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
302 if (!thisObject->m_isStrictMode) {
303 thisObject->m_overrodeCallee = true;
306 thisObject->createStrictModeCalleeIfNecessary(exec);
309 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
310 thisObject->createStrictModeCallerIfNecessary(exec);
312 return JSObject::deleteProperty(thisObject, exec, propertyName);
315 bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
317 Arguments* thisObject = jsCast<Arguments*>(object);
318 unsigned i = propertyName.asIndex();
319 if (i < thisObject->m_numArguments) {
320 RELEASE_ASSERT(i < PropertyName::NotAnIndex);
321 // If the property is not yet present on the object, and is not yet marked as deleted, then add it now.
322 PropertySlot slot(thisObject);
323 if (!thisObject->isDeletedArgument(i) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) {
324 JSValue value = thisObject->tryGetArgument(i);
326 object->putDirectMayBeIndex(exec, propertyName, value);
328 if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
331 // From ES 5.1, 10.6 Arguments Object
332 // 5. If the value of isMapped is not undefined, then
333 if (thisObject->isArgument(i)) {
334 // a. If IsAccessorDescriptor(Desc) is true, then
335 if (descriptor.isAccessorDescriptor()) {
336 // i. Call the [[Delete]] internal method of map passing P, and false as the arguments.
337 thisObject->tryDeleteArgument(exec->vm(), i);
339 // i. If Desc.[[Value]] is present, then
340 // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments.
341 if (descriptor.value())
342 thisObject->trySetArgument(exec->vm(), i, descriptor.value());
343 // ii. If Desc.[[Writable]] is present and its value is false, then
344 // 1. Call the [[Delete]] internal method of map passing P and false as arguments.
345 if (descriptor.writablePresent() && !descriptor.writable())
346 thisObject->tryDeleteArgument(exec->vm(), i);
352 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
353 thisObject->putDirect(exec->vm(), propertyName, jsNumber(thisObject->m_numArguments), DontEnum);
354 thisObject->m_overrodeLength = true;
355 } else if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
356 thisObject->putDirect(exec->vm(), propertyName, thisObject->m_callee.get(), DontEnum);
357 thisObject->m_overrodeCallee = true;
358 } else if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
359 thisObject->createStrictModeCallerIfNecessary(exec);
361 return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
364 void Arguments::allocateRegisterArray(VM& vm)
366 ASSERT(!m_registerArray);
368 if (!vm.heap.tryAllocateStorage(this, registerArraySizeInBytes(), &backingStore))
369 RELEASE_ASSERT_NOT_REACHED();
370 m_registerArray.set(vm, this, static_cast<WriteBarrier<Unknown>*>(backingStore));
373 void Arguments::tearOff(CallFrame* callFrame)
381 // Must be called for the same call frame from which it was created.
382 ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == m_registers);
384 allocateRegisterArray(callFrame->vm());
385 m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1;
387 // If we have a captured argument that logically aliases activation storage,
388 // but we optimize away the activation, the argument needs to tear off into
389 // our storage. The simplest way to do this is to revert it to Normal status.
390 if (m_slowArgumentData && !m_activation) {
391 for (size_t i = 0; i < m_numArguments; ++i) {
392 if (m_slowArgumentData->slowArguments()[i].status != SlowArgument::Captured)
394 m_slowArgumentData->slowArguments()[i].status = SlowArgument::Normal;
395 m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i);
399 for (size_t i = 0; i < m_numArguments; ++i)
400 trySetArgument(callFrame->vm(), i, callFrame->argumentAfterCapture(i));
403 void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation)
405 RELEASE_ASSERT(activation);
412 m_activation.set(exec->vm(), this, activation);
416 void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
424 allocateRegisterArray(callFrame->vm());
425 m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1;
427 for (size_t i = 0; i < m_numArguments; ++i) {
428 ValueRecovery& recovery = inlineCallFrame->arguments[i + 1];
429 trySetArgument(callFrame->vm(), i, recovery.recover(callFrame));
433 EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState* exec)
435 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
436 Arguments* arguments = jsDynamicCast<Arguments*>(thisObj);
438 return JSValue::encode(throwTypeError(exec, ASCIILiteral("Attempted to use Arguments iterator on non-Arguments object")));
439 return JSValue::encode(JSArgumentsIterator::create(exec->vm(), exec->callee()->globalObject()->argumentsIteratorStructure(), arguments));