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 "JSActivation.h"
29#include "JSFunction.h"
30#include "JSGlobalObject.h"
31
32using namespace std;
33
34namespace JSC {
35
36ASSERT_CLASS_FITS_IN_CELL(Arguments);
37
38const ClassInfo Arguments::s_info = { "Arguments", &JSNonFinalObject::s_info, 0, 0 };
39
40Arguments::~Arguments()
41{
42    if (d->extraArguments != d->extraArgumentsFixedBuffer)
43        delete [] d->extraArguments;
44}
45
46void Arguments::markChildren(MarkStack& markStack)
47{
48    JSObject::markChildren(markStack);
49
50    if (d->registerArray)
51        markStack.appendValues(d->registerArray.get(), d->numParameters);
52
53    if (d->extraArguments) {
54        unsigned numExtraArguments = d->numArguments - d->numParameters;
55        markStack.appendValues(d->extraArguments, numExtraArguments);
56    }
57
58    markStack.append(&d->callee);
59
60    if (d->activation)
61        markStack.append(&d->activation);
62}
63
64void Arguments::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize)
65{
66    if (UNLIKELY(d->overrodeLength)) {
67        unsigned length = min(get(exec, exec->propertyNames().length).toUInt32(exec), maxSize);
68        for (unsigned i = 0; i < length; i++)
69            buffer[i] = get(exec, i);
70        return;
71    }
72
73    if (LIKELY(!d->deletedArguments)) {
74        unsigned parametersLength = min(min(d->numParameters, d->numArguments), maxSize);
75        unsigned i = 0;
76        for (; i < parametersLength; ++i)
77            buffer[i] = d->registers[d->firstParameterIndex + i].get();
78        for (; i < d->numArguments; ++i)
79            buffer[i] = d->extraArguments[i - d->numParameters].get();
80        return;
81    }
82
83    unsigned parametersLength = min(min(d->numParameters, d->numArguments), maxSize);
84    unsigned i = 0;
85    for (; i < parametersLength; ++i) {
86        if (!d->deletedArguments[i])
87            buffer[i] = d->registers[d->firstParameterIndex + i].get();
88        else
89            buffer[i] = get(exec, i);
90    }
91    for (; i < d->numArguments; ++i) {
92        if (!d->deletedArguments[i])
93            buffer[i] = d->extraArguments[i - d->numParameters].get();
94        else
95            buffer[i] = get(exec, i);
96    }
97}
98
99void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
100{
101    if (UNLIKELY(d->overrodeLength)) {
102        unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec);
103        for (unsigned i = 0; i < length; i++)
104            args.append(get(exec, i));
105        return;
106    }
107
108    if (LIKELY(!d->deletedArguments)) {
109        if (LIKELY(!d->numParameters)) {
110            args.initialize(d->extraArguments, d->numArguments);
111            return;
112        }
113
114        if (d->numParameters == d->numArguments) {
115            args.initialize(&d->registers[d->firstParameterIndex], d->numArguments);
116            return;
117        }
118
119        unsigned parametersLength = min(d->numParameters, d->numArguments);
120        unsigned i = 0;
121        for (; i < parametersLength; ++i)
122            args.append(d->registers[d->firstParameterIndex + i].get());
123        for (; i < d->numArguments; ++i)
124            args.append(d->extraArguments[i - d->numParameters].get());
125        return;
126    }
127
128    unsigned parametersLength = min(d->numParameters, d->numArguments);
129    unsigned i = 0;
130    for (; i < parametersLength; ++i) {
131        if (!d->deletedArguments[i])
132            args.append(d->registers[d->firstParameterIndex + i].get());
133        else
134            args.append(get(exec, i));
135    }
136    for (; i < d->numArguments; ++i) {
137        if (!d->deletedArguments[i])
138            args.append(d->extraArguments[i - d->numParameters].get());
139        else
140            args.append(get(exec, i));
141    }
142}
143
144bool Arguments::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
145{
146    if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
147        if (i < d->numParameters) {
148            slot.setValue(d->registers[d->firstParameterIndex + i].get());
149        } else
150            slot.setValue(d->extraArguments[i - d->numParameters].get());
151        return true;
152    }
153
154    return JSObject::getOwnPropertySlot(exec, Identifier(exec, UString::number(i)), slot);
155}
156
157void Arguments::createStrictModeCallerIfNecessary(ExecState* exec)
158{
159    if (d->overrodeCaller)
160        return;
161
162    d->overrodeCaller = true;
163    PropertyDescriptor descriptor;
164    JSValue thrower = createTypeErrorFunction(exec, "Unable to access caller of strict mode function");
165    descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter);
166    defineOwnProperty(exec, exec->propertyNames().caller, descriptor, false);
167}
168
169void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec)
170{
171    if (d->overrodeCallee)
172        return;
173
174    d->overrodeCallee = true;
175    PropertyDescriptor descriptor;
176    JSValue thrower = createTypeErrorFunction(exec, "Unable to access callee of strict mode function");
177    descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter);
178    defineOwnProperty(exec, exec->propertyNames().callee, descriptor, false);
179}
180
181bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
182{
183    bool isArrayIndex;
184    unsigned i = propertyName.toArrayIndex(isArrayIndex);
185    if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
186        if (i < d->numParameters) {
187            slot.setValue(d->registers[d->firstParameterIndex + i].get());
188        } else
189            slot.setValue(d->extraArguments[i - d->numParameters].get());
190        return true;
191    }
192
193    if (propertyName == exec->propertyNames().length && LIKELY(!d->overrodeLength)) {
194        slot.setValue(jsNumber(d->numArguments));
195        return true;
196    }
197
198    if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) {
199        if (!d->isStrictMode) {
200            slot.setValue(d->callee.get());
201            return true;
202        }
203        createStrictModeCalleeIfNecessary(exec);
204    }
205
206    if (propertyName == exec->propertyNames().caller && d->isStrictMode)
207        createStrictModeCallerIfNecessary(exec);
208
209    return JSObject::getOwnPropertySlot(exec, propertyName, slot);
210}
211
212bool Arguments::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
213{
214    bool isArrayIndex;
215    unsigned i = propertyName.toArrayIndex(isArrayIndex);
216    if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
217        if (i < d->numParameters) {
218            descriptor.setDescriptor(d->registers[d->firstParameterIndex + i].get(), DontEnum);
219        } else
220            descriptor.setDescriptor(d->extraArguments[i - d->numParameters].get(), DontEnum);
221        return true;
222    }
223
224    if (propertyName == exec->propertyNames().length && LIKELY(!d->overrodeLength)) {
225        descriptor.setDescriptor(jsNumber(d->numArguments), DontEnum);
226        return true;
227    }
228
229    if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) {
230        if (!d->isStrictMode) {
231            descriptor.setDescriptor(d->callee.get(), DontEnum);
232            return true;
233        }
234        createStrictModeCalleeIfNecessary(exec);
235    }
236
237    if (propertyName == exec->propertyNames().caller && d->isStrictMode)
238        createStrictModeCallerIfNecessary(exec);
239
240    return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
241}
242
243void Arguments::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
244{
245    if (mode == IncludeDontEnumProperties) {
246        for (unsigned i = 0; i < d->numArguments; ++i) {
247            if (!d->deletedArguments || !d->deletedArguments[i])
248                propertyNames.add(Identifier(exec, UString::number(i)));
249        }
250        propertyNames.add(exec->propertyNames().callee);
251        propertyNames.add(exec->propertyNames().length);
252    }
253    JSObject::getOwnPropertyNames(exec, propertyNames, mode);
254}
255
256void Arguments::put(ExecState* exec, unsigned i, JSValue value)
257{
258    if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
259        if (i < d->numParameters)
260            d->registers[d->firstParameterIndex + i].set(exec->globalData(), d->activation ? static_cast<JSCell*>(d->activation.get()) : static_cast<JSCell*>(this), value);
261        else
262            d->extraArguments[i - d->numParameters].set(exec->globalData(), this, value);
263        return;
264    }
265
266    PutPropertySlot slot;
267    JSObject::put(exec, Identifier(exec, UString::number(i)), value, slot);
268}
269
270void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
271{
272    bool isArrayIndex;
273    unsigned i = propertyName.toArrayIndex(isArrayIndex);
274    if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
275        if (i < d->numParameters)
276            d->registers[d->firstParameterIndex + i].set(exec->globalData(), d->activation ? static_cast<JSCell*>(d->activation.get()) : static_cast<JSCell*>(this), value);
277        else
278            d->extraArguments[i - d->numParameters].set(exec->globalData(), this, value);
279        return;
280    }
281
282    if (propertyName == exec->propertyNames().length && !d->overrodeLength) {
283        d->overrodeLength = true;
284        putDirect(exec->globalData(), propertyName, value, DontEnum);
285        return;
286    }
287
288    if (propertyName == exec->propertyNames().callee && !d->overrodeCallee) {
289        if (!d->isStrictMode) {
290            d->overrodeCallee = true;
291            putDirect(exec->globalData(), propertyName, value, DontEnum);
292            return;
293        }
294        createStrictModeCalleeIfNecessary(exec);
295    }
296
297    if (propertyName == exec->propertyNames().caller && d->isStrictMode)
298        createStrictModeCallerIfNecessary(exec);
299
300    JSObject::put(exec, propertyName, value, slot);
301}
302
303bool Arguments::deleteProperty(ExecState* exec, unsigned i)
304{
305    if (i < d->numArguments) {
306        if (!d->deletedArguments) {
307            d->deletedArguments = adoptArrayPtr(new bool[d->numArguments]);
308            memset(d->deletedArguments.get(), 0, sizeof(bool) * d->numArguments);
309        }
310        if (!d->deletedArguments[i]) {
311            d->deletedArguments[i] = true;
312            return true;
313        }
314    }
315
316    return JSObject::deleteProperty(exec, Identifier(exec, UString::number(i)));
317}
318
319bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName)
320{
321    bool isArrayIndex;
322    unsigned i = propertyName.toArrayIndex(isArrayIndex);
323    if (isArrayIndex && i < d->numArguments) {
324        if (!d->deletedArguments) {
325            d->deletedArguments = adoptArrayPtr(new bool[d->numArguments]);
326            memset(d->deletedArguments.get(), 0, sizeof(bool) * d->numArguments);
327        }
328        if (!d->deletedArguments[i]) {
329            d->deletedArguments[i] = true;
330            return true;
331        }
332    }
333
334    if (propertyName == exec->propertyNames().length && !d->overrodeLength) {
335        d->overrodeLength = true;
336        return true;
337    }
338
339    if (propertyName == exec->propertyNames().callee && !d->overrodeCallee) {
340        if (!d->isStrictMode) {
341            d->overrodeCallee = true;
342            return true;
343        }
344        createStrictModeCalleeIfNecessary(exec);
345    }
346
347    if (propertyName == exec->propertyNames().caller && !d->isStrictMode)
348        createStrictModeCallerIfNecessary(exec);
349
350    return JSObject::deleteProperty(exec, propertyName);
351}
352
353} // namespace JSC
354