1/*
2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "JSActivation.h"
31
32#include "Arguments.h"
33#include "Interpreter.h"
34#include "JSFunction.h"
35#include "Operations.h"
36
37using namespace std;
38
39namespace JSC {
40
41const ClassInfo JSActivation::s_info = { "JSActivation", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSActivation) };
42
43void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor)
44{
45    JSActivation* thisObject = jsCast<JSActivation*>(cell);
46    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
47    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
48    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
49    Base::visitChildren(thisObject, visitor);
50
51    // No need to mark our registers if they're still in the JSStack.
52    if (!thisObject->isTornOff())
53        return;
54
55    for (int i = 0; i < thisObject->symbolTable()->captureCount(); ++i)
56        visitor.append(&thisObject->storage()[i]);
57}
58
59inline bool JSActivation::symbolTableGet(PropertyName propertyName, PropertySlot& slot)
60{
61    SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.publicName());
62    if (entry.isNull())
63        return false;
64
65    // Defend against the inspector asking for a var after it has been optimized out.
66    if (isTornOff() && !isValid(entry))
67        return false;
68
69    slot.setValue(registerAt(entry.getIndex()).get());
70    return true;
71}
72
73inline bool JSActivation::symbolTableGet(PropertyName propertyName, PropertyDescriptor& descriptor)
74{
75    SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.publicName());
76    if (entry.isNull())
77        return false;
78
79    // Defend against the inspector asking for a var after it has been optimized out.
80    if (isTornOff() && !isValid(entry))
81        return false;
82
83    descriptor.setDescriptor(registerAt(entry.getIndex()).get(), entry.getAttributes());
84    return true;
85}
86
87inline bool JSActivation::symbolTablePut(ExecState* exec, PropertyName propertyName, JSValue value, bool shouldThrow)
88{
89    VM& vm = exec->vm();
90    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
91
92    SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.publicName());
93    if (entry.isNull())
94        return false;
95    if (entry.isReadOnly()) {
96        if (shouldThrow)
97            throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
98        return true;
99    }
100
101    // Defend against the inspector asking for a var after it has been optimized out.
102    if (isTornOff() && !isValid(entry))
103        return false;
104
105    registerAt(entry.getIndex()).set(vm, this, value);
106    return true;
107}
108
109void JSActivation::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
110{
111    JSActivation* thisObject = jsCast<JSActivation*>(object);
112
113    if (mode == IncludeDontEnumProperties && !thisObject->isTornOff())
114        propertyNames.add(exec->propertyNames().arguments);
115
116    SymbolTable::const_iterator end = thisObject->symbolTable()->end();
117    for (SymbolTable::const_iterator it = thisObject->symbolTable()->begin(); it != end; ++it) {
118        if (it->value.getAttributes() & DontEnum && mode != IncludeDontEnumProperties)
119            continue;
120        if (!thisObject->isValid(it->value))
121            continue;
122        propertyNames.add(Identifier(exec, it->key.get()));
123    }
124    // Skip the JSVariableObject implementation of getOwnNonIndexPropertyNames
125    JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
126}
127
128inline bool JSActivation::symbolTablePutWithAttributes(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
129{
130    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
131
132    SymbolTable::iterator iter = symbolTable()->find(propertyName.publicName());
133    if (iter == symbolTable()->end())
134        return false;
135    SymbolTableEntry& entry = iter->value;
136    ASSERT(!entry.isNull());
137    if (!isValid(entry))
138        return false;
139
140    entry.setAttributes(attributes);
141    registerAt(entry.getIndex()).set(vm, this, value);
142    return true;
143}
144
145bool JSActivation::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
146{
147    JSActivation* thisObject = jsCast<JSActivation*>(cell);
148
149    if (propertyName == exec->propertyNames().arguments) {
150        // Defend against the inspector asking for the arguments object after it has been optimized out.
151        if (!thisObject->isTornOff()) {
152            slot.setCustom(thisObject, thisObject->getArgumentsGetter());
153            return true;
154        }
155    }
156
157    if (thisObject->symbolTableGet(propertyName, slot))
158        return true;
159
160    if (JSValue value = thisObject->getDirect(exec->vm(), propertyName)) {
161        slot.setValue(value);
162        return true;
163    }
164
165    // We don't call through to JSObject because there's no way to give an
166    // activation object getter properties or a prototype.
167    ASSERT(!thisObject->hasGetterSetterProperties());
168    ASSERT(thisObject->prototype().isNull());
169    return false;
170}
171
172bool JSActivation::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
173{
174    JSActivation* thisObject = jsCast<JSActivation*>(object);
175
176    if (propertyName == exec->propertyNames().arguments) {
177        // Defend against the inspector asking for the arguments object after it has been optimized out.
178        if (!thisObject->isTornOff()) {
179            PropertySlot slot;
180            JSActivation::getOwnPropertySlot(thisObject, exec, propertyName, slot);
181            descriptor.setDescriptor(slot.getValue(exec, propertyName), DontEnum);
182            return true;
183        }
184    }
185
186    if (thisObject->symbolTableGet(propertyName, descriptor))
187        return true;
188
189    return Base::getOwnPropertyDescriptor(object, exec, propertyName, descriptor);
190}
191
192void JSActivation::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
193{
194    JSActivation* thisObject = jsCast<JSActivation*>(cell);
195    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
196
197    if (thisObject->symbolTablePut(exec, propertyName, value, slot.isStrictMode()))
198        return;
199
200    // We don't call through to JSObject because __proto__ and getter/setter
201    // properties are non-standard extensions that other implementations do not
202    // expose in the activation object.
203    ASSERT(!thisObject->hasGetterSetterProperties());
204    thisObject->putOwnDataProperty(exec->vm(), propertyName, value, slot);
205}
206
207// FIXME: Make this function honor ReadOnly (const) and DontEnum
208void JSActivation::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
209{
210    JSActivation* thisObject = jsCast<JSActivation*>(object);
211    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
212
213    if (thisObject->symbolTablePutWithAttributes(exec->vm(), propertyName, value, attributes))
214        return;
215
216    // We don't call through to JSObject because __proto__ and getter/setter
217    // properties are non-standard extensions that other implementations do not
218    // expose in the activation object.
219    ASSERT(!thisObject->hasGetterSetterProperties());
220    JSObject::putDirectVirtual(thisObject, exec, propertyName, value, attributes);
221}
222
223bool JSActivation::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
224{
225    if (propertyName == exec->propertyNames().arguments)
226        return false;
227
228    return Base::deleteProperty(cell, exec, propertyName);
229}
230
231JSObject* JSActivation::toThisObject(JSCell*, ExecState* exec)
232{
233    return exec->globalThisValue();
234}
235
236JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, PropertyName)
237{
238    JSActivation* activation = jsCast<JSActivation*>(slotBase);
239    if (activation->isTornOff())
240        return jsUndefined();
241
242    CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(activation->m_registers));
243    int argumentsRegister = callFrame->codeBlock()->argumentsRegister();
244    if (JSValue arguments = callFrame->uncheckedR(argumentsRegister).jsValue())
245        return arguments;
246    int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister);
247
248    JSValue arguments = JSValue(Arguments::create(callFrame->vm(), callFrame));
249    callFrame->uncheckedR(argumentsRegister) = arguments;
250    callFrame->uncheckedR(realArgumentsRegister) = arguments;
251
252    ASSERT(callFrame->uncheckedR(realArgumentsRegister).jsValue().inherits(&Arguments::s_info));
253    return callFrame->uncheckedR(realArgumentsRegister).jsValue();
254}
255
256// These two functions serve the purpose of isolating the common case from a
257// PIC branch.
258
259PropertySlot::GetValueFunc JSActivation::getArgumentsGetter()
260{
261    return argumentsGetter;
262}
263
264} // namespace JSC
265