f34055a306214e6afad36839481d7cf11a36e8e9
[WebKit-https.git] / Source / JavaScriptCore / 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, 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     JSObject::visitChildren(thisObject, visitor);
48
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);
53     }
54     if (thisObject->m_slowArgumentData) {
55         visitor.copyLater(thisObject, ArgumentsSlowArgumentDataCopyToken,
56             thisObject->m_slowArgumentData.get(), SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments));
57     }
58     visitor.append(&thisObject->m_callee);
59     visitor.append(&thisObject->m_activation);
60 }
61
62 void Arguments::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token)
63 {
64     Arguments* thisObject = jsCast<Arguments*>(cell);
65     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
66     
67
68     switch (token) {
69     case ArgumentsRegisterArrayCopyToken: {
70         WriteBarrier<Unknown>* registerArray = thisObject->m_registerArray.get();
71         if (!registerArray)
72             return;
73
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);
81         }
82         return;
83     }
84
85     case ArgumentsSlowArgumentDataCopyToken: {
86         SlowArgumentData* slowArgumentData = thisObject->m_slowArgumentData.get();
87         if (!slowArgumentData)
88             return;
89
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);
96         }
97         return;
98     }
99
100     default:
101         return;
102     }
103 }
104     
105 static EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState*);
106
107 void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t copyLength, int32_t firstVarArgOffset)
108 {
109     uint32_t length = copyLength + firstVarArgOffset;
110
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));
115         return;
116     }
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);
121         else
122             callFrame->setArgument(i - firstVarArgOffset, get(exec, i));
123     }
124 }
125
126 void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
127 {
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)); 
132         return;
133     }
134     uint32_t length = this->length(exec);
135     for (size_t i = 0; i < length; ++i) {
136         if (JSValue value = tryGetArgument(i))
137             args.append(value);
138         else
139             args.append(get(exec, i));
140     }
141 }
142
143 bool Arguments::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned i, PropertySlot& slot)
144 {
145     Arguments* thisObject = jsCast<Arguments*>(object);
146     if (JSValue value = thisObject->tryGetArgument(i)) {
147         slot.setValue(thisObject, None, value);
148         return true;
149     }
150
151     return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
152 }
153     
154 void Arguments::createStrictModeCallerIfNecessary(ExecState* exec)
155 {
156     if (m_overrodeCaller)
157         return;
158
159     VM& vm = exec->vm();
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);
164 }
165
166 void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec)
167 {
168     if (m_overrodeCallee)
169         return;
170
171     VM& vm = exec->vm();
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);
176 }
177
178 bool Arguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
179 {
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);
185         return true;
186     }
187
188     if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) {
189         slot.setValue(thisObject, DontEnum, jsNumber(thisObject->m_numArguments));
190         return true;
191     }
192
193     if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) {
194         if (!thisObject->m_isStrictMode) {
195             slot.setValue(thisObject, DontEnum, thisObject->m_callee.get());
196             return true;
197         }
198         thisObject->createStrictModeCalleeIfNecessary(exec);
199     }
200
201     if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
202         thisObject->createStrictModeCallerIfNecessary(exec);
203
204     if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
205         return true;
206     if (propertyName == exec->propertyNames().iteratorPrivateName) {
207         VM& vm = exec->vm();
208         JSGlobalObject* globalObject = exec->lexicalGlobalObject();
209         thisObject->JSC_NATIVE_FUNCTION(exec->propertyNames().iteratorPrivateName, argumentsFuncIterator, DontEnum, 0);
210         if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
211             return true;
212     }
213     return false;
214 }
215
216 void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
217 {
218     Arguments* thisObject = jsCast<Arguments*>(object);
219     for (unsigned i = 0; i < thisObject->m_numArguments; ++i) {
220         if (!thisObject->isArgument(i))
221             continue;
222         propertyNames.add(Identifier::from(exec, i));
223     }
224     if (shouldIncludeDontEnumProperties(mode)) {
225         propertyNames.add(exec->propertyNames().callee);
226         propertyNames.add(exec->propertyNames().length);
227     }
228     JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
229 }
230
231 void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
232 {
233     Arguments* thisObject = jsCast<Arguments*>(cell);
234     if (thisObject->trySetArgument(exec->vm(), i, value))
235         return;
236
237     PutPropertySlot slot(thisObject, shouldThrow);
238     JSObject::put(thisObject, exec, Identifier::from(exec, i), value, slot);
239 }
240
241 void Arguments::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
242 {
243     Arguments* thisObject = jsCast<Arguments*>(cell);
244     unsigned i = propertyName.asIndex();
245     if (thisObject->trySetArgument(exec->vm(), i, value))
246         return;
247
248     if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
249         thisObject->m_overrodeLength = true;
250         thisObject->putDirect(exec->vm(), propertyName, value, DontEnum);
251         return;
252     }
253
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);
258             return;
259         }
260         thisObject->createStrictModeCalleeIfNecessary(exec);
261     }
262
263     if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
264         thisObject->createStrictModeCallerIfNecessary(exec);
265
266     JSObject::put(thisObject, exec, propertyName, value, slot);
267 }
268
269 bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) 
270 {
271     Arguments* thisObject = jsCast<Arguments*>(cell);
272     if (i < thisObject->m_numArguments) {
273         if (!Base::deletePropertyByIndex(cell, exec, i))
274             return false;
275         if (thisObject->tryDeleteArgument(exec->vm(), i))
276             return true;
277     }
278     return JSObject::deletePropertyByIndex(thisObject, exec, i);
279 }
280
281 bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) 
282 {
283     if (exec->vm().isInDefineOwnProperty())
284         return Base::deleteProperty(cell, exec, propertyName);
285
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))
291             return false;
292         if (thisObject->tryDeleteArgument(exec->vm(), i))
293             return true;
294     }
295
296     if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) {
297         thisObject->m_overrodeLength = true;
298         return true;
299     }
300
301     if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) {
302         if (!thisObject->m_isStrictMode) {
303             thisObject->m_overrodeCallee = true;
304             return true;
305         }
306         thisObject->createStrictModeCalleeIfNecessary(exec);
307     }
308     
309     if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode)
310         thisObject->createStrictModeCallerIfNecessary(exec);
311
312     return JSObject::deleteProperty(thisObject, exec, propertyName);
313 }
314
315 bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
316 {
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);
325             ASSERT(value);
326             object->putDirectMayBeIndex(exec, propertyName, value);
327         }
328         if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
329             return false;
330
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);
338             } else { // b. Else
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);
347             }
348         }
349         return true;
350     }
351
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);
360
361     return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
362 }
363
364 void Arguments::allocateRegisterArray(VM& vm)
365 {
366     ASSERT(!m_registerArray);
367     void* backingStore;
368     if (!vm.heap.tryAllocateStorage(this, registerArraySizeInBytes(), &backingStore))
369         RELEASE_ASSERT_NOT_REACHED();
370     m_registerArray.set(vm, this, static_cast<WriteBarrier<Unknown>*>(backingStore));
371 }
372
373 void Arguments::tearOff(CallFrame* callFrame)
374 {
375     if (isTornOff())
376         return;
377
378     if (!m_numArguments)
379         return;
380
381     // Must be called for the same call frame from which it was created.
382     ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == m_registers);
383     
384     allocateRegisterArray(callFrame->vm());
385     m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1;
386
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)
393                 continue;
394             m_slowArgumentData->slowArguments()[i].status = SlowArgument::Normal;
395             m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i);
396         }
397     }
398
399     for (size_t i = 0; i < m_numArguments; ++i)
400         trySetArgument(callFrame->vm(), i, callFrame->argumentAfterCapture(i));
401 }
402
403 void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation)
404 {
405     RELEASE_ASSERT(activation);
406     if (isTornOff())
407         return;
408
409     if (!m_numArguments)
410         return;
411     
412     m_activation.set(exec->vm(), this, activation);
413     tearOff(exec);
414 }
415
416 void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
417 {
418     if (isTornOff())
419         return;
420     
421     if (!m_numArguments)
422         return;
423     
424     allocateRegisterArray(callFrame->vm());
425     m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1;
426
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));
430     }
431 }
432     
433 EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState* exec)
434 {
435     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
436     Arguments* arguments = jsDynamicCast<Arguments*>(thisObj);
437     if (!arguments)
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));
440 }
441
442
443 } // namespace JSC