]> git.saurik.com Git - apple/javascriptcore.git/blame - API/JSCallbackObjectFunctions.h
JavaScriptCore-466.1.6.tar.gz
[apple/javascriptcore.git] / API / JSCallbackObjectFunctions.h
CommitLineData
b37bf2e1
A
1// -*- mode: c++; c-basic-offset: 4 -*-
2/*
3 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
14 *
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.
26 */
27
28#include <wtf/Platform.h>
29#include "APICast.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"
36#include "internal.h"
37#include <wtf/Vector.h>
38
39namespace KJS {
40
41template <class Base>
42JSCallbackObject<Base>::JSCallbackObject(ExecState* exec, JSClassRef jsClass, JSValue* prototype, void* data)
43 : Base(prototype)
44 , m_privateData(data)
45 , m_class(JSClassRetain(jsClass))
46{
47 init(exec);
48}
49
50// Global object constructor. FIXME: Move this into a JSGlobalCallbackObject subclass.
51template <class Base>
52JSCallbackObject<Base>::JSCallbackObject(JSClassRef jsClass)
53 : m_privateData(0)
54 , m_class(JSClassRetain(jsClass))
55{
56 ASSERT(Base::isGlobalObject());
57 init(static_cast<JSGlobalObject*>(this)->globalExec());
58}
59
60template <class Base>
61void JSCallbackObject<Base>::init(ExecState* exec)
62{
63 ASSERT(exec);
64
65 Vector<JSObjectInitializeCallback, 16> initRoutines;
66 JSClassRef jsClass = m_class;
67 do {
68 if (JSObjectInitializeCallback initialize = jsClass->initialize)
69 initRoutines.append(initialize);
70 } while ((jsClass = jsClass->parentClass));
71
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));
77 }
78}
79
80template <class Base>
81JSCallbackObject<Base>::~JSCallbackObject()
82{
83 JSObjectRef thisRef = toRef(this);
84
85 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
86 if (JSObjectFinalizeCallback finalize = jsClass->finalize) {
87 finalize(thisRef);
88 }
89
90 JSClassRelease(m_class);
91}
92
93template <class Base>
94UString JSCallbackObject<Base>::className() const
95{
96 if (!m_class->className.isNull())
97 return m_class->className;
98
99 return Base::className();
100}
101
102template <class Base>
103bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
104{
105 JSContextRef ctx = toRef(exec);
106 JSObjectRef thisRef = toRef(this);
107 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
108
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);
115 return true;
116 }
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);
124 return true;
125 }
126 }
127
128 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
129 if (staticValues->contains(propertyName.ustring().rep())) {
130 slot.setCustom(this, staticValueGetter);
131 return true;
132 }
133 }
134
135 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
136 if (staticFunctions->contains(propertyName.ustring().rep())) {
137 slot.setCustom(this, staticFunctionGetter);
138 return true;
139 }
140 }
141 }
142
143 return Base::getOwnPropertySlot(exec, propertyName, slot);
144}
145
146template <class Base>
147bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
148{
149 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
150}
151
152template <class Base>
153void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
154{
155 JSContextRef ctx = toRef(exec);
156 JSObjectRef thisRef = toRef(this);
157 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
158 JSValueRef valueRef = toRef(value);
159
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())))
164 return;
165 }
166
167 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
168 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
169 if (entry->attributes & kJSPropertyAttributeReadOnly)
170 return;
171 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
172 JSLock::DropAllLocks dropAllLocks;
173 if (setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
174 return;
175 } else
176 throwError(exec, ReferenceError, "Attempt to set a property that is not settable.");
177 }
178 }
179
180 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
181 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
182 if (entry->attributes & kJSPropertyAttributeReadOnly)
183 return;
184 JSCallbackObject<Base>::putDirect(propertyName, value, attr); // put as override property
185 return;
186 }
187 }
188 }
189
190 return Base::put(exec, propertyName, value, attr);
191}
192
193template <class Base>
194void JSCallbackObject<Base>::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr)
195{
196 return put(exec, Identifier::from(propertyName), value, attr);
197}
198
199template <class Base>
200bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, const Identifier& propertyName)
201{
202 JSContextRef ctx = toRef(exec);
203 JSObjectRef thisRef = toRef(this);
204 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
205
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())))
210 return true;
211 }
212
213 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
214 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
215 if (entry->attributes & kJSPropertyAttributeDontDelete)
216 return false;
217 return true;
218 }
219 }
220
221 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
222 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
223 if (entry->attributes & kJSPropertyAttributeDontDelete)
224 return false;
225 return true;
226 }
227 }
228 }
229
230 return Base::deleteProperty(exec, propertyName);
231}
232
233template <class Base>
234bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, unsigned propertyName)
235{
236 return deleteProperty(exec, Identifier::from(propertyName));
237}
238
239template <class Base>
240bool JSCallbackObject<Base>::implementsConstruct() const
241{
242 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
243 if (jsClass->callAsConstructor)
244 return true;
245
246 return false;
247}
248
249template <class Base>
250JSObject* JSCallbackObject<Base>::construct(ExecState* exec, const List& args)
251{
252 JSContextRef execRef = toRef(exec);
253 JSObjectRef thisRef = toRef(this);
254
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())));
263 }
264 }
265
266 ASSERT(0); // implementsConstruct should prevent us from reaching here
267 return 0;
268}
269
270template <class Base>
271bool JSCallbackObject<Base>::implementsHasInstance() const
272{
273 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
274 if (jsClass->hasInstance)
275 return true;
276
277 return false;
278}
279
280template <class Base>
281bool JSCallbackObject<Base>::hasInstance(ExecState *exec, JSValue *value)
282{
283 JSContextRef execRef = toRef(exec);
284 JSObjectRef thisRef = toRef(this);
285
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()));
290 }
291
292 ASSERT_NOT_REACHED(); // implementsHasInstance should prevent us from reaching here
293 return 0;
294}
295
296
297template <class Base>
298bool JSCallbackObject<Base>::implementsCall() const
299{
300 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
301 if (jsClass->callAsFunction)
302 return true;
303
304 return false;
305}
306
307template <class Base>
308JSValue* JSCallbackObject<Base>::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
309{
310 JSContextRef execRef = toRef(exec);
311 JSObjectRef thisRef = toRef(this);
312 JSObjectRef thisObjRef = toRef(thisObj);
313
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())));
322 }
323 }
324
325 ASSERT_NOT_REACHED(); // implementsCall should prevent us from reaching here
326 return 0;
327}
328
329template <class Base>
330void JSCallbackObject<Base>::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
331{
332 JSContextRef execRef = toRef(exec);
333 JSObjectRef thisRef = toRef(this);
334
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));
339 }
340
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));
349 }
350 }
351
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));
360 }
361 }
362 }
363
364 Base::getPropertyNames(exec, propertyNames);
365}
366
367template <class Base>
368double JSCallbackObject<Base>::toNumber(ExecState* exec) const
369{
370 JSContextRef ctx = toRef(exec);
371 JSObjectRef thisRef = toRef(this);
372
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();
378 }
379
380 return Base::toNumber(exec);
381}
382
383template <class Base>
384UString JSCallbackObject<Base>::toString(ExecState* exec) const
385{
386 JSContextRef ctx = toRef(exec);
387 JSObjectRef thisRef = toRef(this);
388
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();
394 }
395
396 return Base::toString(exec);
397}
398
399template <class Base>
400void JSCallbackObject<Base>::setPrivate(void* data)
401{
402 m_privateData = data;
403}
404
405template <class Base>
406void* JSCallbackObject<Base>::getPrivate()
407{
408 return m_privateData;
409}
410
411template <class Base>
412bool JSCallbackObject<Base>::inherits(JSClassRef c) const
413{
414 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
415 if (jsClass == c)
416 return true;
417
418 return false;
419}
420
421template <class Base>
422JSValue* JSCallbackObject<Base>::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
423{
424 JSValue* v = slot.slotBase();
425 ASSERT(v);
426 return v;
427}
428
429template <class Base>
430JSValue* JSCallbackObject<Base>::staticValueGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
431{
432 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
433 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
434
435 JSObjectRef thisRef = toRef(thisObj);
436 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
437
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())))
444 return toJS(value);
445 }
446
447 return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback.");
448}
449
450template <class Base>
451JSValue* JSCallbackObject<Base>::staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
452{
453 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
454 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
455
456 // Check for cached or override property.
457 PropertySlot slot2;
458 if (thisObj->Base::getOwnPropertySlot(exec, propertyName, slot2))
459 return slot2.getValue(exec, thisObj, propertyName);
460
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);
467 return o;
468 }
469 }
470 }
471 }
472
473 return throwError(exec, ReferenceError, "Static function property defined with NULL callAsFunction callback.");
474}
475
476template <class Base>
477JSValue* JSCallbackObject<Base>::callbackGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
478{
479 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
480 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
481
482 JSObjectRef thisRef = toRef(thisObj);
483 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
484
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())))
489 return toJS(value);
490 }
491
492 return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist.");
493}
494
495} // namespace KJS