initial import
[vuplus_webkit] / Source / JavaScriptCore / runtime / JSFunction.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 "JSFunction.h"
27
28 #include "CodeBlock.h"
29 #include "CommonIdentifiers.h"
30 #include "CallFrame.h"
31 #include "ExceptionHelpers.h"
32 #include "FunctionPrototype.h"
33 #include "JSGlobalObject.h"
34 #include "JSNotAnObject.h"
35 #include "Interpreter.h"
36 #include "ObjectPrototype.h"
37 #include "Parser.h"
38 #include "PropertyNameArray.h"
39 #include "ScopeChainMark.h"
40
41 using namespace WTF;
42 using namespace Unicode;
43
44 namespace JSC {
45 EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec)
46 {
47     return throwVMError(exec, createNotAConstructorError(exec, exec->callee()));
48 }
49
50 ASSERT_CLASS_FITS_IN_CELL(JSFunction);
51
52 const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0 };
53
54 bool JSFunction::isHostFunctionNonInline() const
55 {
56     return isHostFunction();
57 }
58
59 JSFunction::JSFunction(VPtrStealingHackType)
60     : Base(VPtrStealingHack)
61 {
62 }
63
64 JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
65     : Base(exec->globalData(), structure)
66     , m_executable()
67     , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
68 {
69 }
70
71 JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
72     : Base(exec->globalData(), scopeChainNode->globalObject->functionStructure())
73     , m_executable(exec->globalData(), this, executable)
74     , m_scopeChain(exec->globalData(), this, scopeChainNode)
75 {
76 }
77
78 void JSFunction::finishCreation(ExecState* exec, int length, const Identifier& name, ExecutableBase* executable)
79 {
80     Base::finishCreation(exec->globalData());
81     ASSERT(inherits(&s_info));
82     m_executable.set(exec->globalData(), this, executable);
83     putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
84     putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
85 }
86
87 void JSFunction::finishCreation(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
88 {
89     Base::finishCreation(exec->globalData());
90     ASSERT(inherits(&s_info));
91     setStructure(exec->globalData(), scopeChainNode->globalObject->namedFunctionStructure());
92     putDirectOffset(exec->globalData(), scopeChainNode->globalObject->functionNameOffset(), executable->nameValue());
93 }
94
95 JSFunction::~JSFunction()
96 {
97     ASSERT(vptr() == JSGlobalData::jsFunctionVPtr);
98 }
99
100 static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function";
101 static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function";
102
103 static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message)
104 {
105     JSValue thrower = createTypeErrorFunction(exec, message);
106     descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter);
107 }
108
109 const UString& JSFunction::name(ExecState* exec)
110 {
111     return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue();
112 }
113
114 const UString JSFunction::displayName(ExecState* exec)
115 {
116     JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName);
117     
118     if (displayName && isJSString(&exec->globalData(), displayName))
119         return asString(displayName)->tryGetValue();
120     
121     return UString();
122 }
123
124 const UString JSFunction::calculatedDisplayName(ExecState* exec)
125 {
126     const UString explicitName = displayName(exec);
127     
128     if (!explicitName.isEmpty())
129         return explicitName;
130     
131     return name(exec);
132 }
133
134 void JSFunction::visitChildren(SlotVisitor& visitor)
135 {
136     ASSERT_GC_OBJECT_INHERITS(this, &s_info);
137     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
138     ASSERT(structure()->typeInfo().overridesVisitChildren());
139     Base::visitChildren(visitor);
140
141     visitor.append(&m_scopeChain);
142     if (m_executable)
143         visitor.append(&m_executable);
144 }
145
146 CallType JSFunction::getCallData(CallData& callData)
147 {
148     if (isHostFunction()) {
149         callData.native.function = nativeFunction();
150         return CallTypeHost;
151     }
152     callData.js.functionExecutable = jsExecutable();
153     callData.js.scopeChain = scope();
154     return CallTypeJS;
155 }
156
157 JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&)
158 {
159     JSFunction* thisObj = asFunction(slotBase);
160     ASSERT(!thisObj->isHostFunction());
161     return exec->interpreter()->retrieveArguments(exec, thisObj);
162 }
163
164 JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&)
165 {
166     JSFunction* thisObj = asFunction(slotBase);
167     ASSERT(!thisObj->isHostFunction());
168     return exec->interpreter()->retrieveCaller(exec, thisObj);
169 }
170
171 JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&)
172 {
173     JSFunction* thisObj = asFunction(slotBase);
174     ASSERT(!thisObj->isHostFunction());
175     return jsNumber(thisObj->jsExecutable()->parameterCount());
176 }
177
178 static inline WriteBarrierBase<Unknown>* createPrototypeProperty(JSGlobalData& globalData, JSGlobalObject* globalObject, JSFunction* function)
179 {
180     ASSERT(!function->isHostFunction());
181
182     ExecState* exec = globalObject->globalExec();
183     if (WriteBarrierBase<Unknown>* location = function->getDirectLocation(globalData, exec->propertyNames().prototype))
184         return location;
185     JSObject* prototype = constructEmptyObject(exec, globalObject->emptyObjectStructure());
186     prototype->putDirect(globalData, exec->propertyNames().constructor, function, DontEnum);
187     function->putDirect(globalData, exec->propertyNames().prototype, prototype, DontDelete | DontEnum);
188     return function->getDirectLocation(exec->globalData(), exec->propertyNames().prototype);
189 }
190
191 void JSFunction::preventExtensions(JSGlobalData& globalData)
192 {
193     if (!isHostFunction())
194         createPrototypeProperty(globalData, scope()->globalObject.get(), this);
195     JSObject::preventExtensions(globalData);
196 }
197
198 bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
199 {
200     if (isHostFunction())
201         return Base::getOwnPropertySlot(exec, propertyName, slot);
202
203     if (propertyName == exec->propertyNames().prototype) {
204         WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName);
205
206         if (!location)
207             location = createPrototypeProperty(exec->globalData(), scope()->globalObject.get(), this);
208
209         slot.setValue(this, location->get(), offsetForLocation(location));
210     }
211
212     if (propertyName == exec->propertyNames().arguments) {
213         if (jsExecutable()->isStrictMode()) {
214             throwTypeError(exec, "Can't access arguments object of a strict mode function");
215             slot.setValue(jsNull());
216             return true;
217         }
218    
219         slot.setCacheableCustom(this, argumentsGetter);
220         return true;
221     }
222
223     if (propertyName == exec->propertyNames().length) {
224         slot.setCacheableCustom(this, lengthGetter);
225         return true;
226     }
227
228     if (propertyName == exec->propertyNames().caller) {
229         if (jsExecutable()->isStrictMode()) {
230             throwTypeError(exec, StrictModeCallerAccessError);
231             slot.setValue(jsNull());
232             return true;
233         }
234         slot.setCacheableCustom(this, callerGetter);
235         return true;
236     }
237
238     return Base::getOwnPropertySlot(exec, propertyName, slot);
239 }
240
241 bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
242 {
243     if (isHostFunction())
244         return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
245     
246     if (propertyName == exec->propertyNames().prototype) {
247         PropertySlot slot;
248         getOwnPropertySlot(exec, propertyName, slot);
249         return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
250     }
251     
252     if (propertyName == exec->propertyNames().arguments) {
253         if (jsExecutable()->isStrictMode())
254             createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError);
255         else
256             descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete);
257         return true;
258     }
259     
260     if (propertyName == exec->propertyNames().length) {
261         descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete);
262         return true;
263     }
264     
265     if (propertyName == exec->propertyNames().caller) {
266         if (jsExecutable()->isStrictMode())
267             createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError);
268         else
269             descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete);
270         return true;
271     }
272     
273     return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
274 }
275
276 void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
277 {
278     if (!isHostFunction() && (mode == IncludeDontEnumProperties)) {
279         // Make sure prototype has been reified.
280         PropertySlot slot;
281         getOwnPropertySlot(exec, exec->propertyNames().prototype, slot);
282
283         propertyNames.add(exec->propertyNames().arguments);
284         propertyNames.add(exec->propertyNames().caller);
285         propertyNames.add(exec->propertyNames().length);
286     }
287     Base::getOwnPropertyNames(exec, propertyNames, mode);
288 }
289
290 void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
291 {
292     if (isHostFunction()) {
293         Base::put(exec, propertyName, value, slot);
294         return;
295     }
296     if (propertyName == exec->propertyNames().prototype) {
297         // Make sure prototype has been reified, such that it can only be overwritten
298         // following the rules set out in ECMA-262 8.12.9.
299         PropertySlot slot;
300         getOwnPropertySlot(exec, propertyName, slot);
301     }
302     if (jsExecutable()->isStrictMode()) {
303         if (propertyName == exec->propertyNames().arguments) {
304             throwTypeError(exec, StrictModeArgumentsAccessError);
305             return;
306         }
307         if (propertyName == exec->propertyNames().caller) {
308             throwTypeError(exec, StrictModeCallerAccessError);
309             return;
310         }
311     }
312     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
313         return;
314     Base::put(exec, propertyName, value, slot);
315 }
316
317 bool JSFunction::deleteProperty(ExecState* exec, const Identifier& propertyName)
318 {
319     if (isHostFunction())
320         return Base::deleteProperty(exec, propertyName);
321     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
322         return false;
323     return Base::deleteProperty(exec, propertyName);
324 }
325
326 // ECMA 13.2.2 [[Construct]]
327 ConstructType JSFunction::getConstructData(ConstructData& constructData)
328 {
329     if (isHostFunction())
330         return ConstructTypeNone;
331     constructData.js.functionExecutable = jsExecutable();
332     constructData.js.scopeChain = scope();
333     return ConstructTypeJS;
334 }
335
336 } // namespace JSC