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.
30 #include "JSCallbackFunction.h"
31 #include "JSClassRef.h"
32 #include "JSGlobalObject.h"
34 #include "JSObjectRef.h"
36 #include "JSStringRef.h"
37 #include "OpaqueJSString.h"
38 #include "PropertyNameArray.h"
39 #include <wtf/Vector.h>
44 inline JSCallbackObject
<Base
>* JSCallbackObject
<Base
>::asCallbackObject(JSValue value
)
46 ASSERT(asObject(value
)->inherits(&info
));
47 return static_cast<JSCallbackObject
*>(asObject(value
));
51 JSCallbackObject
<Base
>::JSCallbackObject(ExecState
* exec
, NonNullPassRefPtr
<Structure
> structure
, JSClassRef jsClass
, void* data
)
53 , m_callbackObjectData(new JSCallbackObjectData(data
, jsClass
))
58 // Global object constructor.
59 // FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one.
61 JSCallbackObject
<Base
>::JSCallbackObject(JSClassRef jsClass
)
63 , m_callbackObjectData(new JSCallbackObjectData(0, jsClass
))
65 ASSERT(Base::isGlobalObject());
66 init(static_cast<JSGlobalObject
*>(this)->globalExec());
70 void JSCallbackObject
<Base
>::init(ExecState
* exec
)
74 Vector
<JSObjectInitializeCallback
, 16> initRoutines
;
75 JSClassRef jsClass
= classRef();
77 if (JSObjectInitializeCallback initialize
= jsClass
->initialize
)
78 initRoutines
.append(initialize
);
79 } while ((jsClass
= jsClass
->parentClass
));
81 // initialize from base to derived
82 for (int i
= static_cast<int>(initRoutines
.size()) - 1; i
>= 0; i
--) {
83 APICallbackShim
callbackShim(exec
);
84 JSObjectInitializeCallback initialize
= initRoutines
[i
];
85 initialize(toRef(exec
), toRef(this));
90 JSCallbackObject
<Base
>::~JSCallbackObject()
92 JSObjectRef thisRef
= toRef(this);
94 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
95 if (JSObjectFinalizeCallback finalize
= jsClass
->finalize
)
100 UString JSCallbackObject
<Base
>::className() const
102 UString thisClassName
= classRef()->className();
103 if (!thisClassName
.isEmpty())
104 return thisClassName
;
106 return Base::className();
109 template <class Base
>
110 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
112 JSContextRef ctx
= toRef(exec
);
113 JSObjectRef thisRef
= toRef(this);
114 RefPtr
<OpaqueJSString
> propertyNameRef
;
116 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
117 // optional optimization to bypass getProperty in cases when we only need to know if the property exists
118 if (JSObjectHasPropertyCallback hasProperty
= jsClass
->hasProperty
) {
119 if (!propertyNameRef
)
120 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
121 APICallbackShim
callbackShim(exec
);
122 if (hasProperty(ctx
, thisRef
, propertyNameRef
.get())) {
123 slot
.setCustom(this, callbackGetter
);
126 } else if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
127 if (!propertyNameRef
)
128 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
129 JSValueRef exception
= 0;
132 APICallbackShim
callbackShim(exec
);
133 value
= getProperty(ctx
, thisRef
, propertyNameRef
.get(), &exception
);
136 exec
->setException(toJS(exec
, exception
));
137 slot
.setValue(jsUndefined());
141 slot
.setValue(toJS(exec
, value
));
146 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
147 if (staticValues
->contains(propertyName
.ustring().rep())) {
148 slot
.setCustom(this, staticValueGetter
);
153 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
154 if (staticFunctions
->contains(propertyName
.ustring().rep())) {
155 slot
.setCustom(this, staticFunctionGetter
);
161 return Base::getOwnPropertySlot(exec
, propertyName
, slot
);
164 template <class Base
>
165 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
167 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
170 template <class Base
>
171 bool JSCallbackObject
<Base
>::getOwnPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
174 if (getOwnPropertySlot(exec
, propertyName
, slot
)) {
175 // Ideally we should return an access descriptor, but returning a value descriptor is better than nothing.
176 JSValue value
= slot
.getValue(exec
, propertyName
);
177 if (!exec
->hadException())
178 descriptor
.setValue(value
);
179 // We don't know whether the property is configurable, but assume it is.
180 descriptor
.setConfigurable(true);
181 // We don't know whether the property is enumerable (we could call getOwnPropertyNames() to find out), but assume it isn't.
182 descriptor
.setEnumerable(false);
186 return Base::getOwnPropertyDescriptor(exec
, propertyName
, descriptor
);
189 template <class Base
>
190 void JSCallbackObject
<Base
>::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
192 JSContextRef ctx
= toRef(exec
);
193 JSObjectRef thisRef
= toRef(this);
194 RefPtr
<OpaqueJSString
> propertyNameRef
;
195 JSValueRef valueRef
= toRef(exec
, value
);
197 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
198 if (JSObjectSetPropertyCallback setProperty
= jsClass
->setProperty
) {
199 if (!propertyNameRef
)
200 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
201 JSValueRef exception
= 0;
204 APICallbackShim
callbackShim(exec
);
205 result
= setProperty(ctx
, thisRef
, propertyNameRef
.get(), valueRef
, &exception
);
208 exec
->setException(toJS(exec
, exception
));
209 if (result
|| exception
)
213 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
214 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
215 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
217 if (JSObjectSetPropertyCallback setProperty
= entry
->setProperty
) {
218 if (!propertyNameRef
)
219 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
220 JSValueRef exception
= 0;
223 APICallbackShim
callbackShim(exec
);
224 result
= setProperty(ctx
, thisRef
, propertyNameRef
.get(), valueRef
, &exception
);
227 exec
->setException(toJS(exec
, exception
));
228 if (result
|| exception
)
231 throwError(exec
, ReferenceError
, "Attempt to set a property that is not settable.");
235 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
236 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
237 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
239 JSCallbackObject
<Base
>::putDirect(propertyName
, value
); // put as override property
245 return Base::put(exec
, propertyName
, value
, slot
);
248 template <class Base
>
249 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
251 JSContextRef ctx
= toRef(exec
);
252 JSObjectRef thisRef
= toRef(this);
253 RefPtr
<OpaqueJSString
> propertyNameRef
;
255 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
256 if (JSObjectDeletePropertyCallback deleteProperty
= jsClass
->deleteProperty
) {
257 if (!propertyNameRef
)
258 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
259 JSValueRef exception
= 0;
262 APICallbackShim
callbackShim(exec
);
263 result
= deleteProperty(ctx
, thisRef
, propertyNameRef
.get(), &exception
);
266 exec
->setException(toJS(exec
, exception
));
267 if (result
|| exception
)
271 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
272 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
273 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
279 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
280 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
281 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
288 return Base::deleteProperty(exec
, propertyName
);
291 template <class Base
>
292 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, unsigned propertyName
)
294 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
297 template <class Base
>
298 ConstructType JSCallbackObject
<Base
>::getConstructData(ConstructData
& constructData
)
300 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
301 if (jsClass
->callAsConstructor
) {
302 constructData
.native
.function
= construct
;
303 return ConstructTypeHost
;
306 return ConstructTypeNone
;
309 template <class Base
>
310 JSObject
* JSCallbackObject
<Base
>::construct(ExecState
* exec
, JSObject
* constructor
, const ArgList
& args
)
312 JSContextRef execRef
= toRef(exec
);
313 JSObjectRef constructorRef
= toRef(constructor
);
315 for (JSClassRef jsClass
= static_cast<JSCallbackObject
<Base
>*>(constructor
)->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
316 if (JSObjectCallAsConstructorCallback callAsConstructor
= jsClass
->callAsConstructor
) {
317 int argumentCount
= static_cast<int>(args
.size());
318 Vector
<JSValueRef
, 16> arguments(argumentCount
);
319 for (int i
= 0; i
< argumentCount
; i
++)
320 arguments
[i
] = toRef(exec
, args
.at(i
));
321 JSValueRef exception
= 0;
324 APICallbackShim
callbackShim(exec
);
325 result
= toJS(callAsConstructor(execRef
, constructorRef
, argumentCount
, arguments
.data(), &exception
));
328 exec
->setException(toJS(exec
, exception
));
333 ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
337 template <class Base
>
338 bool JSCallbackObject
<Base
>::hasInstance(ExecState
* exec
, JSValue value
, JSValue
)
340 JSContextRef execRef
= toRef(exec
);
341 JSObjectRef thisRef
= toRef(this);
343 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
344 if (JSObjectHasInstanceCallback hasInstance
= jsClass
->hasInstance
) {
345 JSValueRef valueRef
= toRef(exec
, value
);
346 JSValueRef exception
= 0;
349 APICallbackShim
callbackShim(exec
);
350 result
= hasInstance(execRef
, thisRef
, valueRef
, &exception
);
353 exec
->setException(toJS(exec
, exception
));
360 template <class Base
>
361 CallType JSCallbackObject
<Base
>::getCallData(CallData
& callData
)
363 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
364 if (jsClass
->callAsFunction
) {
365 callData
.native
.function
= call
;
372 template <class Base
>
373 JSValue JSCallbackObject
<Base
>::call(ExecState
* exec
, JSObject
* functionObject
, JSValue thisValue
, const ArgList
& args
)
375 JSContextRef execRef
= toRef(exec
);
376 JSObjectRef functionRef
= toRef(functionObject
);
377 JSObjectRef thisObjRef
= toRef(thisValue
.toThisObject(exec
));
379 for (JSClassRef jsClass
= static_cast<JSCallbackObject
<Base
>*>(functionObject
)->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
380 if (JSObjectCallAsFunctionCallback callAsFunction
= jsClass
->callAsFunction
) {
381 int argumentCount
= static_cast<int>(args
.size());
382 Vector
<JSValueRef
, 16> arguments(argumentCount
);
383 for (int i
= 0; i
< argumentCount
; i
++)
384 arguments
[i
] = toRef(exec
, args
.at(i
));
385 JSValueRef exception
= 0;
388 APICallbackShim
callbackShim(exec
);
389 result
= toJS(exec
, callAsFunction(execRef
, functionRef
, thisObjRef
, argumentCount
, arguments
.data(), &exception
));
392 exec
->setException(toJS(exec
, exception
));
397 ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
401 template <class Base
>
402 void JSCallbackObject
<Base
>::getOwnPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
404 JSContextRef execRef
= toRef(exec
);
405 JSObjectRef thisRef
= toRef(this);
407 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
408 if (JSObjectGetPropertyNamesCallback getPropertyNames
= jsClass
->getPropertyNames
) {
409 APICallbackShim
callbackShim(exec
);
410 getPropertyNames(execRef
, thisRef
, toRef(&propertyNames
));
413 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
414 typedef OpaqueJSClassStaticValuesTable::const_iterator iterator
;
415 iterator end
= staticValues
->end();
416 for (iterator it
= staticValues
->begin(); it
!= end
; ++it
) {
417 UString::Rep
* name
= it
->first
.get();
418 StaticValueEntry
* entry
= it
->second
;
419 if (entry
->getProperty
&& (!(entry
->attributes
& kJSPropertyAttributeDontEnum
) || (mode
== IncludeDontEnumProperties
)))
420 propertyNames
.add(Identifier(exec
, name
));
424 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
425 typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator
;
426 iterator end
= staticFunctions
->end();
427 for (iterator it
= staticFunctions
->begin(); it
!= end
; ++it
) {
428 UString::Rep
* name
= it
->first
.get();
429 StaticFunctionEntry
* entry
= it
->second
;
430 if (!(entry
->attributes
& kJSPropertyAttributeDontEnum
) || (mode
== IncludeDontEnumProperties
))
431 propertyNames
.add(Identifier(exec
, name
));
436 Base::getOwnPropertyNames(exec
, propertyNames
, mode
);
439 template <class Base
>
440 double JSCallbackObject
<Base
>::toNumber(ExecState
* exec
) const
442 // We need this check to guard against the case where this object is rhs of
443 // a binary expression where lhs threw an exception in its conversion to
445 if (exec
->hadException())
447 JSContextRef ctx
= toRef(exec
);
448 JSObjectRef thisRef
= toRef(this);
450 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
451 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
452 JSValueRef exception
= 0;
455 APICallbackShim
callbackShim(exec
);
456 value
= convertToType(ctx
, thisRef
, kJSTypeNumber
, &exception
);
459 exec
->setException(toJS(exec
, exception
));
465 return toJS(exec
, value
).getNumber(dValue
) ? dValue
: NaN
;
468 return Base::toNumber(exec
);
471 template <class Base
>
472 UString JSCallbackObject
<Base
>::toString(ExecState
* exec
) const
474 JSContextRef ctx
= toRef(exec
);
475 JSObjectRef thisRef
= toRef(this);
477 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
478 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
479 JSValueRef exception
= 0;
482 APICallbackShim
callbackShim(exec
);
483 value
= convertToType(ctx
, thisRef
, kJSTypeString
, &exception
);
486 exec
->setException(toJS(exec
, exception
));
490 return toJS(exec
, value
).getString(exec
);
493 return Base::toString(exec
);
496 template <class Base
>
497 void JSCallbackObject
<Base
>::setPrivate(void* data
)
499 m_callbackObjectData
->privateData
= data
;
502 template <class Base
>
503 void* JSCallbackObject
<Base
>::getPrivate()
505 return m_callbackObjectData
->privateData
;
508 template <class Base
>
509 bool JSCallbackObject
<Base
>::inherits(JSClassRef c
) const
511 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
518 template <class Base
>
519 JSValue JSCallbackObject
<Base
>::staticValueGetter(ExecState
* exec
, JSValue slotBase
, const Identifier
& propertyName
)
521 JSCallbackObject
* thisObj
= asCallbackObject(slotBase
);
523 JSObjectRef thisRef
= toRef(thisObj
);
524 RefPtr
<OpaqueJSString
> propertyNameRef
;
526 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
527 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
))
528 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep()))
529 if (JSObjectGetPropertyCallback getProperty
= entry
->getProperty
) {
530 if (!propertyNameRef
)
531 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
532 JSValueRef exception
= 0;
535 APICallbackShim
callbackShim(exec
);
536 value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
.get(), &exception
);
539 exec
->setException(toJS(exec
, exception
));
540 return jsUndefined();
543 return toJS(exec
, value
);
546 return throwError(exec
, ReferenceError
, "Static value property defined with NULL getProperty callback.");
549 template <class Base
>
550 JSValue JSCallbackObject
<Base
>::staticFunctionGetter(ExecState
* exec
, JSValue slotBase
, const Identifier
& propertyName
)
552 JSCallbackObject
* thisObj
= asCallbackObject(slotBase
);
554 // Check for cached or override property.
555 PropertySlot
slot2(thisObj
);
556 if (thisObj
->Base::getOwnPropertySlot(exec
, propertyName
, slot2
))
557 return slot2
.getValue(exec
, propertyName
);
559 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
560 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
561 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
562 if (JSObjectCallAsFunctionCallback callAsFunction
= entry
->callAsFunction
) {
563 JSObject
* o
= new (exec
) JSCallbackFunction(exec
, callAsFunction
, propertyName
);
564 thisObj
->putDirect(propertyName
, o
, entry
->attributes
);
571 return throwError(exec
, ReferenceError
, "Static function property defined with NULL callAsFunction callback.");
574 template <class Base
>
575 JSValue JSCallbackObject
<Base
>::callbackGetter(ExecState
* exec
, JSValue slotBase
, const Identifier
& propertyName
)
577 JSCallbackObject
* thisObj
= asCallbackObject(slotBase
);
579 JSObjectRef thisRef
= toRef(thisObj
);
580 RefPtr
<OpaqueJSString
> propertyNameRef
;
582 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
583 if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
584 if (!propertyNameRef
)
585 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
586 JSValueRef exception
= 0;
589 APICallbackShim
callbackShim(exec
);
590 value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
.get(), &exception
);
593 exec
->setException(toJS(exec
, exception
));
594 return jsUndefined();
597 return toJS(exec
, value
);
600 return throwError(exec
, ReferenceError
, "hasProperty callback returned true for a property that doesn't exist.");