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(JSValue 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
.isEmpty())
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 JSValueRef exception
= 0;
131 JSLock::DropAllLocks
dropAllLocks(exec
);
132 value
= getProperty(ctx
, thisRef
, propertyNameRef
.get(), &exception
);
134 exec
->setException(toJS(exec
, exception
));
136 slot
.setValue(toJS(exec
, value
));
140 slot
.setValue(jsUndefined());
145 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
146 if (staticValues
->contains(propertyName
.ustring().rep())) {
147 slot
.setCustom(this, staticValueGetter
);
152 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
153 if (staticFunctions
->contains(propertyName
.ustring().rep())) {
154 slot
.setCustom(this, staticFunctionGetter
);
160 return Base::getOwnPropertySlot(exec
, propertyName
, slot
);
163 template <class Base
>
164 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
166 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
169 template <class Base
>
170 void JSCallbackObject
<Base
>::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
172 JSContextRef ctx
= toRef(exec
);
173 JSObjectRef thisRef
= toRef(this);
174 RefPtr
<OpaqueJSString
> propertyNameRef
;
175 JSValueRef valueRef
= toRef(exec
, value
);
177 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
178 if (JSObjectSetPropertyCallback setProperty
= jsClass
->setProperty
) {
179 if (!propertyNameRef
)
180 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
181 JSValueRef exception
= 0;
184 JSLock::DropAllLocks
dropAllLocks(exec
);
185 result
= setProperty(ctx
, thisRef
, propertyNameRef
.get(), valueRef
, &exception
);
187 exec
->setException(toJS(exec
, exception
));
188 if (result
|| exception
)
192 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
193 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
194 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
196 if (JSObjectSetPropertyCallback setProperty
= entry
->setProperty
) {
197 if (!propertyNameRef
)
198 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
199 JSValueRef exception
= 0;
202 JSLock::DropAllLocks
dropAllLocks(exec
);
203 result
= setProperty(ctx
, thisRef
, propertyNameRef
.get(), valueRef
, &exception
);
205 exec
->setException(toJS(exec
, exception
));
206 if (result
|| exception
)
209 throwError(exec
, ReferenceError
, "Attempt to set a property that is not settable.");
213 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
214 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
215 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
217 JSCallbackObject
<Base
>::putDirect(propertyName
, value
); // put as override property
223 return Base::put(exec
, propertyName
, value
, slot
);
226 template <class Base
>
227 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
229 JSContextRef ctx
= toRef(exec
);
230 JSObjectRef thisRef
= toRef(this);
231 RefPtr
<OpaqueJSString
> propertyNameRef
;
233 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
234 if (JSObjectDeletePropertyCallback deleteProperty
= jsClass
->deleteProperty
) {
235 if (!propertyNameRef
)
236 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
237 JSValueRef exception
= 0;
240 JSLock::DropAllLocks
dropAllLocks(exec
);
241 result
= deleteProperty(ctx
, thisRef
, propertyNameRef
.get(), &exception
);
243 exec
->setException(toJS(exec
, exception
));
244 if (result
|| exception
)
248 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
249 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
250 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
256 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
257 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
258 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
265 return Base::deleteProperty(exec
, propertyName
);
268 template <class Base
>
269 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, unsigned propertyName
)
271 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
274 template <class Base
>
275 ConstructType JSCallbackObject
<Base
>::getConstructData(ConstructData
& constructData
)
277 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
278 if (jsClass
->callAsConstructor
) {
279 constructData
.native
.function
= construct
;
280 return ConstructTypeHost
;
283 return ConstructTypeNone
;
286 template <class Base
>
287 JSObject
* JSCallbackObject
<Base
>::construct(ExecState
* exec
, JSObject
* constructor
, const ArgList
& args
)
289 JSContextRef execRef
= toRef(exec
);
290 JSObjectRef constructorRef
= toRef(constructor
);
292 for (JSClassRef jsClass
= static_cast<JSCallbackObject
<Base
>*>(constructor
)->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
293 if (JSObjectCallAsConstructorCallback callAsConstructor
= jsClass
->callAsConstructor
) {
294 int argumentCount
= static_cast<int>(args
.size());
295 Vector
<JSValueRef
, 16> arguments(argumentCount
);
296 for (int i
= 0; i
< argumentCount
; i
++)
297 arguments
[i
] = toRef(exec
, args
.at(i
));
298 JSValueRef exception
= 0;
301 JSLock::DropAllLocks
dropAllLocks(exec
);
302 result
= toJS(callAsConstructor(execRef
, constructorRef
, argumentCount
, arguments
.data(), &exception
));
304 exec
->setException(toJS(exec
, exception
));
309 ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
313 template <class Base
>
314 bool JSCallbackObject
<Base
>::hasInstance(ExecState
* exec
, JSValue value
, JSValue
)
316 JSContextRef execRef
= toRef(exec
);
317 JSObjectRef thisRef
= toRef(this);
319 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
320 if (JSObjectHasInstanceCallback hasInstance
= jsClass
->hasInstance
) {
321 JSValueRef valueRef
= toRef(exec
, value
);
322 JSValueRef exception
= 0;
325 JSLock::DropAllLocks
dropAllLocks(exec
);
326 result
= hasInstance(execRef
, thisRef
, valueRef
, &exception
);
328 exec
->setException(toJS(exec
, exception
));
335 template <class Base
>
336 CallType JSCallbackObject
<Base
>::getCallData(CallData
& callData
)
338 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
339 if (jsClass
->callAsFunction
) {
340 callData
.native
.function
= call
;
347 template <class Base
>
348 JSValue JSCallbackObject
<Base
>::call(ExecState
* exec
, JSObject
* functionObject
, JSValue thisValue
, const ArgList
& args
)
350 JSContextRef execRef
= toRef(exec
);
351 JSObjectRef functionRef
= toRef(functionObject
);
352 JSObjectRef thisObjRef
= toRef(thisValue
.toThisObject(exec
));
354 for (JSClassRef jsClass
= static_cast<JSCallbackObject
<Base
>*>(functionObject
)->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
355 if (JSObjectCallAsFunctionCallback callAsFunction
= jsClass
->callAsFunction
) {
356 int argumentCount
= static_cast<int>(args
.size());
357 Vector
<JSValueRef
, 16> arguments(argumentCount
);
358 for (int i
= 0; i
< argumentCount
; i
++)
359 arguments
[i
] = toRef(exec
, args
.at(i
));
360 JSValueRef exception
= 0;
363 JSLock::DropAllLocks
dropAllLocks(exec
);
364 result
= toJS(exec
, callAsFunction(execRef
, functionRef
, thisObjRef
, argumentCount
, arguments
.data(), &exception
));
366 exec
->setException(toJS(exec
, exception
));
371 ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
375 template <class Base
>
376 void JSCallbackObject
<Base
>::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
378 JSContextRef execRef
= toRef(exec
);
379 JSObjectRef thisRef
= toRef(this);
381 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
382 if (JSObjectGetPropertyNamesCallback getPropertyNames
= jsClass
->getPropertyNames
) {
383 JSLock::DropAllLocks
dropAllLocks(exec
);
384 getPropertyNames(execRef
, thisRef
, toRef(&propertyNames
));
387 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
)) {
388 typedef OpaqueJSClassStaticValuesTable::const_iterator iterator
;
389 iterator end
= staticValues
->end();
390 for (iterator it
= staticValues
->begin(); it
!= end
; ++it
) {
391 UString::Rep
* name
= it
->first
.get();
392 StaticValueEntry
* entry
= it
->second
;
393 if (entry
->getProperty
&& !(entry
->attributes
& kJSPropertyAttributeDontEnum
))
394 propertyNames
.add(Identifier(exec
, name
));
398 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
399 typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator
;
400 iterator end
= staticFunctions
->end();
401 for (iterator it
= staticFunctions
->begin(); it
!= end
; ++it
) {
402 UString::Rep
* name
= it
->first
.get();
403 StaticFunctionEntry
* entry
= it
->second
;
404 if (!(entry
->attributes
& kJSPropertyAttributeDontEnum
))
405 propertyNames
.add(Identifier(exec
, name
));
410 Base::getPropertyNames(exec
, propertyNames
);
413 template <class Base
>
414 double JSCallbackObject
<Base
>::toNumber(ExecState
* exec
) const
416 // We need this check to guard against the case where this object is rhs of
417 // a binary expression where lhs threw an exception in its conversion to
419 if (exec
->hadException())
421 JSContextRef ctx
= toRef(exec
);
422 JSObjectRef thisRef
= toRef(this);
424 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
425 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
426 JSValueRef exception
= 0;
429 JSLock::DropAllLocks
dropAllLocks(exec
);
430 value
= convertToType(ctx
, thisRef
, kJSTypeNumber
, &exception
);
433 exec
->setException(toJS(exec
, exception
));
438 return toJS(exec
, value
).getNumber(dValue
) ? dValue
: NaN
;
441 return Base::toNumber(exec
);
444 template <class Base
>
445 UString JSCallbackObject
<Base
>::toString(ExecState
* exec
) const
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 JSLock::DropAllLocks
dropAllLocks(exec
);
456 value
= convertToType(ctx
, thisRef
, kJSTypeString
, &exception
);
459 exec
->setException(toJS(exec
, exception
));
462 return toJS(exec
, value
).getString();
465 return Base::toString(exec
);
468 template <class Base
>
469 void JSCallbackObject
<Base
>::setPrivate(void* data
)
471 m_callbackObjectData
->privateData
= data
;
474 template <class Base
>
475 void* JSCallbackObject
<Base
>::getPrivate()
477 return m_callbackObjectData
->privateData
;
480 template <class Base
>
481 bool JSCallbackObject
<Base
>::inherits(JSClassRef c
) const
483 for (JSClassRef jsClass
= classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
490 template <class Base
>
491 JSValue JSCallbackObject
<Base
>::staticValueGetter(ExecState
* exec
, const Identifier
& propertyName
, const PropertySlot
& slot
)
493 JSCallbackObject
* thisObj
= asCallbackObject(slot
.slotBase());
495 JSObjectRef thisRef
= toRef(thisObj
);
496 RefPtr
<OpaqueJSString
> propertyNameRef
;
498 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
499 if (OpaqueJSClassStaticValuesTable
* staticValues
= jsClass
->staticValues(exec
))
500 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep()))
501 if (JSObjectGetPropertyCallback getProperty
= entry
->getProperty
) {
502 if (!propertyNameRef
)
503 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
504 JSValueRef exception
= 0;
507 JSLock::DropAllLocks
dropAllLocks(exec
);
508 value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
.get(), &exception
);
510 exec
->setException(toJS(exec
, exception
));
512 return toJS(exec
, value
);
514 return jsUndefined();
517 return throwError(exec
, ReferenceError
, "Static value property defined with NULL getProperty callback.");
520 template <class Base
>
521 JSValue JSCallbackObject
<Base
>::staticFunctionGetter(ExecState
* exec
, const Identifier
& propertyName
, const PropertySlot
& slot
)
523 JSCallbackObject
* thisObj
= asCallbackObject(slot
.slotBase());
525 // Check for cached or override property.
526 PropertySlot
slot2(thisObj
);
527 if (thisObj
->Base::getOwnPropertySlot(exec
, propertyName
, slot2
))
528 return slot2
.getValue(exec
, propertyName
);
530 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
) {
531 if (OpaqueJSClassStaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions(exec
)) {
532 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
533 if (JSObjectCallAsFunctionCallback callAsFunction
= entry
->callAsFunction
) {
534 JSObject
* o
= new (exec
) JSCallbackFunction(exec
, callAsFunction
, propertyName
);
535 thisObj
->putDirect(propertyName
, o
, entry
->attributes
);
542 return throwError(exec
, ReferenceError
, "Static function property defined with NULL callAsFunction callback.");
545 template <class Base
>
546 JSValue JSCallbackObject
<Base
>::callbackGetter(ExecState
* exec
, const Identifier
& propertyName
, const PropertySlot
& slot
)
548 JSCallbackObject
* thisObj
= asCallbackObject(slot
.slotBase());
550 JSObjectRef thisRef
= toRef(thisObj
);
551 RefPtr
<OpaqueJSString
> propertyNameRef
;
553 for (JSClassRef jsClass
= thisObj
->classRef(); jsClass
; jsClass
= jsClass
->parentClass
)
554 if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
555 if (!propertyNameRef
)
556 propertyNameRef
= OpaqueJSString::create(propertyName
.ustring());
557 JSValueRef exception
= 0;
560 JSLock::DropAllLocks
dropAllLocks(exec
);
561 value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
.get(), &exception
);
563 exec
->setException(toJS(exec
, exception
));
565 return toJS(exec
, value
);
567 return jsUndefined();
570 return throwError(exec
, ReferenceError
, "hasProperty callback returned true for a property that doesn't exist.");