2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "JSCallbackFunction.h"
30 #include "JSClassRef.h"
31 #include "JSGlobalObject.h"
33 #include "JSObjectRef.h"
35 #include "JSStringRef.h"
36 #include "OpaqueJSString.h"
37 #include "PropertyNameArray.h"
38 #include <wtf/Vector.h>
43 inline JSCallbackObject
<Base
>* JSCallbackObject
<Base
>::asCallbackObject(JSValuePtr value
)
45 ASSERT(asObject(value
)->inherits(&info
));
46 return static_cast<JSCallbackObject
*>(asObject(value
));
50 JSCallbackObject
<Base
>::JSCallbackObject(ExecState
* exec
, PassRefPtr
<Structure
> structure
, JSClassRef jsClass
, void* data
)
52 , m_callbackObjectData(new JSCallbackObjectData(data
, jsClass
))
57 // Global object constructor.
58 // FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one.
60 JSCallbackObject
<Base
>::JSCallbackObject(JSClassRef jsClass
)
62 , m_callbackObjectData(new JSCallbackObjectData(0, jsClass
))
64 ASSERT(Base::isGlobalObject());
65 init(static_cast<JSGlobalObject
*>(this)->globalExec());
69 void JSCallbackObject
<Base
>::init(ExecState
* exec
)
73 Vector
<JSObjectInitializeCallback
, 16> initRoutines
;
74 JSClassRef jsClass
= classRef();
76 if (JSObjectInitializeCallback initialize
= jsClass
->initialize
)
77 initRoutines
.append(initialize
);
78 } while ((jsClass
= jsClass
->parentClass
));
80 // initialize from base to derived
81 for (int i
= static_cast<int>(initRoutines
.size()) - 1; i
>= 0; i
--) {
82 JSLock::DropAllLocks
dropAllLocks(exec
);
83 JSObjectInitializeCallback initialize
= initRoutines
[i
];
84 initialize(toRef(exec
), toRef(this));
89 JSCallbackObject
<Base
>::~JSCallbackObject()
91 JSObjectRef thisRef
= toRef(this);
93 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
94 if (JSObjectFinalizeCallback finalize
= jsClass
->finalize
)
99 UString JSCallbackObject
<Base
>::className() const
101 UString thisClassName
= classRef()->className();
102 if (!thisClassName
.isNull())
103 return thisClassName
;
105 return Base::className();
108 template <class Base
>
109 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
111 JSContextRef ctx
= toRef(exec
);
112 JSObjectRef thisRef
= toRef(this);
113 RefPtr
<OpaqueJSString
> propertyNameRef
;
115 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
116 // optional optimization to bypass getProperty in cases when we only need to know if the property exists
117 if (JSObjectHasPropertyCallback hasProperty
= jsClass
->hasProperty
) {
118 if (!propertyNameRef
)
119 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
120 JSLock::DropAllLocks
dropAllLocks(exec
);
121 if (hasProperty(ctx
, thisRef
, propertyNameRef
.get())) {
122 slot
.setCustom(this, callbackGetter
);
125 } else if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
126 if (!propertyNameRef
)
127 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
128 JSLock::DropAllLocks
dropAllLocks(exec
);
129 if (JSValueRef value
= getProperty(ctx
, thisRef
, propertyNameRef
.get(), toRef(exec
->exceptionSlot()))) {
130 slot
.setValue(toJS(value
));
135 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
136 if (staticValues
->contains(propertyName
.ustring().rep())) {
137 slot
.setCustom(this, staticValueGetter
);
142 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
143 if (staticFunctions
->contains(propertyName
.ustring().rep())) {
144 slot
.setCustom(this, staticFunctionGetter
);
150 return Base::getOwnPropertySlot(exec
, propertyName
, slot
);
153 template <class Base
>
154 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
156 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
159 template <class Base
>
160 void JSCallbackObject
<Base
>::put(ExecState
* exec
, const Identifier
& propertyName
, JSValuePtr value
, PutPropertySlot
& slot
)
162 JSContextRef ctx
= toRef(exec
);
163 JSObjectRef thisRef
= toRef(this);
164 RefPtr
<OpaqueJSString
> propertyNameRef
;
165 JSValueRef valueRef
= toRef(value
);
167 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
168 if (JSObjectSetPropertyCallback setProperty
= jsClass
->setProperty
) {
169 if (!propertyNameRef
)
170 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
171 JSLock::DropAllLocks
dropAllLocks(exec
);
172 if (setProperty(ctx
, thisRef
, propertyNameRef
.get(), valueRef
, toRef(exec
->exceptionSlot())))
176 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
177 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
178 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
180 if (JSObjectSetPropertyCallback setProperty
= entry
->setProperty
) {
181 if (!propertyNameRef
)
182 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
183 JSLock::DropAllLocks
dropAllLocks(exec
);
184 if (setProperty(ctx
, thisRef
, propertyNameRef
.get(), valueRef
, toRef(exec
->exceptionSlot())))
187 throwError(exec
, ReferenceError
, "Attempt to set a property that is not settable.");
191 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
192 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
193 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
195 JSCallbackObject
<Base
>::putDirect(propertyName
, value
); // put as override property
201 return Base::put(exec
, propertyName
, value
, slot
);
204 template <class Base
>
205 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
207 JSContextRef ctx
= toRef(exec
);
208 JSObjectRef thisRef
= toRef(this);
209 RefPtr
<OpaqueJSString
> propertyNameRef
;
211 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
212 if (JSObjectDeletePropertyCallback deleteProperty
= jsClass
->deleteProperty
) {
213 if (!propertyNameRef
)
214 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
215 JSLock::DropAllLocks
dropAllLocks(exec
);
216 if (deleteProperty(ctx
, thisRef
, propertyNameRef
.get(), toRef(exec
->exceptionSlot())))
220 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
221 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
222 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
228 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
229 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
230 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
237 return Base::deleteProperty(exec
, propertyName
);
240 template <class Base
>
241 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, unsigned propertyName
)
243 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
246 template <class Base
>
247 ConstructType JSCallbackObject
<Base
>::getConstructData(ConstructData
& constructData
)
249 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
250 if (jsClass
->callAsConstructor
) {
251 constructData
.native
.function
= construct
;
252 return ConstructTypeHost
;
255 return ConstructTypeNone
;
258 template <class Base
>
259 JSObject
* JSCallbackObject
<Base
>::construct(ExecState
* exec
, JSObject
* constructor
, const ArgList
& args
)
261 JSContextRef execRef
= toRef(exec
);
262 JSObjectRef constructorRef
= toRef(constructor
);
264 for (JSClassRef jsClass
= static_cast<JSCallbackObject
<Base
>*>(constructor
)->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
265 if (JSObjectCallAsConstructorCallback callAsConstructor
= jsClass
->callAsConstructor
) {
266 int argumentCount
= static_cast<int>(args
.size());
267 Vector
<JSValueRef
, 16> arguments(argumentCount
);
268 for (int i
= 0; i
< argumentCount
; i
++)
269 arguments
[i
] = toRef(args
.at(exec
, i
));
270 JSLock::DropAllLocks
dropAllLocks(exec
);
271 return toJS(callAsConstructor(execRef
, constructorRef
, argumentCount
, arguments
.data(), toRef(exec
->exceptionSlot())));
275 ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
279 template <class Base
>
280 bool JSCallbackObject
<Base
>::hasInstance(ExecState
* exec
, JSValuePtr value
, JSValuePtr
)
282 JSContextRef execRef
= toRef(exec
);
283 JSObjectRef thisRef
= toRef(this);
285 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
286 if (JSObjectHasInstanceCallback hasInstance
= jsClass
->hasInstance
) {
287 JSLock::DropAllLocks
dropAllLocks(exec
);
288 return hasInstance(execRef
, thisRef
, toRef(value
), toRef(exec
->exceptionSlot()));
294 template <class Base
>
295 CallType JSCallbackObject
<Base
>::getCallData(CallData
& callData
)
297 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
298 if (jsClass
->callAsFunction
) {
299 callData
.native
.function
= call
;
306 template <class Base
>
307 JSValuePtr JSCallbackObject
<Base
>::call(ExecState
* exec
, JSObject
* functionObject
, JSValuePtr thisValue
, const ArgList
& args
)
309 JSContextRef execRef
= toRef(exec
);
310 JSObjectRef functionRef
= toRef(functionObject
);
311 JSObjectRef thisObjRef
= toRef(thisValue
.toThisObject(exec
));
313 for (JSClassRef jsClass
= static_cast<JSCallbackObject
<Base
>*>(functionObject
)->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
314 if (JSObjectCallAsFunctionCallback callAsFunction
= jsClass
->callAsFunction
) {
315 int argumentCount
= static_cast<int>(args
.size());
316 Vector
<JSValueRef
, 16> arguments(argumentCount
);
317 for (int i
= 0; i
< argumentCount
; i
++)
318 arguments
[i
] = toRef(args
.at(exec
, i
));
319 JSLock::DropAllLocks
dropAllLocks(exec
);
320 return toJS(callAsFunction(execRef
, functionRef
, thisObjRef
, argumentCount
, arguments
.data(), toRef(exec
->exceptionSlot())));
324 ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
328 template <class Base
>
329 void JSCallbackObject
<Base
>::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
331 JSContextRef execRef
= toRef(exec
);
332 JSObjectRef thisRef
= toRef(this);
334 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
335 if (JSObjectGetPropertyNamesCallback getPropertyNames
= jsClass
->getPropertyNames
) {
336 JSLock::DropAllLocks
dropAllLocks(exec
);
337 getPropertyNames(execRef
, thisRef
, toRef(&propertyNames
));
340 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
341 typedef OpaqueJSClassStaticValuesTable::const_iterator iterator
;
342 iterator end
= staticValues
->end();
343 for (iterator it
= staticValues
->begin(); it
!= end
; ++it
) {
344 UString::Rep
* name
= it
->first
.get();
345 StaticValueEntry
* entry
= it
->second
;
346 if (entry
->getProperty
&& !(entry
->attributes
& kJSPropertyAttributeDontEnum
))
347 propertyNames
.add(Identifier(exec
, name
));
351 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
352 typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator
;
353 iterator end
= staticFunctions
->end();
354 for (iterator it
= staticFunctions
->begin(); it
!= end
; ++it
) {
355 UString::Rep
* name
= it
->first
.get();
356 StaticFunctionEntry
* entry
= it
->second
;
357 if (!(entry
->attributes
& kJSPropertyAttributeDontEnum
))
358 propertyNames
.add(Identifier(exec
, name
));
363 Base::getPropertyNames(exec
, propertyNames
);
366 template <class Base
>
367 double JSCallbackObject
<Base
>::toNumber(ExecState
* exec
) const
369 // We need this check to guard against the case where this object is rhs of
370 // a binary expression where lhs threw an exception in its conversion to
372 if (exec
->hadException())
374 JSContextRef ctx
= toRef(exec
);
375 JSObjectRef thisRef
= toRef(this);
377 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
378 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
379 JSLock::DropAllLocks
dropAllLocks(exec
);
380 if (JSValueRef value
= convertToType(ctx
, thisRef
, kJSTypeNumber
, toRef(exec
->exceptionSlot()))) {
382 return toJS(value
).getNumber(dValue
) ? dValue
: NaN
;
386 return Base::toNumber(exec
);
389 template <class Base
>
390 UString JSCallbackObject
<Base
>::toString(ExecState
* exec
) const
392 JSContextRef ctx
= toRef(exec
);
393 JSObjectRef thisRef
= toRef(this);
395 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
396 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
399 JSLock::DropAllLocks
dropAllLocks(exec
);
400 value
= convertToType(ctx
, thisRef
, kJSTypeString
, toRef(exec
->exceptionSlot()));
403 return toJS(value
).getString();
406 return Base::toString(exec
);
409 template <class Base
>
410 void JSCallbackObject
<Base
>::setPrivate(void* data
)
412 m_callbackObjectData
->privateData
= data
;
415 template <class Base
>
416 void* JSCallbackObject
<Base
>::getPrivate()
418 return m_callbackObjectData
->privateData
;
421 template <class Base
>
422 bool JSCallbackObject
<Base
>::inherits(JSClassRef c
) const
424 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
431 template <class Base
>
432 JSValuePtr JSCallbackObject
<Base
>::staticValueGetter(ExecState
* exec
, const Identifier
& propertyName
, const PropertySlot
& slot
)
434 JSCallbackObject
* thisObj
= asCallbackObject(slot
.slotBase());
436 JSObjectRef thisRef
= toRef(thisObj
);
437 RefPtr
<OpaqueJSString
> propertyNameRef
;
439 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
440 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
))
441 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep()))
442 if (JSObjectGetPropertyCallback getProperty
= entry
->getProperty
) {
443 if (!propertyNameRef
)
444 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
445 JSLock::DropAllLocks
dropAllLocks(exec
);
446 if (JSValueRef value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
.get(), toRef(exec
->exceptionSlot())))
450 return throwError(exec
, ReferenceError
, "Static value property defined with NULL getProperty callback.");
453 template <class Base
>
454 JSValuePtr JSCallbackObject
<Base
>::staticFunctionGetter(ExecState
* exec
, const Identifier
& propertyName
, const PropertySlot
& slot
)
456 JSCallbackObject
* thisObj
= asCallbackObject(slot
.slotBase());
458 // Check for cached or override property.
459 PropertySlot
slot2(thisObj
);
460 if (thisObj
->Base::getOwnPropertySlot(exec
, propertyName
, slot2
))
461 return slot2
.getValue(exec
, propertyName
);
463 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
464 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
465 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
466 if (JSObjectCallAsFunctionCallback callAsFunction
= entry
->callAsFunction
) {
467 JSObject
* o
= new (exec
) JSCallbackFunction(exec
, callAsFunction
, propertyName
);
468 thisObj
->putDirect(propertyName
, o
, entry
->attributes
);
475 return throwError(exec
, ReferenceError
, "Static function property defined with NULL callAsFunction callback.");
478 template <class Base
>
479 JSValuePtr JSCallbackObject
<Base
>::callbackGetter(ExecState
* exec
, const Identifier
& propertyName
, const PropertySlot
& slot
)
481 JSCallbackObject
* thisObj
= asCallbackObject(slot
.slotBase());
483 JSObjectRef thisRef
= toRef(thisObj
);
484 RefPtr
<OpaqueJSString
> propertyNameRef
;
486 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
487 if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
488 if (!propertyNameRef
)
489 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
490 JSLock::DropAllLocks
dropAllLocks(exec
);
491 if (JSValueRef value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
.get(), toRef(exec
->exceptionSlot())))
495 return throwError(exec
, ReferenceError
, "hasProperty callback returned true for a property that doesn't exist.");