]> git.saurik.com Git - apple/javascriptcore.git/blob - API/JSCallbackObjectFunctions.h
JavaScriptCore-466.1.tar.gz
[apple/javascriptcore.git] / API / JSCallbackObjectFunctions.h
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
39 namespace KJS {
40
41 template <class Base>
42 JSCallbackObject<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.
51 template <class Base>
52 JSCallbackObject<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
60 template <class Base>
61 void 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
80 template <class Base>
81 JSCallbackObject<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
93 template <class Base>
94 UString JSCallbackObject<Base>::className() const
95 {
96 if (!m_class->className.isNull())
97 return m_class->className;
98
99 return Base::className();
100 }
101
102 template <class Base>
103 bool 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
146 template <class Base>
147 bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
148 {
149 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
150 }
151
152 template <class Base>
153 void 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
193 template <class Base>
194 void JSCallbackObject<Base>::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr)
195 {
196 return put(exec, Identifier::from(propertyName), value, attr);
197 }
198
199 template <class Base>
200 bool 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
233 template <class Base>
234 bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, unsigned propertyName)
235 {
236 return deleteProperty(exec, Identifier::from(propertyName));
237 }
238
239 template <class Base>
240 bool 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
249 template <class Base>
250 JSObject* 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
270 template <class Base>
271 bool 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
280 template <class Base>
281 bool 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
297 template <class Base>
298 bool 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
307 template <class Base>
308 JSValue* 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
329 template <class Base>
330 void 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
367 template <class Base>
368 double 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
383 template <class Base>
384 UString 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
399 template <class Base>
400 void JSCallbackObject<Base>::setPrivate(void* data)
401 {
402 m_privateData = data;
403 }
404
405 template <class Base>
406 void* JSCallbackObject<Base>::getPrivate()
407 {
408 return m_privateData;
409 }
410
411 template <class Base>
412 bool 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
421 template <class Base>
422 JSValue* JSCallbackObject<Base>::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
423 {
424 JSValue* v = slot.slotBase();
425 ASSERT(v);
426 return v;
427 }
428
429 template <class Base>
430 JSValue* 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
450 template <class Base>
451 JSValue* 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
476 template <class Base>
477 JSValue* 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