1 // -*- mode: c++; c-basic-offset: 4 -*-
3 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <wtf/Platform.h>
30 #include "JSCallbackFunction.h"
31 #include "JSClassRef.h"
32 #include "JSObjectRef.h"
33 #include "JSGlobalObject.h"
34 #include "JSStringRef.h"
35 #include "PropertyNameArray.h"
37 #include <wtf/Vector.h>
42 JSCallbackObject
<Base
>::JSCallbackObject(ExecState
* exec
, JSClassRef jsClass
, JSValue
* prototype
, void* data
)
45 , m_class(JSClassRetain(jsClass
))
50 // Global object constructor. FIXME: Move this into a JSGlobalCallbackObject subclass.
52 JSCallbackObject
<Base
>::JSCallbackObject(JSClassRef jsClass
)
54 , m_class(JSClassRetain(jsClass
))
56 ASSERT(Base::isGlobalObject());
57 init(static_cast<JSGlobalObject
*>(this)->globalExec());
61 void JSCallbackObject
<Base
>::init(ExecState
* exec
)
65 Vector
<JSObjectInitializeCallback
, 16> initRoutines
;
66 JSClassRef jsClass
= m_class
;
68 if (JSObjectInitializeCallback initialize
= jsClass
->initialize
)
69 initRoutines
.append(initialize
);
70 } while ((jsClass
= jsClass
->parentClass
));
72 // initialize from base to derived
73 for (int i
= static_cast<int>(initRoutines
.size()) - 1; i
>= 0; i
--) {
74 JSLock::DropAllLocks dropAllLocks
;
75 JSObjectInitializeCallback initialize
= initRoutines
[i
];
76 initialize(toRef(exec
), toRef(this));
81 JSCallbackObject
<Base
>::~JSCallbackObject()
83 JSObjectRef thisRef
= toRef(this);
85 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
86 if (JSObjectFinalizeCallback finalize
= jsClass
->finalize
) {
90 JSClassRelease(m_class
);
94 UString JSCallbackObject
<Base
>::className() const
96 if (!m_class
->className
.isNull())
97 return m_class
->className
;
99 return Base::className();
102 template <class Base
>
103 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
105 JSContextRef ctx
= toRef(exec
);
106 JSObjectRef thisRef
= toRef(this);
107 JSStringRef propertyNameRef
= toRef(propertyName
.ustring().rep());
109 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
110 // optional optimization to bypass getProperty in cases when we only need to know if the property exists
111 if (JSObjectHasPropertyCallback hasProperty
= jsClass
->hasProperty
) {
112 JSLock::DropAllLocks dropAllLocks
;
113 if (hasProperty(ctx
, thisRef
, propertyNameRef
)) {
114 slot
.setCustom(this, callbackGetter
);
117 } else if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
118 JSLock::DropAllLocks dropAllLocks
;
119 if (JSValueRef value
= getProperty(ctx
, thisRef
, propertyNameRef
, toRef(exec
->exceptionSlot()))) {
120 // cache the value so we don't have to compute it again
121 // FIXME: This violates the PropertySlot design a little bit.
122 // We should either use this optimization everywhere, or nowhere.
123 slot
.setCustom(reinterpret_cast<JSObject
*>(toJS(value
)), cachedValueGetter
);
128 if (OpaqueJSClass::StaticValuesTable
* staticValues
= jsClass
->staticValues
) {
129 if (staticValues
->contains(propertyName
.ustring().rep())) {
130 slot
.setCustom(this, staticValueGetter
);
135 if (OpaqueJSClass::StaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions
) {
136 if (staticFunctions
->contains(propertyName
.ustring().rep())) {
137 slot
.setCustom(this, staticFunctionGetter
);
143 return Base::getOwnPropertySlot(exec
, propertyName
, slot
);
146 template <class Base
>
147 bool JSCallbackObject
<Base
>::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
149 return getOwnPropertySlot(exec
, Identifier::from(propertyName
), slot
);
152 template <class Base
>
153 void JSCallbackObject
<Base
>::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue
* value
, int attr
)
155 JSContextRef ctx
= toRef(exec
);
156 JSObjectRef thisRef
= toRef(this);
157 JSStringRef propertyNameRef
= toRef(propertyName
.ustring().rep());
158 JSValueRef valueRef
= toRef(value
);
160 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
161 if (JSObjectSetPropertyCallback setProperty
= jsClass
->setProperty
) {
162 JSLock::DropAllLocks dropAllLocks
;
163 if (setProperty(ctx
, thisRef
, propertyNameRef
, valueRef
, toRef(exec
->exceptionSlot())))
167 if (OpaqueJSClass::StaticValuesTable
* staticValues
= jsClass
->staticValues
) {
168 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
169 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
171 if (JSObjectSetPropertyCallback setProperty
= entry
->setProperty
) {
172 JSLock::DropAllLocks dropAllLocks
;
173 if (setProperty(ctx
, thisRef
, propertyNameRef
, valueRef
, toRef(exec
->exceptionSlot())))
176 throwError(exec
, ReferenceError
, "Attempt to set a property that is not settable.");
180 if (OpaqueJSClass::StaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions
) {
181 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
182 if (entry
->attributes
& kJSPropertyAttributeReadOnly
)
184 JSCallbackObject
<Base
>::putDirect(propertyName
, value
, attr
); // put as override property
190 return Base::put(exec
, propertyName
, value
, attr
);
193 template <class Base
>
194 void JSCallbackObject
<Base
>::put(ExecState
* exec
, unsigned propertyName
, JSValue
* value
, int attr
)
196 return put(exec
, Identifier::from(propertyName
), value
, attr
);
199 template <class Base
>
200 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
202 JSContextRef ctx
= toRef(exec
);
203 JSObjectRef thisRef
= toRef(this);
204 JSStringRef propertyNameRef
= toRef(propertyName
.ustring().rep());
206 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
207 if (JSObjectDeletePropertyCallback deleteProperty
= jsClass
->deleteProperty
) {
208 JSLock::DropAllLocks dropAllLocks
;
209 if (deleteProperty(ctx
, thisRef
, propertyNameRef
, toRef(exec
->exceptionSlot())))
213 if (OpaqueJSClass::StaticValuesTable
* staticValues
= jsClass
->staticValues
) {
214 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep())) {
215 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
221 if (OpaqueJSClass::StaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions
) {
222 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
223 if (entry
->attributes
& kJSPropertyAttributeDontDelete
)
230 return Base::deleteProperty(exec
, propertyName
);
233 template <class Base
>
234 bool JSCallbackObject
<Base
>::deleteProperty(ExecState
* exec
, unsigned propertyName
)
236 return deleteProperty(exec
, Identifier::from(propertyName
));
239 template <class Base
>
240 bool JSCallbackObject
<Base
>::implementsConstruct() const
242 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
243 if (jsClass
->callAsConstructor
)
249 template <class Base
>
250 JSObject
* JSCallbackObject
<Base
>::construct(ExecState
* exec
, const List
& args
)
252 JSContextRef execRef
= toRef(exec
);
253 JSObjectRef thisRef
= toRef(this);
255 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
256 if (JSObjectCallAsConstructorCallback callAsConstructor
= jsClass
->callAsConstructor
) {
257 int argumentCount
= static_cast<int>(args
.size());
258 Vector
<JSValueRef
, 16> arguments(argumentCount
);
259 for (int i
= 0; i
< argumentCount
; i
++)
260 arguments
[i
] = toRef(args
[i
]);
261 JSLock::DropAllLocks dropAllLocks
;
262 return toJS(callAsConstructor(execRef
, thisRef
, argumentCount
, arguments
.data(), toRef(exec
->exceptionSlot())));
266 ASSERT(0); // implementsConstruct should prevent us from reaching here
270 template <class Base
>
271 bool JSCallbackObject
<Base
>::implementsHasInstance() const
273 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
274 if (jsClass
->hasInstance
)
280 template <class Base
>
281 bool JSCallbackObject
<Base
>::hasInstance(ExecState
*exec
, JSValue
*value
)
283 JSContextRef execRef
= toRef(exec
);
284 JSObjectRef thisRef
= toRef(this);
286 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
287 if (JSObjectHasInstanceCallback hasInstance
= jsClass
->hasInstance
) {
288 JSLock::DropAllLocks dropAllLocks
;
289 return hasInstance(execRef
, thisRef
, toRef(value
), toRef(exec
->exceptionSlot()));
292 ASSERT_NOT_REACHED(); // implementsHasInstance should prevent us from reaching here
297 template <class Base
>
298 bool JSCallbackObject
<Base
>::implementsCall() const
300 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
301 if (jsClass
->callAsFunction
)
307 template <class Base
>
308 JSValue
* JSCallbackObject
<Base
>::callAsFunction(ExecState
* exec
, JSObject
* thisObj
, const List
&args
)
310 JSContextRef execRef
= toRef(exec
);
311 JSObjectRef thisRef
= toRef(this);
312 JSObjectRef thisObjRef
= toRef(thisObj
);
314 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
315 if (JSObjectCallAsFunctionCallback callAsFunction
= jsClass
->callAsFunction
) {
316 int argumentCount
= static_cast<int>(args
.size());
317 Vector
<JSValueRef
, 16> arguments(argumentCount
);
318 for (int i
= 0; i
< argumentCount
; i
++)
319 arguments
[i
] = toRef(args
[i
]);
320 JSLock::DropAllLocks dropAllLocks
;
321 return toJS(callAsFunction(execRef
, thisRef
, thisObjRef
, argumentCount
, arguments
.data(), toRef(exec
->exceptionSlot())));
325 ASSERT_NOT_REACHED(); // implementsCall should prevent us from reaching here
329 template <class Base
>
330 void JSCallbackObject
<Base
>::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
332 JSContextRef execRef
= toRef(exec
);
333 JSObjectRef thisRef
= toRef(this);
335 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
336 if (JSObjectGetPropertyNamesCallback getPropertyNames
= jsClass
->getPropertyNames
) {
337 JSLock::DropAllLocks dropAllLocks
;
338 getPropertyNames(execRef
, thisRef
, toRef(&propertyNames
));
341 if (OpaqueJSClass::StaticValuesTable
* staticValues
= jsClass
->staticValues
) {
342 typedef OpaqueJSClass::StaticValuesTable::const_iterator iterator
;
343 iterator end
= staticValues
->end();
344 for (iterator it
= staticValues
->begin(); it
!= end
; ++it
) {
345 UString::Rep
* name
= it
->first
.get();
346 StaticValueEntry
* entry
= it
->second
;
347 if (entry
->getProperty
&& !(entry
->attributes
& kJSPropertyAttributeDontEnum
))
348 propertyNames
.add(Identifier(name
));
352 if (OpaqueJSClass::StaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions
) {
353 typedef OpaqueJSClass::StaticFunctionsTable::const_iterator iterator
;
354 iterator end
= staticFunctions
->end();
355 for (iterator it
= staticFunctions
->begin(); it
!= end
; ++it
) {
356 UString::Rep
* name
= it
->first
.get();
357 StaticFunctionEntry
* entry
= it
->second
;
358 if (!(entry
->attributes
& kJSPropertyAttributeDontEnum
))
359 propertyNames
.add(Identifier(name
));
364 Base::getPropertyNames(exec
, propertyNames
);
367 template <class Base
>
368 double JSCallbackObject
<Base
>::toNumber(ExecState
* exec
) const
370 JSContextRef ctx
= toRef(exec
);
371 JSObjectRef thisRef
= toRef(this);
373 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
374 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
375 JSLock::DropAllLocks dropAllLocks
;
376 if (JSValueRef value
= convertToType(ctx
, thisRef
, kJSTypeNumber
, toRef(exec
->exceptionSlot())))
377 return toJS(value
)->getNumber();
380 return Base::toNumber(exec
);
383 template <class Base
>
384 UString JSCallbackObject
<Base
>::toString(ExecState
* exec
) const
386 JSContextRef ctx
= toRef(exec
);
387 JSObjectRef thisRef
= toRef(this);
389 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
390 if (JSObjectConvertToTypeCallback convertToType
= jsClass
->convertToType
) {
391 JSLock::DropAllLocks dropAllLocks
;
392 if (JSValueRef value
= convertToType(ctx
, thisRef
, kJSTypeString
, toRef(exec
->exceptionSlot())))
393 return toJS(value
)->getString();
396 return Base::toString(exec
);
399 template <class Base
>
400 void JSCallbackObject
<Base
>::setPrivate(void* data
)
402 m_privateData
= data
;
405 template <class Base
>
406 void* JSCallbackObject
<Base
>::getPrivate()
408 return m_privateData
;
411 template <class Base
>
412 bool JSCallbackObject
<Base
>::inherits(JSClassRef c
) const
414 for (JSClassRef jsClass
= m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
421 template <class Base
>
422 JSValue
* JSCallbackObject
<Base
>::cachedValueGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
& slot
)
424 JSValue
* v
= slot
.slotBase();
429 template <class Base
>
430 JSValue
* JSCallbackObject
<Base
>::staticValueGetter(ExecState
* exec
, JSObject
*, const Identifier
& propertyName
, const PropertySlot
& slot
)
432 ASSERT(slot
.slotBase()->inherits(&JSCallbackObject::info
));
433 JSCallbackObject
* thisObj
= static_cast<JSCallbackObject
*>(slot
.slotBase());
435 JSObjectRef thisRef
= toRef(thisObj
);
436 JSStringRef propertyNameRef
= toRef(propertyName
.ustring().rep());
438 for (JSClassRef jsClass
= thisObj
->m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
439 if (OpaqueJSClass::StaticValuesTable
* staticValues
= jsClass
->staticValues
)
440 if (StaticValueEntry
* entry
= staticValues
->get(propertyName
.ustring().rep()))
441 if (JSObjectGetPropertyCallback getProperty
= entry
->getProperty
) {
442 JSLock::DropAllLocks dropAllLocks
;
443 if (JSValueRef value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
, toRef(exec
->exceptionSlot())))
447 return throwError(exec
, ReferenceError
, "Static value property defined with NULL getProperty callback.");
450 template <class Base
>
451 JSValue
* JSCallbackObject
<Base
>::staticFunctionGetter(ExecState
* exec
, JSObject
*, const Identifier
& propertyName
, const PropertySlot
& slot
)
453 ASSERT(slot
.slotBase()->inherits(&JSCallbackObject::info
));
454 JSCallbackObject
* thisObj
= static_cast<JSCallbackObject
*>(slot
.slotBase());
456 // Check for cached or override property.
458 if (thisObj
->Base::getOwnPropertySlot(exec
, propertyName
, slot2
))
459 return slot2
.getValue(exec
, thisObj
, propertyName
);
461 for (JSClassRef jsClass
= thisObj
->m_class
; jsClass
; jsClass
= jsClass
->parentClass
) {
462 if (OpaqueJSClass::StaticFunctionsTable
* staticFunctions
= jsClass
->staticFunctions
) {
463 if (StaticFunctionEntry
* entry
= staticFunctions
->get(propertyName
.ustring().rep())) {
464 if (JSObjectCallAsFunctionCallback callAsFunction
= entry
->callAsFunction
) {
465 JSObject
* o
= new JSCallbackFunction(exec
, callAsFunction
, propertyName
);
466 thisObj
->putDirect(propertyName
, o
, entry
->attributes
);
473 return throwError(exec
, ReferenceError
, "Static function property defined with NULL callAsFunction callback.");
476 template <class Base
>
477 JSValue
* JSCallbackObject
<Base
>::callbackGetter(ExecState
* exec
, JSObject
*, const Identifier
& propertyName
, const PropertySlot
& slot
)
479 ASSERT(slot
.slotBase()->inherits(&JSCallbackObject::info
));
480 JSCallbackObject
* thisObj
= static_cast<JSCallbackObject
*>(slot
.slotBase());
482 JSObjectRef thisRef
= toRef(thisObj
);
483 JSStringRef propertyNameRef
= toRef(propertyName
.ustring().rep());
485 for (JSClassRef jsClass
= thisObj
->m_class
; jsClass
; jsClass
= jsClass
->parentClass
)
486 if (JSObjectGetPropertyCallback getProperty
= jsClass
->getProperty
) {
487 JSLock::DropAllLocks dropAllLocks
;
488 if (JSValueRef value
= getProperty(toRef(exec
), thisRef
, propertyNameRef
, toRef(exec
->exceptionSlot())))
492 return throwError(exec
, ReferenceError
, "hasProperty callback returned true for a property that doesn't exist.");