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
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.
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.
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.
26 #include "JSFunction.h"
28 #include "CodeBlock.h"
29 #include "CommonIdentifiers.h"
30 #include "CallFrame.h"
31 #include "ExceptionHelpers.h"
32 #include "FunctionPrototype.h"
33 #include "GetterSetter.h"
35 #include "JSGlobalObject.h"
36 #include "JSNotAnObject.h"
37 #include "Interpreter.h"
38 #include "ObjectPrototype.h"
40 #include "PropertyNameArray.h"
41 #include "ScopeChainMark.h"
44 using namespace Unicode
;
47 EncodedJSValue JSC_HOST_CALL
callHostFunctionAsConstructor(ExecState
* exec
)
49 return throwVMError(exec
, createNotAConstructorError(exec
, exec
->callee()));
52 ASSERT_CLASS_FITS_IN_CELL(JSFunction
);
53 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFunction
);
55 const ClassInfo
JSFunction::s_info
= { "Function", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(JSFunction
) };
57 bool JSFunction::isHostFunctionNonInline() const
59 return isHostFunction();
62 JSFunction
* JSFunction::create(ExecState
* exec
, JSGlobalObject
* globalObject
, int length
, const Identifier
& name
, NativeFunction nativeFunction
, Intrinsic intrinsic
, NativeFunction nativeConstructor
)
64 NativeExecutable
* executable
;
66 UNUSED_PARAM(intrinsic
);
68 if (intrinsic
!= NoIntrinsic
&& exec
->globalData().canUseJIT()) {
69 ASSERT(nativeConstructor
== callHostFunctionAsConstructor
);
70 executable
= exec
->globalData().getHostFunction(nativeFunction
, intrinsic
);
73 executable
= exec
->globalData().getHostFunction(nativeFunction
, nativeConstructor
);
75 JSFunction
* function
= new (NotNull
, allocateCell
<JSFunction
>(*exec
->heap())) JSFunction(exec
, globalObject
, globalObject
->functionStructure());
76 // Can't do this during initialization because getHostFunction might do a GC allocation.
77 function
->finishCreation(exec
, executable
, length
, name
);
81 JSFunction::JSFunction(ExecState
* exec
, JSGlobalObject
* globalObject
, Structure
* structure
)
82 : Base(exec
->globalData(), structure
)
84 , m_scopeChain(exec
->globalData(), this, globalObject
->globalScopeChain())
88 JSFunction::JSFunction(ExecState
* exec
, FunctionExecutable
* executable
, ScopeChainNode
* scopeChainNode
)
89 : Base(exec
->globalData(), scopeChainNode
->globalObject
->functionStructure())
90 , m_executable(exec
->globalData(), this, executable
)
91 , m_scopeChain(exec
->globalData(), this, scopeChainNode
)
95 void JSFunction::finishCreation(ExecState
* exec
, NativeExecutable
* executable
, int length
, const Identifier
& name
)
97 Base::finishCreation(exec
->globalData());
98 ASSERT(inherits(&s_info
));
99 m_executable
.set(exec
->globalData(), this, executable
);
100 putDirect(exec
->globalData(), exec
->globalData().propertyNames
->name
, jsString(exec
, name
.isNull() ? "" : name
.ustring()), DontDelete
| ReadOnly
| DontEnum
);
101 putDirect(exec
->globalData(), exec
->propertyNames().length
, jsNumber(length
), DontDelete
| ReadOnly
| DontEnum
);
104 void JSFunction::finishCreation(ExecState
* exec
, FunctionExecutable
* executable
, ScopeChainNode
* scopeChainNode
)
106 Base::finishCreation(exec
->globalData());
107 ASSERT(inherits(&s_info
));
109 // Switching the structure here is only safe if we currently have the function structure!
110 ASSERT(structure() == scopeChainNode
->globalObject
->functionStructure());
111 setStructure(exec
->globalData(), scopeChainNode
->globalObject
->namedFunctionStructure());
112 putDirectOffset(exec
->globalData(), scopeChainNode
->globalObject
->functionNameOffset(), executable
->nameValue());
115 const UString
& JSFunction::name(ExecState
* exec
)
117 return asString(getDirect(exec
->globalData(), exec
->globalData().propertyNames
->name
))->tryGetValue();
120 const UString
JSFunction::displayName(ExecState
* exec
)
122 JSValue displayName
= getDirect(exec
->globalData(), exec
->globalData().propertyNames
->displayName
);
124 if (displayName
&& isJSString(displayName
))
125 return asString(displayName
)->tryGetValue();
130 const UString
JSFunction::calculatedDisplayName(ExecState
* exec
)
132 const UString explicitName
= displayName(exec
);
134 if (!explicitName
.isEmpty())
137 const UString actualName
= name(exec
);
138 if (!actualName
.isEmpty() || isHostFunction())
141 return jsExecutable()->inferredName().ustring();
144 const SourceCode
* JSFunction::sourceCode() const
146 if (isHostFunction())
148 return &jsExecutable()->source();
151 void JSFunction::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
153 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
154 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
155 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
156 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
157 Base::visitChildren(thisObject
, visitor
);
159 visitor
.append(&thisObject
->m_scopeChain
);
160 if (thisObject
->m_executable
)
161 visitor
.append(&thisObject
->m_executable
);
164 CallType
JSFunction::getCallData(JSCell
* cell
, CallData
& callData
)
166 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
167 if (thisObject
->isHostFunction()) {
168 callData
.native
.function
= thisObject
->nativeFunction();
171 callData
.js
.functionExecutable
= thisObject
->jsExecutable();
172 callData
.js
.scopeChain
= thisObject
->scope();
176 JSValue
JSFunction::argumentsGetter(ExecState
* exec
, JSValue slotBase
, const Identifier
&)
178 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
179 ASSERT(!thisObj
->isHostFunction());
180 return exec
->interpreter()->retrieveArgumentsFromVMCode(exec
, thisObj
);
183 JSValue
JSFunction::callerGetter(ExecState
* exec
, JSValue slotBase
, const Identifier
&)
185 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
186 ASSERT(!thisObj
->isHostFunction());
187 JSValue caller
= exec
->interpreter()->retrieveCallerFromVMCode(exec
, thisObj
);
189 // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller.
190 if (!caller
.isObject() || !asObject(caller
)->inherits(&JSFunction::s_info
))
192 JSFunction
* function
= jsCast
<JSFunction
*>(caller
);
193 if (function
->isHostFunction() || !function
->jsExecutable()->isStrictMode())
195 return throwTypeError(exec
, "Function.caller used to retrieve strict caller");
198 JSValue
JSFunction::lengthGetter(ExecState
*, JSValue slotBase
, const Identifier
&)
200 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
201 ASSERT(!thisObj
->isHostFunction());
202 return jsNumber(thisObj
->jsExecutable()->parameterCount());
205 bool JSFunction::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
207 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
208 if (thisObject
->isHostFunction())
209 return Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
211 if (propertyName
== exec
->propertyNames().prototype
) {
212 WriteBarrierBase
<Unknown
>* location
= thisObject
->getDirectLocation(exec
->globalData(), propertyName
);
215 JSObject
* prototype
= constructEmptyObject(exec
, thisObject
->globalObject()->emptyObjectStructure());
216 prototype
->putDirect(exec
->globalData(), exec
->propertyNames().constructor
, thisObject
, DontEnum
);
217 thisObject
->putDirect(exec
->globalData(), exec
->propertyNames().prototype
, prototype
, DontDelete
| DontEnum
);
218 location
= thisObject
->getDirectLocation(exec
->globalData(), exec
->propertyNames().prototype
);
221 slot
.setValue(thisObject
, location
->get(), thisObject
->offsetForLocation(location
));
224 if (propertyName
== exec
->propertyNames().arguments
) {
225 if (thisObject
->jsExecutable()->isStrictMode()) {
226 bool result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
228 thisObject
->putDirectAccessor(exec
->globalData(), propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
229 result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
234 slot
.setCacheableCustom(thisObject
, argumentsGetter
);
238 if (propertyName
== exec
->propertyNames().length
) {
239 slot
.setCacheableCustom(thisObject
, lengthGetter
);
243 if (propertyName
== exec
->propertyNames().caller
) {
244 if (thisObject
->jsExecutable()->isStrictMode()) {
245 bool result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
247 thisObject
->putDirectAccessor(exec
->globalData(), propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
248 result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
253 slot
.setCacheableCustom(thisObject
, callerGetter
);
257 return Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
260 bool JSFunction::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
262 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
263 if (thisObject
->isHostFunction())
264 return Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
266 if (propertyName
== exec
->propertyNames().prototype
) {
268 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
269 return Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
272 if (propertyName
== exec
->propertyNames().arguments
) {
273 if (thisObject
->jsExecutable()->isStrictMode()) {
274 bool result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
276 thisObject
->putDirectAccessor(exec
->globalData(), propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
277 result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
282 descriptor
.setDescriptor(exec
->interpreter()->retrieveArgumentsFromVMCode(exec
, thisObject
), ReadOnly
| DontEnum
| DontDelete
);
286 if (propertyName
== exec
->propertyNames().length
) {
287 descriptor
.setDescriptor(jsNumber(thisObject
->jsExecutable()->parameterCount()), ReadOnly
| DontEnum
| DontDelete
);
291 if (propertyName
== exec
->propertyNames().caller
) {
292 if (thisObject
->jsExecutable()->isStrictMode()) {
293 bool result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
295 thisObject
->putDirectAccessor(exec
->globalData(), propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
296 result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
301 descriptor
.setDescriptor(exec
->interpreter()->retrieveCallerFromVMCode(exec
, thisObject
), ReadOnly
| DontEnum
| DontDelete
);
305 return Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
308 void JSFunction::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
310 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
311 if (!thisObject
->isHostFunction() && (mode
== IncludeDontEnumProperties
)) {
312 // Make sure prototype has been reified.
314 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, exec
->propertyNames().prototype
, slot
);
316 propertyNames
.add(exec
->propertyNames().arguments
);
317 propertyNames
.add(exec
->propertyNames().caller
);
318 propertyNames
.add(exec
->propertyNames().length
);
320 Base::getOwnPropertyNames(thisObject
, exec
, propertyNames
, mode
);
323 void JSFunction::put(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
325 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
326 if (thisObject
->isHostFunction()) {
327 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
330 if (propertyName
== exec
->propertyNames().prototype
) {
331 // Make sure prototype has been reified, such that it can only be overwritten
332 // following the rules set out in ECMA-262 8.12.9.
334 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
336 if (thisObject
->jsExecutable()->isStrictMode() && (propertyName
== exec
->propertyNames().arguments
|| propertyName
== exec
->propertyNames().caller
)) {
337 // This will trigger the property to be reified, if this is not already the case!
338 bool okay
= thisObject
->hasProperty(exec
, propertyName
);
339 ASSERT_UNUSED(okay
, okay
);
340 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
343 if (propertyName
== exec
->propertyNames().arguments
|| propertyName
== exec
->propertyNames().length
|| propertyName
== exec
->propertyNames().caller
) {
344 if (slot
.isStrictMode())
345 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
348 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
351 bool JSFunction::deleteProperty(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
)
353 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
354 // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
355 if (!thisObject
->isHostFunction() && !exec
->globalData().isInDefineOwnProperty()
356 && (propertyName
== exec
->propertyNames().arguments
357 || propertyName
== exec
->propertyNames().length
358 || propertyName
== exec
->propertyNames().prototype
359 || propertyName
== exec
->propertyNames().caller
))
361 return Base::deleteProperty(thisObject
, exec
, propertyName
);
364 bool JSFunction::defineOwnProperty(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
366 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
367 if (thisObject
->isHostFunction())
368 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
370 if (propertyName
== exec
->propertyNames().prototype
) {
371 // Make sure prototype has been reified, such that it can only be overwritten
372 // following the rules set out in ECMA-262 8.12.9.
374 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
375 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
379 if (propertyName
== exec
->propertyNames().arguments
) {
380 if (thisObject
->jsExecutable()->isStrictMode()) {
381 if (!Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
))
382 thisObject
->putDirectAccessor(exec
->globalData(), propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
383 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
385 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), exec
->interpreter()->retrieveArgumentsFromVMCode(exec
, thisObject
));
386 } else if (propertyName
== exec
->propertyNames().caller
) {
387 if (thisObject
->jsExecutable()->isStrictMode()) {
388 if (!Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
))
389 thisObject
->putDirectAccessor(exec
->globalData(), propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
390 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
392 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), exec
->interpreter()->retrieveCallerFromVMCode(exec
, thisObject
));
393 } else if (propertyName
== exec
->propertyNames().length
)
394 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), jsNumber(thisObject
->jsExecutable()->parameterCount()));
396 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
398 if (descriptor
.configurablePresent() && descriptor
.configurable()) {
400 throwError(exec
, createTypeError(exec
, "Attempting to configurable attribute of unconfigurable property."));
403 if (descriptor
.enumerablePresent() && descriptor
.enumerable()) {
405 throwError(exec
, createTypeError(exec
, "Attempting to change enumerable attribute of unconfigurable property."));
408 if (descriptor
.isAccessorDescriptor()) {
410 throwError(exec
, createTypeError(exec
, "Attempting to change access mechanism for an unconfigurable property."));
413 if (descriptor
.writablePresent() && descriptor
.writable()) {
415 throwError(exec
, createTypeError(exec
, "Attempting to change writable attribute of unconfigurable property."));
420 throwError(exec
, createTypeError(exec
, "Attempting to change value of a readonly property."));
426 // ECMA 13.2.2 [[Construct]]
427 ConstructType
JSFunction::getConstructData(JSCell
* cell
, ConstructData
& constructData
)
429 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
430 if (thisObject
->isHostFunction()) {
431 constructData
.native
.function
= thisObject
->nativeConstructor();
432 return ConstructTypeHost
;
434 constructData
.js
.functionExecutable
= thisObject
->jsExecutable();
435 constructData
.js
.scopeChain
= thisObject
->scope();
436 return ConstructTypeJS
;
440 UString
getCalculatedDisplayName(CallFrame
* callFrame
, JSObject
* object
)
442 if (JSFunction
* function
= jsDynamicCast
<JSFunction
*>(object
))
443 return function
->calculatedDisplayName(callFrame
);
444 if (InternalFunction
* function
= jsDynamicCast
<InternalFunction
*>(object
))
445 return function
->calculatedDisplayName(callFrame
);
446 return callFrame
->globalData().propertyNames
->emptyIdentifier
.ustring();