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 "ObjectConstructor.h"
39 #include "ObjectPrototype.h"
40 #include "Operations.h"
42 #include "PropertyNameArray.h"
45 using namespace Unicode
;
48 EncodedJSValue JSC_HOST_CALL
callHostFunctionAsConstructor(ExecState
* exec
)
50 return throwVMError(exec
, createNotAConstructorError(exec
, exec
->callee()));
53 const ClassInfo
JSFunction::s_info
= { "Function", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(JSFunction
) };
55 bool JSFunction::isHostFunctionNonInline() const
57 return isHostFunction();
60 JSFunction
* JSFunction::create(ExecState
* exec
, JSGlobalObject
* globalObject
, int length
, const String
& name
, NativeFunction nativeFunction
, Intrinsic intrinsic
, NativeFunction nativeConstructor
)
62 NativeExecutable
* executable
;
64 UNUSED_PARAM(intrinsic
);
66 if (intrinsic
!= NoIntrinsic
&& exec
->vm().canUseJIT()) {
67 ASSERT(nativeConstructor
== callHostFunctionAsConstructor
);
68 executable
= exec
->vm().getHostFunction(nativeFunction
, intrinsic
);
71 executable
= exec
->vm().getHostFunction(nativeFunction
, nativeConstructor
);
73 JSFunction
* function
= new (NotNull
, allocateCell
<JSFunction
>(*exec
->heap())) JSFunction(exec
, globalObject
, globalObject
->functionStructure());
74 // Can't do this during initialization because getHostFunction might do a GC allocation.
75 function
->finishCreation(exec
, executable
, length
, name
);
79 void JSFunction::destroy(JSCell
* cell
)
81 static_cast<JSFunction
*>(cell
)->JSFunction::~JSFunction();
84 JSFunction::JSFunction(ExecState
* exec
, JSGlobalObject
* globalObject
, Structure
* structure
)
85 : Base(exec
->vm(), structure
)
87 , m_scope(exec
->vm(), this, globalObject
)
88 // We initialize blind so that changes to the prototype after function creation but before
89 // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the
90 // watchpoint will start watching and any changes will both force deoptimization and disable
91 // future attempts to optimize. This is necessary because we are guaranteed that the
92 // allocation profile is changed exactly once prior to optimizations kicking in. We could be
93 // smarter and count the number of times the prototype is clobbered and only optimize if it
94 // was clobbered exactly once, but that seems like overkill. In almost all cases it will be
95 // clobbered once, and if it's clobbered more than once, that will probably only occur
96 // before we started optimizing, anyway.
97 , m_allocationProfileWatchpoint(InitializedBlind
)
101 void JSFunction::finishCreation(ExecState
* exec
, NativeExecutable
* executable
, int length
, const String
& name
)
103 Base::finishCreation(exec
->vm());
104 ASSERT(inherits(&s_info
));
105 m_executable
.set(exec
->vm(), this, executable
);
106 putDirect(exec
->vm(), exec
->vm().propertyNames
->name
, jsString(exec
, name
), DontDelete
| ReadOnly
| DontEnum
);
107 putDirect(exec
->vm(), exec
->propertyNames().length
, jsNumber(length
), DontDelete
| ReadOnly
| DontEnum
);
110 ObjectAllocationProfile
* JSFunction::createAllocationProfile(ExecState
* exec
, size_t inlineCapacity
)
113 JSObject
* prototype
= jsDynamicCast
<JSObject
*>(get(exec
, vm
.propertyNames
->prototype
));
115 prototype
= globalObject()->objectPrototype();
116 m_allocationProfile
.initialize(globalObject()->vm(), this, prototype
, inlineCapacity
);
117 return &m_allocationProfile
;
120 String
JSFunction::name(ExecState
* exec
)
122 return get(exec
, exec
->vm().propertyNames
->name
).toWTFString(exec
);
125 String
JSFunction::displayName(ExecState
* exec
)
127 JSValue displayName
= getDirect(exec
->vm(), exec
->vm().propertyNames
->displayName
);
129 if (displayName
&& isJSString(displayName
))
130 return asString(displayName
)->tryGetValue();
135 const String
JSFunction::calculatedDisplayName(ExecState
* exec
)
137 const String explicitName
= displayName(exec
);
139 if (!explicitName
.isEmpty())
142 const String actualName
= name(exec
);
143 if (!actualName
.isEmpty() || isHostFunction())
146 return jsExecutable()->inferredName().string();
149 const SourceCode
* JSFunction::sourceCode() const
151 if (isHostFunction())
153 return &jsExecutable()->source();
156 void JSFunction::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
158 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
159 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
160 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
161 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
162 Base::visitChildren(thisObject
, visitor
);
164 visitor
.append(&thisObject
->m_scope
);
165 visitor
.append(&thisObject
->m_executable
);
166 thisObject
->m_allocationProfile
.visitAggregate(visitor
);
169 CallType
JSFunction::getCallData(JSCell
* cell
, CallData
& callData
)
171 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
172 if (thisObject
->isHostFunction()) {
173 callData
.native
.function
= thisObject
->nativeFunction();
176 callData
.js
.functionExecutable
= thisObject
->jsExecutable();
177 callData
.js
.scope
= thisObject
->scope();
181 JSValue
JSFunction::argumentsGetter(ExecState
* exec
, JSValue slotBase
, PropertyName
)
183 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
184 ASSERT(!thisObj
->isHostFunction());
185 return exec
->interpreter()->retrieveArgumentsFromVMCode(exec
, thisObj
);
188 JSValue
JSFunction::callerGetter(ExecState
* exec
, JSValue slotBase
, PropertyName
)
190 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
191 ASSERT(!thisObj
->isHostFunction());
192 JSValue caller
= exec
->interpreter()->retrieveCallerFromVMCode(exec
, thisObj
);
194 // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller.
195 if (!caller
.isObject() || !asObject(caller
)->inherits(&JSFunction::s_info
))
197 JSFunction
* function
= jsCast
<JSFunction
*>(caller
);
198 if (function
->isHostFunction() || !function
->jsExecutable()->isStrictMode())
200 return throwTypeError(exec
, ASCIILiteral("Function.caller used to retrieve strict caller"));
203 JSValue
JSFunction::lengthGetter(ExecState
*, JSValue slotBase
, PropertyName
)
205 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
206 ASSERT(!thisObj
->isHostFunction());
207 return jsNumber(thisObj
->jsExecutable()->parameterCount());
210 JSValue
JSFunction::nameGetter(ExecState
*, JSValue slotBase
, PropertyName
)
212 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
213 ASSERT(!thisObj
->isHostFunction());
214 return thisObj
->jsExecutable()->nameValue();
217 bool JSFunction::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
219 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
220 if (thisObject
->isHostFunction())
221 return Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
223 if (propertyName
== exec
->propertyNames().prototype
) {
225 PropertyOffset offset
= thisObject
->getDirectOffset(vm
, propertyName
);
226 if (!isValidOffset(offset
)) {
227 JSObject
* prototype
= constructEmptyObject(exec
);
228 prototype
->putDirect(vm
, exec
->propertyNames().constructor
, thisObject
, DontEnum
);
229 thisObject
->putDirect(vm
, exec
->propertyNames().prototype
, prototype
, DontDelete
| DontEnum
);
230 offset
= thisObject
->getDirectOffset(vm
, exec
->propertyNames().prototype
);
231 ASSERT(isValidOffset(offset
));
234 slot
.setValue(thisObject
, thisObject
->getDirect(offset
), offset
);
237 if (propertyName
== exec
->propertyNames().arguments
) {
238 if (thisObject
->jsExecutable()->isStrictMode()) {
239 bool result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
241 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
242 result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
247 slot
.setCacheableCustom(thisObject
, argumentsGetter
);
251 if (propertyName
== exec
->propertyNames().length
) {
252 slot
.setCacheableCustom(thisObject
, lengthGetter
);
256 if (propertyName
== exec
->propertyNames().name
) {
257 slot
.setCacheableCustom(thisObject
, nameGetter
);
261 if (propertyName
== exec
->propertyNames().caller
) {
262 if (thisObject
->jsExecutable()->isStrictMode()) {
263 bool result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
265 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
266 result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
271 slot
.setCacheableCustom(thisObject
, callerGetter
);
275 return Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
278 bool JSFunction::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
280 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
281 if (thisObject
->isHostFunction())
282 return Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
284 if (propertyName
== exec
->propertyNames().prototype
) {
286 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
287 return Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
290 if (propertyName
== exec
->propertyNames().arguments
) {
291 if (thisObject
->jsExecutable()->isStrictMode()) {
292 bool result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
294 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
295 result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
300 descriptor
.setDescriptor(exec
->interpreter()->retrieveArgumentsFromVMCode(exec
, thisObject
), ReadOnly
| DontEnum
| DontDelete
);
304 if (propertyName
== exec
->propertyNames().length
) {
305 descriptor
.setDescriptor(jsNumber(thisObject
->jsExecutable()->parameterCount()), ReadOnly
| DontEnum
| DontDelete
);
309 if (propertyName
== exec
->propertyNames().name
) {
310 descriptor
.setDescriptor(thisObject
->jsExecutable()->nameValue(), ReadOnly
| DontEnum
| DontDelete
);
314 if (propertyName
== exec
->propertyNames().caller
) {
315 if (thisObject
->jsExecutable()->isStrictMode()) {
316 bool result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
318 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
319 result
= Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
324 descriptor
.setDescriptor(exec
->interpreter()->retrieveCallerFromVMCode(exec
, thisObject
), ReadOnly
| DontEnum
| DontDelete
);
328 return Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
331 void JSFunction::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
333 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
334 if (!thisObject
->isHostFunction() && (mode
== IncludeDontEnumProperties
)) {
335 // Make sure prototype has been reified.
337 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, exec
->propertyNames().prototype
, slot
);
339 propertyNames
.add(exec
->propertyNames().arguments
);
340 propertyNames
.add(exec
->propertyNames().caller
);
341 propertyNames
.add(exec
->propertyNames().length
);
342 propertyNames
.add(exec
->propertyNames().name
);
344 Base::getOwnNonIndexPropertyNames(thisObject
, exec
, propertyNames
, mode
);
347 void JSFunction::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
349 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
350 if (thisObject
->isHostFunction()) {
351 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
354 if (propertyName
== exec
->propertyNames().prototype
) {
355 // Make sure prototype has been reified, such that it can only be overwritten
356 // following the rules set out in ECMA-262 8.12.9.
358 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
359 thisObject
->m_allocationProfile
.clear();
360 thisObject
->m_allocationProfileWatchpoint
.notifyWrite();
361 // Don't allow this to be cached, since a [[Put]] must clear m_allocationProfile.
362 PutPropertySlot dontCache
;
363 Base::put(thisObject
, exec
, propertyName
, value
, dontCache
);
366 if (thisObject
->jsExecutable()->isStrictMode() && (propertyName
== exec
->propertyNames().arguments
|| propertyName
== exec
->propertyNames().caller
)) {
367 // This will trigger the property to be reified, if this is not already the case!
368 bool okay
= thisObject
->hasProperty(exec
, propertyName
);
369 ASSERT_UNUSED(okay
, okay
);
370 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
373 if (propertyName
== exec
->propertyNames().arguments
|| propertyName
== exec
->propertyNames().length
|| propertyName
== exec
->propertyNames().name
|| propertyName
== exec
->propertyNames().caller
) {
374 if (slot
.isStrictMode())
375 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
378 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
381 bool JSFunction::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
383 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
384 // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
385 if (!thisObject
->isHostFunction() && !exec
->vm().isInDefineOwnProperty()
386 && (propertyName
== exec
->propertyNames().arguments
387 || propertyName
== exec
->propertyNames().length
388 || propertyName
== exec
->propertyNames().name
389 || propertyName
== exec
->propertyNames().prototype
390 || propertyName
== exec
->propertyNames().caller
))
392 return Base::deleteProperty(thisObject
, exec
, propertyName
);
395 bool JSFunction::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
397 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
398 if (thisObject
->isHostFunction())
399 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
401 if (propertyName
== exec
->propertyNames().prototype
) {
402 // Make sure prototype has been reified, such that it can only be overwritten
403 // following the rules set out in ECMA-262 8.12.9.
405 thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
406 thisObject
->m_allocationProfile
.clear();
407 thisObject
->m_allocationProfileWatchpoint
.notifyWrite();
408 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
412 if (propertyName
== exec
->propertyNames().arguments
) {
413 if (thisObject
->jsExecutable()->isStrictMode()) {
414 if (!Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
))
415 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
416 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
418 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), exec
->interpreter()->retrieveArgumentsFromVMCode(exec
, thisObject
));
419 } else if (propertyName
== exec
->propertyNames().caller
) {
420 if (thisObject
->jsExecutable()->isStrictMode()) {
421 if (!Base::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
))
422 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
), DontDelete
| DontEnum
| Accessor
);
423 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
425 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), exec
->interpreter()->retrieveCallerFromVMCode(exec
, thisObject
));
426 } else if (propertyName
== exec
->propertyNames().length
)
427 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), jsNumber(thisObject
->jsExecutable()->parameterCount()));
428 else if (propertyName
== exec
->propertyNames().name
)
429 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), thisObject
->jsExecutable()->nameValue());
431 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
433 if (descriptor
.configurablePresent() && descriptor
.configurable()) {
435 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
438 if (descriptor
.enumerablePresent() && descriptor
.enumerable()) {
440 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
443 if (descriptor
.isAccessorDescriptor()) {
445 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
448 if (descriptor
.writablePresent() && descriptor
.writable()) {
450 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
455 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change value of a readonly property.")));
461 // ECMA 13.2.2 [[Construct]]
462 ConstructType
JSFunction::getConstructData(JSCell
* cell
, ConstructData
& constructData
)
464 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
465 if (thisObject
->isHostFunction()) {
466 constructData
.native
.function
= thisObject
->nativeConstructor();
467 return ConstructTypeHost
;
469 constructData
.js
.functionExecutable
= thisObject
->jsExecutable();
470 constructData
.js
.scope
= thisObject
->scope();
471 return ConstructTypeJS
;
474 String
getCalculatedDisplayName(CallFrame
* callFrame
, JSObject
* object
)
476 if (JSFunction
* function
= jsDynamicCast
<JSFunction
*>(object
))
477 return function
->calculatedDisplayName(callFrame
);
478 if (InternalFunction
* function
= jsDynamicCast
<InternalFunction
*>(object
))
479 return function
->calculatedDisplayName(callFrame
);