Partial fix for <rdar://problem/9417875> REGRESSION: SunSpider ~17% slower
[webkit:qtwebkit.git] / 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, int length, const Identifier& name, NativeExecutable* thunk)
65     : Base(globalObject, structure)
66     , m_executable(exec->globalData(), this, thunk)
67     , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
68 {
69     ASSERT(inherits(&s_info));
70     putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
71     putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
72 }
73
74 JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeFunction func)
75     : Base(globalObject, structure)
76     , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
77 {
78     ASSERT(inherits(&s_info));
79     
80     // Can't do this during initialization because getHostFunction might do a GC allocation.
81     m_executable.set(exec->globalData(), this, exec->globalData().getHostFunction(func));
82     
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 JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
88     : Base(scopeChainNode->globalObject.get(), scopeChainNode->globalObject->functionStructure())
89     , m_executable(exec->globalData(), this, executable)
90     , m_scopeChain(exec->globalData(), this, scopeChainNode)
91 {
92     ASSERT(inherits(&s_info));
93     const Identifier& name = static_cast<FunctionExecutable*>(m_executable.get())->name();
94     putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
95 }
96
97 JSFunction::~JSFunction()
98 {
99     ASSERT(vptr() == JSGlobalData::jsFunctionVPtr);
100 }
101
102 static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function";
103 static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function";
104
105 static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message)
106 {
107     JSValue thrower = createTypeErrorFunction(exec, message);
108     descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter);
109 }
110
111 const UString& JSFunction::name(ExecState* exec)
112 {
113     return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue();
114 }
115
116 const UString JSFunction::displayName(ExecState* exec)
117 {
118     JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName);
119     
120     if (displayName && isJSString(&exec->globalData(), displayName))
121         return asString(displayName)->tryGetValue();
122     
123     return UString();
124 }
125
126 const UString JSFunction::calculatedDisplayName(ExecState* exec)
127 {
128     const UString explicitName = displayName(exec);
129     
130     if (!explicitName.isEmpty())
131         return explicitName;
132     
133     return name(exec);
134 }
135
136 void JSFunction::visitChildren(SlotVisitor& visitor)
137 {
138     Base::visitChildren(visitor);
139
140     visitor.append(&m_scopeChain);
141     if (m_executable)
142         visitor.append(&m_executable);
143 }
144
145 CallType JSFunction::getCallData(CallData& callData)
146 {
147     if (isHostFunction()) {
148         callData.native.function = nativeFunction();
149         return CallTypeHost;
150     }
151     callData.js.functionExecutable = jsExecutable();
152     callData.js.scopeChain = scope();
153     return CallTypeJS;
154 }
155
156 JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&)
157 {
158     JSFunction* thisObj = asFunction(slotBase);
159     ASSERT(!thisObj->isHostFunction());
160     return exec->interpreter()->retrieveArguments(exec, thisObj);
161 }
162
163 JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&)
164 {
165     JSFunction* thisObj = asFunction(slotBase);
166     ASSERT(!thisObj->isHostFunction());
167     return exec->interpreter()->retrieveCaller(exec, thisObj);
168 }
169
170 JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&)
171 {
172     JSFunction* thisObj = asFunction(slotBase);
173     ASSERT(!thisObj->isHostFunction());
174     return jsNumber(thisObj->jsExecutable()->parameterCount());
175 }
176
177 bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
178 {
179     if (isHostFunction())
180         return Base::getOwnPropertySlot(exec, propertyName, slot);
181
182     if (propertyName == exec->propertyNames().prototype) {
183         WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName);
184
185         if (!location) {
186             JSObject* prototype = constructEmptyObject(exec, scope()->globalObject->emptyObjectStructure());
187             prototype->putDirect(exec->globalData(), exec->propertyNames().constructor, this, DontEnum);
188             putDirect(exec->globalData(), exec->propertyNames().prototype, prototype, DontDelete | DontEnum);
189             location = getDirectLocation(exec->globalData(), propertyName);
190         }
191
192         slot.setValue(this, location->get(), offsetForLocation(location));
193     }
194
195     if (propertyName == exec->propertyNames().arguments) {
196         if (jsExecutable()->isStrictMode()) {
197             throwTypeError(exec, "Can't access arguments object of a strict mode function");
198             slot.setValue(jsNull());
199             return true;
200         }
201    
202         slot.setCacheableCustom(this, argumentsGetter);
203         return true;
204     }
205
206     if (propertyName == exec->propertyNames().length) {
207         slot.setCacheableCustom(this, lengthGetter);
208         return true;
209     }
210
211     if (propertyName == exec->propertyNames().caller) {
212         if (jsExecutable()->isStrictMode()) {
213             throwTypeError(exec, StrictModeCallerAccessError);
214             slot.setValue(jsNull());
215             return true;
216         }
217         slot.setCacheableCustom(this, callerGetter);
218         return true;
219     }
220
221     return Base::getOwnPropertySlot(exec, propertyName, slot);
222 }
223
224 bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
225 {
226     if (isHostFunction())
227         return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
228     
229     if (propertyName == exec->propertyNames().prototype) {
230         PropertySlot slot;
231         getOwnPropertySlot(exec, propertyName, slot);
232         return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
233     }
234     
235     if (propertyName == exec->propertyNames().arguments) {
236         if (jsExecutable()->isStrictMode())
237             createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError);
238         else
239             descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete);
240         return true;
241     }
242     
243     if (propertyName == exec->propertyNames().length) {
244         descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete);
245         return true;
246     }
247     
248     if (propertyName == exec->propertyNames().caller) {
249         if (jsExecutable()->isStrictMode())
250             createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError);
251         else
252             descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete);
253         return true;
254     }
255     
256     return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
257 }
258
259 void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
260 {
261     if (!isHostFunction() && (mode == IncludeDontEnumProperties)) {
262         // Make sure prototype has been reified.
263         PropertySlot slot;
264         getOwnPropertySlot(exec, exec->propertyNames().prototype, slot);
265
266         propertyNames.add(exec->propertyNames().arguments);
267         propertyNames.add(exec->propertyNames().callee);
268         propertyNames.add(exec->propertyNames().caller);
269         propertyNames.add(exec->propertyNames().length);
270     }
271     Base::getOwnPropertyNames(exec, propertyNames, mode);
272 }
273
274 void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
275 {
276     if (isHostFunction()) {
277         Base::put(exec, propertyName, value, slot);
278         return;
279     }
280     if (propertyName == exec->propertyNames().prototype) {
281         // Make sure prototype has been reified, such that it can only be overwritten
282         // following the rules set out in ECMA-262 8.12.9.
283         PropertySlot slot;
284         getOwnPropertySlot(exec, propertyName, slot);
285     }
286     if (jsExecutable()->isStrictMode()) {
287         if (propertyName == exec->propertyNames().arguments) {
288             throwTypeError(exec, StrictModeArgumentsAccessError);
289             return;
290         }
291         if (propertyName == exec->propertyNames().caller) {
292             throwTypeError(exec, StrictModeCallerAccessError);
293             return;
294         }
295     }
296     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
297         return;
298     Base::put(exec, propertyName, value, slot);
299 }
300
301 bool JSFunction::deleteProperty(ExecState* exec, const Identifier& propertyName)
302 {
303     if (isHostFunction())
304         return Base::deleteProperty(exec, propertyName);
305     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
306         return false;
307     return Base::deleteProperty(exec, propertyName);
308 }
309
310 // ECMA 13.2.2 [[Construct]]
311 ConstructType JSFunction::getConstructData(ConstructData& constructData)
312 {
313     if (isHostFunction())
314         return ConstructTypeNone;
315     constructData.js.functionExecutable = jsExecutable();
316     constructData.js.scopeChain = scope();
317     return ConstructTypeJS;
318 }
319
320 } // namespace JSC