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 "Arguments.h"
29 #include "CodeBlock.h"
30 #include "CommonIdentifiers.h"
31 #include "CallFrame.h"
32 #include "ExceptionHelpers.h"
33 #include "FunctionPrototype.h"
34 #include "GetterSetter.h"
36 #include "JSBoundFunction.h"
37 #include "JSFunctionInlines.h"
38 #include "JSGlobalObject.h"
39 #include "JSNameScope.h"
40 #include "JSNotAnObject.h"
41 #include "Interpreter.h"
42 #include "ObjectConstructor.h"
43 #include "ObjectPrototype.h"
44 #include "JSCInlines.h"
46 #include "PropertyNameArray.h"
47 #include "StackVisitor.h"
51 EncodedJSValue JSC_HOST_CALL
callHostFunctionAsConstructor(ExecState
* exec
)
53 return throwVMError(exec
, createNotAConstructorError(exec
, exec
->callee()));
56 const ClassInfo
JSFunction::s_info
= { "Function", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(JSFunction
) };
58 bool JSFunction::isHostFunctionNonInline() const
60 return isHostFunction();
63 JSFunction
* JSFunction::create(VM
& vm
, JSGlobalObject
* globalObject
, int length
, const String
& name
, NativeFunction nativeFunction
, Intrinsic intrinsic
, NativeFunction nativeConstructor
)
65 NativeExecutable
* executable
;
67 UNUSED_PARAM(intrinsic
);
69 if (intrinsic
!= NoIntrinsic
&& vm
.canUseJIT()) {
70 ASSERT(nativeConstructor
== callHostFunctionAsConstructor
);
71 executable
= vm
.getHostFunction(nativeFunction
, intrinsic
);
74 executable
= vm
.getHostFunction(nativeFunction
, nativeConstructor
);
76 JSFunction
* function
= new (NotNull
, allocateCell
<JSFunction
>(vm
.heap
)) JSFunction(vm
, globalObject
, globalObject
->functionStructure());
77 // Can't do this during initialization because getHostFunction might do a GC allocation.
78 function
->finishCreation(vm
, executable
, length
, name
);
82 void JSFunction::destroy(JSCell
* cell
)
84 static_cast<JSFunction
*>(cell
)->JSFunction::~JSFunction();
87 JSFunction::JSFunction(VM
& vm
, JSGlobalObject
* globalObject
, Structure
* structure
)
90 , m_scope(vm
, this, globalObject
)
91 // We initialize blind so that changes to the prototype after function creation but before
92 // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the
93 // watchpoint will start watching and any changes will both force deoptimization and disable
94 // future attempts to optimize. This is necessary because we are guaranteed that the
95 // allocation profile is changed exactly once prior to optimizations kicking in. We could be
96 // smarter and count the number of times the prototype is clobbered and only optimize if it
97 // was clobbered exactly once, but that seems like overkill. In almost all cases it will be
98 // clobbered once, and if it's clobbered more than once, that will probably only occur
99 // before we started optimizing, anyway.
100 , m_allocationProfileWatchpoint(ClearWatchpoint
)
104 void JSFunction::finishCreation(VM
& vm
, NativeExecutable
* executable
, int length
, const String
& name
)
106 Base::finishCreation(vm
);
107 ASSERT(inherits(info()));
108 m_executable
.set(vm
, this, executable
);
109 putDirect(vm
, vm
.propertyNames
->name
, jsString(&vm
, name
), DontDelete
| ReadOnly
| DontEnum
);
110 putDirect(vm
, vm
.propertyNames
->length
, jsNumber(length
), DontDelete
| ReadOnly
| DontEnum
);
113 void JSFunction::addNameScopeIfNeeded(VM
& vm
)
115 FunctionExecutable
* executable
= jsCast
<FunctionExecutable
*>(m_executable
.get());
116 if (!functionNameIsInScope(executable
->name(), executable
->functionMode()))
118 if (!functionNameScopeIsDynamic(executable
->usesEval(), executable
->isStrictMode()))
120 m_scope
.set(vm
, this, JSNameScope::create(vm
, m_scope
->globalObject(), executable
->name(), this, ReadOnly
| DontDelete
, m_scope
.get()));
123 JSFunction
* JSFunction::createBuiltinFunction(VM
& vm
, FunctionExecutable
* executable
, JSGlobalObject
* globalObject
)
125 JSFunction
* function
= create(vm
, executable
, globalObject
);
126 function
->putDirect(vm
, vm
.propertyNames
->name
, jsString(&vm
, executable
->name().string()), DontDelete
| ReadOnly
| DontEnum
);
127 function
->putDirect(vm
, vm
.propertyNames
->length
, jsNumber(executable
->parameterCount()), DontDelete
| ReadOnly
| DontEnum
);
131 ObjectAllocationProfile
* JSFunction::createAllocationProfile(ExecState
* exec
, size_t inlineCapacity
)
134 JSObject
* prototype
= jsDynamicCast
<JSObject
*>(get(exec
, vm
.propertyNames
->prototype
));
136 prototype
= globalObject()->objectPrototype();
137 m_allocationProfile
.initialize(globalObject()->vm(), this, prototype
, inlineCapacity
);
138 return &m_allocationProfile
;
141 String
JSFunction::name(ExecState
* exec
)
143 return get(exec
, exec
->vm().propertyNames
->name
).toWTFString(exec
);
146 String
JSFunction::displayName(ExecState
* exec
)
148 JSValue displayName
= getDirect(exec
->vm(), exec
->vm().propertyNames
->displayName
);
150 if (displayName
&& isJSString(displayName
))
151 return asString(displayName
)->tryGetValue();
156 const String
JSFunction::calculatedDisplayName(ExecState
* exec
)
158 const String explicitName
= displayName(exec
);
160 if (!explicitName
.isEmpty())
163 const String actualName
= name(exec
);
164 if (!actualName
.isEmpty() || isHostOrBuiltinFunction())
167 return jsExecutable()->inferredName().string();
170 const SourceCode
* JSFunction::sourceCode() const
172 if (isHostOrBuiltinFunction())
174 return &jsExecutable()->source();
177 void JSFunction::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
179 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
180 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
181 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
182 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
183 Base::visitChildren(thisObject
, visitor
);
185 visitor
.append(&thisObject
->m_scope
);
186 visitor
.append(&thisObject
->m_executable
);
187 thisObject
->m_allocationProfile
.visitAggregate(visitor
);
190 CallType
JSFunction::getCallData(JSCell
* cell
, CallData
& callData
)
192 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
193 if (thisObject
->isHostFunction()) {
194 callData
.native
.function
= thisObject
->nativeFunction();
197 callData
.js
.functionExecutable
= thisObject
->jsExecutable();
198 callData
.js
.scope
= thisObject
->scope();
202 class RetrieveArgumentsFunctor
{
204 RetrieveArgumentsFunctor(JSFunction
* functionObj
)
205 : m_targetCallee(jsDynamicCast
<JSObject
*>(functionObj
))
210 JSValue
result() const { return m_result
; }
212 StackVisitor::Status
operator()(StackVisitor
& visitor
)
214 JSObject
* callee
= visitor
->callee();
215 if (callee
!= m_targetCallee
)
216 return StackVisitor::Continue
;
218 m_result
= JSValue(visitor
->createArguments());
219 return StackVisitor::Done
;
223 JSObject
* m_targetCallee
;
227 static JSValue
retrieveArguments(ExecState
* exec
, JSFunction
* functionObj
)
229 RetrieveArgumentsFunctor
functor(functionObj
);
230 exec
->iterate(functor
);
231 return functor
.result();
234 EncodedJSValue
JSFunction::argumentsGetter(ExecState
* exec
, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
236 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
237 ASSERT(!thisObj
->isHostFunction());
239 return JSValue::encode(retrieveArguments(exec
, thisObj
));
242 class RetrieveCallerFunctionFunctor
{
244 RetrieveCallerFunctionFunctor(JSFunction
* functionObj
)
245 : m_targetCallee(jsDynamicCast
<JSObject
*>(functionObj
))
246 , m_hasFoundFrame(false)
247 , m_hasSkippedToCallerFrame(false)
252 JSValue
result() const { return m_result
; }
254 StackVisitor::Status
operator()(StackVisitor
& visitor
)
256 JSObject
* callee
= visitor
->callee();
258 if (callee
&& callee
->inherits(JSBoundFunction::info()))
259 return StackVisitor::Continue
;
261 if (!m_hasFoundFrame
&& (callee
!= m_targetCallee
))
262 return StackVisitor::Continue
;
264 m_hasFoundFrame
= true;
265 if (!m_hasSkippedToCallerFrame
) {
266 m_hasSkippedToCallerFrame
= true;
267 return StackVisitor::Continue
;
272 return StackVisitor::Done
;
276 JSObject
* m_targetCallee
;
277 bool m_hasFoundFrame
;
278 bool m_hasSkippedToCallerFrame
;
282 static JSValue
retrieveCallerFunction(ExecState
* exec
, JSFunction
* functionObj
)
284 RetrieveCallerFunctionFunctor
functor(functionObj
);
285 exec
->iterate(functor
);
286 return functor
.result();
289 EncodedJSValue
JSFunction::callerGetter(ExecState
* exec
, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
291 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
292 ASSERT(!thisObj
->isHostFunction());
293 JSValue caller
= retrieveCallerFunction(exec
, thisObj
);
295 // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller.
296 if (!caller
.isObject() || !asObject(caller
)->inherits(JSFunction::info()))
297 return JSValue::encode(caller
);
298 JSFunction
* function
= jsCast
<JSFunction
*>(caller
);
299 if (function
->isHostOrBuiltinFunction() || !function
->jsExecutable()->isStrictMode())
300 return JSValue::encode(caller
);
301 return JSValue::encode(throwTypeError(exec
, ASCIILiteral("Function.caller used to retrieve strict caller")));
304 EncodedJSValue
JSFunction::lengthGetter(ExecState
*, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
306 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
307 ASSERT(!thisObj
->isHostFunction());
308 return JSValue::encode(jsNumber(thisObj
->jsExecutable()->parameterCount()));
311 EncodedJSValue
JSFunction::nameGetter(ExecState
*, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
313 JSFunction
* thisObj
= jsCast
<JSFunction
*>(slotBase
);
314 ASSERT(!thisObj
->isHostFunction());
315 return JSValue::encode(thisObj
->jsExecutable()->nameValue());
318 bool JSFunction::getOwnPropertySlot(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
320 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
321 if (thisObject
->isHostOrBuiltinFunction())
322 return Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
324 if (propertyName
== exec
->propertyNames().prototype
) {
327 PropertyOffset offset
= thisObject
->getDirectOffset(vm
, propertyName
, attributes
);
328 if (!isValidOffset(offset
)) {
329 JSObject
* prototype
= constructEmptyObject(exec
);
330 prototype
->putDirect(vm
, exec
->propertyNames().constructor
, thisObject
, DontEnum
);
331 thisObject
->putDirect(vm
, exec
->propertyNames().prototype
, prototype
, DontDelete
| DontEnum
);
332 offset
= thisObject
->getDirectOffset(vm
, exec
->propertyNames().prototype
, attributes
);
333 ASSERT(isValidOffset(offset
));
336 slot
.setValue(thisObject
, attributes
, thisObject
->getDirect(offset
), offset
);
339 if (propertyName
== exec
->propertyNames().arguments
) {
340 if (thisObject
->jsExecutable()->isStrictMode()) {
341 bool result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
343 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
->vm()), DontDelete
| DontEnum
| Accessor
);
344 result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
349 slot
.setCacheableCustom(thisObject
, ReadOnly
| DontEnum
| DontDelete
, argumentsGetter
);
353 if (propertyName
== exec
->propertyNames().length
) {
354 slot
.setCacheableCustom(thisObject
, ReadOnly
| DontEnum
| DontDelete
, lengthGetter
);
358 if (propertyName
== exec
->propertyNames().name
) {
359 slot
.setCacheableCustom(thisObject
, ReadOnly
| DontEnum
| DontDelete
, nameGetter
);
363 if (propertyName
== exec
->propertyNames().caller
) {
364 if (thisObject
->jsExecutable()->isStrictMode()) {
365 bool result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
367 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
->vm()), DontDelete
| DontEnum
| Accessor
);
368 result
= Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
373 slot
.setCacheableCustom(thisObject
, ReadOnly
| DontEnum
| DontDelete
, callerGetter
);
377 return Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
380 void JSFunction::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
382 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
383 if (!thisObject
->isHostOrBuiltinFunction() && (mode
== IncludeDontEnumProperties
)) {
385 // Make sure prototype has been reified.
386 PropertySlot
slot(thisObject
);
387 thisObject
->methodTable(vm
)->getOwnPropertySlot(thisObject
, exec
, vm
.propertyNames
->prototype
, slot
);
389 propertyNames
.add(vm
.propertyNames
->arguments
);
390 propertyNames
.add(vm
.propertyNames
->caller
);
391 propertyNames
.add(vm
.propertyNames
->length
);
392 propertyNames
.add(vm
.propertyNames
->name
);
394 Base::getOwnNonIndexPropertyNames(thisObject
, exec
, propertyNames
, mode
);
397 void JSFunction::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
399 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
400 if (thisObject
->isHostOrBuiltinFunction()) {
401 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
404 if (propertyName
== exec
->propertyNames().prototype
) {
405 // Make sure prototype has been reified, such that it can only be overwritten
406 // following the rules set out in ECMA-262 8.12.9.
407 PropertySlot
slot(thisObject
);
408 thisObject
->methodTable(exec
->vm())->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
409 thisObject
->m_allocationProfile
.clear();
410 thisObject
->m_allocationProfileWatchpoint
.fireAll();
411 // Don't allow this to be cached, since a [[Put]] must clear m_allocationProfile.
412 PutPropertySlot
dontCache(thisObject
);
413 Base::put(thisObject
, exec
, propertyName
, value
, dontCache
);
416 if (thisObject
->jsExecutable()->isStrictMode() && (propertyName
== exec
->propertyNames().arguments
|| propertyName
== exec
->propertyNames().caller
)) {
417 // This will trigger the property to be reified, if this is not already the case!
418 bool okay
= thisObject
->hasProperty(exec
, propertyName
);
419 ASSERT_UNUSED(okay
, okay
);
420 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
423 if (propertyName
== exec
->propertyNames().arguments
|| propertyName
== exec
->propertyNames().length
|| propertyName
== exec
->propertyNames().name
|| propertyName
== exec
->propertyNames().caller
) {
424 if (slot
.isStrictMode())
425 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
428 Base::put(thisObject
, exec
, propertyName
, value
, slot
);
431 bool JSFunction::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
433 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
434 // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
435 if (!thisObject
->isHostOrBuiltinFunction() && !exec
->vm().isInDefineOwnProperty()
436 && (propertyName
== exec
->propertyNames().arguments
437 || propertyName
== exec
->propertyNames().length
438 || propertyName
== exec
->propertyNames().name
439 || propertyName
== exec
->propertyNames().prototype
440 || propertyName
== exec
->propertyNames().caller
))
442 return Base::deleteProperty(thisObject
, exec
, propertyName
);
445 bool JSFunction::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
447 JSFunction
* thisObject
= jsCast
<JSFunction
*>(object
);
448 if (thisObject
->isHostOrBuiltinFunction())
449 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
451 if (propertyName
== exec
->propertyNames().prototype
) {
452 // Make sure prototype has been reified, such that it can only be overwritten
453 // following the rules set out in ECMA-262 8.12.9.
454 PropertySlot
slot(thisObject
);
455 thisObject
->methodTable(exec
->vm())->getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
456 thisObject
->m_allocationProfile
.clear();
457 thisObject
->m_allocationProfileWatchpoint
.fireAll();
458 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
462 if (propertyName
== exec
->propertyNames().arguments
) {
463 if (thisObject
->jsExecutable()->isStrictMode()) {
464 PropertySlot
slot(thisObject
);
465 if (!Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
))
466 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
->vm()), DontDelete
| DontEnum
| Accessor
);
467 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
469 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), retrieveArguments(exec
, thisObject
));
470 } else if (propertyName
== exec
->propertyNames().caller
) {
471 if (thisObject
->jsExecutable()->isStrictMode()) {
472 PropertySlot
slot(thisObject
);
473 if (!Base::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
))
474 thisObject
->putDirectAccessor(exec
, propertyName
, thisObject
->globalObject()->throwTypeErrorGetterSetter(exec
->vm()), DontDelete
| DontEnum
| Accessor
);
475 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
477 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), retrieveCallerFunction(exec
, thisObject
));
478 } else if (propertyName
== exec
->propertyNames().length
)
479 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), jsNumber(thisObject
->jsExecutable()->parameterCount()));
480 else if (propertyName
== exec
->propertyNames().name
)
481 valueCheck
= !descriptor
.value() || sameValue(exec
, descriptor
.value(), thisObject
->jsExecutable()->nameValue());
483 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
485 if (descriptor
.configurablePresent() && descriptor
.configurable()) {
487 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
490 if (descriptor
.enumerablePresent() && descriptor
.enumerable()) {
492 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
495 if (descriptor
.isAccessorDescriptor()) {
497 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
500 if (descriptor
.writablePresent() && descriptor
.writable()) {
502 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
507 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change value of a readonly property.")));
513 // ECMA 13.2.2 [[Construct]]
514 ConstructType
JSFunction::getConstructData(JSCell
* cell
, ConstructData
& constructData
)
516 JSFunction
* thisObject
= jsCast
<JSFunction
*>(cell
);
517 if (thisObject
->isHostFunction()) {
518 constructData
.native
.function
= thisObject
->nativeConstructor();
519 return ConstructTypeHost
;
521 constructData
.js
.functionExecutable
= thisObject
->jsExecutable();
522 constructData
.js
.scope
= thisObject
->scope();
523 return ConstructTypeJS
;
526 String
getCalculatedDisplayName(CallFrame
* callFrame
, JSObject
* object
)
528 if (JSFunction
* function
= jsDynamicCast
<JSFunction
*>(object
))
529 return function
->calculatedDisplayName(callFrame
);
530 if (InternalFunction
* function
= jsDynamicCast
<InternalFunction
*>(object
))
531 return function
->calculatedDisplayName(callFrame
);