source: webkit/trunk/JavaScriptCore/API/JSCallbackObject.cpp@ 15384

Last change on this file since 15384 was 15384, checked in by mjs, 19 years ago

4eviewed by Geoff.


  • add handling of hasInstance callback for API objects
  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::implementsHasInstance): Check if callback is present. (KJS::JSCallbackObject::hasInstance): Invoke appropriate callback.
  • API/JSCallbackObject.h:
  • API/JSClassRef.cpp:
  • API/JSObjectRef.h:
  • API/testapi.c: (MyObject_hasInstance): Test case; should match what construct would do.
  • API/testapi.js:
File size: 16.9 KB
Line 
1// -*- mode: c++; c-basic-offset: 4 -*-
2/*
3 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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.
25 */
26
27#include "APICast.h"
28#include "JSCallbackObject.h"
29#include "JSInternalStringRef.h"
30#include "JSClassRef.h"
31#include "JSObjectRef.h"
32#include "internal.h"
33#include "reference.h"
34#include "reference_list.h"
35
36namespace KJS {
37
38const ClassInfo JSCallbackObject::info = { "CallbackObject", 0, 0, 0 };
39
40JSCallbackObject::JSCallbackObject(JSContextRef context, JSClassRef jsClass)
41 : JSObject()
42{
43 init(context, jsClass);
44}
45
46JSCallbackObject::JSCallbackObject(JSContextRef context, JSClassRef jsClass, JSValue* prototype)
47 : JSObject(prototype)
48{
49 init(context, jsClass);
50}
51
52void JSCallbackObject::init(JSContextRef context, JSClassRef jsClass)
53{
54 ExecState* exec = toJS(context);
55
56 m_privateData = 0;
57 m_class = JSClassRetain(jsClass);
58
59 JSObjectRef thisRef = toRef(this);
60
61 do {
62 if (JSObjectInitializeCallback initialize = jsClass->callbacks.initialize)
63 initialize(context, thisRef, toRef(exec->exceptionSlot()));
64 } while ((jsClass = jsClass->parent));
65}
66
67JSCallbackObject::~JSCallbackObject()
68{
69 JSObjectRef thisRef = toRef(this);
70
71 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
72 if (JSObjectFinalizeCallback finalize = jsClass->callbacks.finalize)
73 finalize(thisRef);
74
75 JSClassRelease(m_class);
76}
77
78UString JSCallbackObject::className() const
79{
80 return classInfo()->className;
81}
82
83bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
84{
85 JSContextRef context = toRef(exec);
86 JSObjectRef thisRef = toRef(this);
87 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
88
89 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
90 // optional optimization to bypass getProperty in cases when we only need to know if the property exists
91 if (JSObjectHasPropertyCallback hasProperty = jsClass->callbacks.hasProperty) {
92 if (hasProperty(context, thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) {
93 slot.setCustom(this, callbackGetter);
94 return true;
95 }
96 } else if (JSObjectGetPropertyCallback getProperty = jsClass->callbacks.getProperty) {
97 if (JSValueRef value = getProperty(context, thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) {
98 // cache the value so we don't have to compute it again
99 // FIXME: This violates the PropertySlot design a little bit.
100 // We should either use this optimization everywhere, or nowhere.
101 slot.setCustom(reinterpret_cast<JSObject*>(toJS(value)), cachedValueGetter);
102 return true;
103 }
104 }
105
106 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
107 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
108 if (entry->getProperty) {
109 slot.setCustom(this, staticValueGetter);
110 return true;
111 }
112 }
113 }
114
115 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
116 if (staticFunctions->contains(propertyName.ustring().rep())) {
117 slot.setCustom(this, staticFunctionGetter);
118 return true;
119 }
120 }
121 }
122
123 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
124}
125
126bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
127{
128 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
129}
130
131void JSCallbackObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
132{
133 JSContextRef context = toRef(exec);
134 JSObjectRef thisRef = toRef(this);
135 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
136 JSValueRef valueRef = toRef(value);
137
138 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
139 if (JSObjectSetPropertyCallback setProperty = jsClass->callbacks.setProperty) {
140 if (setProperty(context, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
141 return;
142 }
143
144 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
145 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
146 if (entry->attributes & kJSPropertyAttributeReadOnly)
147 return;
148 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
149 if (setProperty(context, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
150 return;
151 }
152 }
153 }
154
155 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
156 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
157 if (entry->attributes & kJSPropertyAttributeReadOnly)
158 return;
159 putDirect(propertyName, value, attr); // put as override property
160 return;
161 }
162 }
163 }
164
165 return JSObject::put(exec, propertyName, value, attr);
166}
167
168void JSCallbackObject::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr)
169{
170 return put(exec, Identifier::from(propertyName), value, attr);
171}
172
173bool JSCallbackObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
174{
175 JSContextRef context = toRef(exec);
176 JSObjectRef thisRef = toRef(this);
177 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
178
179 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
180 if (JSObjectDeletePropertyCallback deleteProperty = jsClass->callbacks.deleteProperty) {
181 if (deleteProperty(context, thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
182 return true;
183 }
184
185 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
186 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
187 if (entry->attributes & kJSPropertyAttributeDontDelete)
188 return false;
189 return true;
190 }
191 }
192
193 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
194 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
195 if (entry->attributes & kJSPropertyAttributeDontDelete)
196 return false;
197 return true;
198 }
199 }
200 }
201
202 return JSObject::deleteProperty(exec, propertyName);
203}
204
205bool JSCallbackObject::deleteProperty(ExecState* exec, unsigned propertyName)
206{
207 return deleteProperty(exec, Identifier::from(propertyName));
208}
209
210bool JSCallbackObject::implementsConstruct() const
211{
212 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
213 if (jsClass->callbacks.callAsConstructor)
214 return true;
215
216 return false;
217}
218
219JSObject* JSCallbackObject::construct(ExecState* exec, const List& args)
220{
221 JSContextRef execRef = toRef(exec);
222 JSObjectRef thisRef = toRef(this);
223
224 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
225 if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callbacks.callAsConstructor) {
226 size_t argc = args.size();
227 JSValueRef argv[argc];
228 for (size_t i = 0; i < argc; i++)
229 argv[i] = toRef(args[i]);
230 return toJS(callAsConstructor(execRef, thisRef, argc, argv, toRef(exec->exceptionSlot())));
231 }
232 }
233
234 ASSERT(0); // implementsConstruct should prevent us from reaching here
235 return 0;
236}
237
238bool JSCallbackObject::implementsHasInstance() const
239{
240 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
241 if (jsClass->callbacks.hasInstance)
242 return true;
243
244 return false;
245}
246
247bool JSCallbackObject::hasInstance(ExecState *exec, JSValue *value)
248{
249 JSContextRef execRef = toRef(exec);
250 JSObjectRef thisRef = toRef(this);
251
252 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
253 if (JSObjectHasInstanceCallback hasInstance = jsClass->callbacks.hasInstance)
254 return hasInstance(execRef, thisRef, toRef(value), toRef(exec->exceptionSlot()));
255
256 ASSERT(0); // implementsHasInstance should prevent us from reaching here
257 return 0;
258}
259
260
261bool JSCallbackObject::implementsCall() const
262{
263 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
264 if (jsClass->callbacks.callAsFunction)
265 return true;
266
267 return false;
268}
269
270JSValue* JSCallbackObject::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
271{
272 JSContextRef execRef = toRef(exec);
273 JSObjectRef thisRef = toRef(this);
274 JSObjectRef thisObjRef = toRef(thisObj);
275
276 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
277 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callbacks.callAsFunction) {
278 size_t argc = args.size();
279 JSValueRef argv[argc];
280 for (size_t i = 0; i < argc; i++)
281 argv[i] = toRef(args[i]);
282 return toJS(callAsFunction(execRef, thisRef, thisObjRef, argc, argv, toRef(exec->exceptionSlot())));
283 }
284 }
285
286 ASSERT(0); // implementsCall should prevent us from reaching here
287 return 0;
288}
289
290void JSCallbackObject::getPropertyList(ExecState* exec, ReferenceList& propertyList, bool recursive)
291{
292 JSContextRef context = toRef(exec);
293 JSObjectRef thisRef = toRef(this);
294
295 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
296 if (JSObjectAddPropertiesToListCallback addPropertiesToList = jsClass->callbacks.addPropertiesToList)
297 addPropertiesToList(context, thisRef, toRef(&propertyList), toRef(exec->exceptionSlot()));
298
299 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
300 typedef __JSClass::StaticValuesTable::const_iterator iterator;
301 iterator end = staticValues->end();
302 for (iterator it = staticValues->begin(); it != end; ++it) {
303 UString::Rep* name = it->first.get();
304 StaticValueEntry* entry = it->second;
305 if (entry->getProperty && !(entry->attributes & kJSPropertyAttributeDontEnum))
306 propertyList.append(Reference(this, Identifier(name)));
307 }
308 }
309
310 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
311 typedef __JSClass::StaticFunctionsTable::const_iterator iterator;
312 iterator end = staticFunctions->end();
313 for (iterator it = staticFunctions->begin(); it != end; ++it) {
314 UString::Rep* name = it->first.get();
315 StaticFunctionEntry* entry = it->second;
316 if (!(entry->attributes & kJSPropertyAttributeDontEnum))
317 propertyList.append(Reference(this, Identifier(name)));
318 }
319 }
320 }
321
322 JSObject::getPropertyList(exec, propertyList, recursive);
323}
324
325bool JSCallbackObject::toBoolean(ExecState* exec) const
326{
327 JSContextRef context = toRef(exec);
328 JSObjectRef thisRef = toRef(this);
329
330 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
331 if (JSObjectConvertToTypeCallback convertToType = jsClass->callbacks.convertToType)
332 if (JSValueRef value = convertToType(context, thisRef, kJSTypeBoolean, toRef(exec->exceptionSlot())))
333 return toJS(value)->getBoolean();
334
335 return JSObject::toBoolean(exec);
336}
337
338double JSCallbackObject::toNumber(ExecState* exec) const
339{
340 JSContextRef context = toRef(exec);
341 JSObjectRef thisRef = toRef(this);
342
343 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
344 if (JSObjectConvertToTypeCallback convertToType = jsClass->callbacks.convertToType)
345 if (JSValueRef value = convertToType(context, thisRef, kJSTypeNumber, toRef(exec->exceptionSlot())))
346 return toJS(value)->getNumber();
347
348 return JSObject::toNumber(exec);
349}
350
351UString JSCallbackObject::toString(ExecState* exec) const
352{
353 JSContextRef context = toRef(exec);
354 JSObjectRef thisRef = toRef(this);
355
356 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
357 if (JSObjectConvertToTypeCallback convertToType = jsClass->callbacks.convertToType)
358 if (JSValueRef value = convertToType(context, thisRef, kJSTypeString, toRef(exec->exceptionSlot())))
359 return toJS(value)->getString();
360
361 return JSObject::toString(exec);
362}
363
364void JSCallbackObject::setPrivate(void* data)
365{
366 m_privateData = data;
367}
368
369void* JSCallbackObject::getPrivate()
370{
371 return m_privateData;
372}
373
374bool JSCallbackObject::inherits(JSClassRef c) const
375{
376 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
377 if (jsClass == c)
378 return true;
379
380 return false;
381}
382
383JSValue* JSCallbackObject::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
384{
385 JSValue* v = slot.slotBase();
386 ASSERT(v);
387 return v;
388}
389
390JSValue* JSCallbackObject::staticValueGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
391{
392 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
393 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
394
395 JSObjectRef thisRef = toRef(thisObj);
396 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
397
398 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parent)
399 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues)
400 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep()))
401 if (JSObjectGetPropertyCallback getProperty = entry->getProperty)
402 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
403 return toJS(value);
404
405 return jsUndefined();
406}
407
408JSValue* JSCallbackObject::staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
409{
410 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
411 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
412
413 if (JSValue* cachedOrOverrideValue = thisObj->getDirect(propertyName))
414 return cachedOrOverrideValue;
415
416 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parent) {
417 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
418 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
419 JSValue* v = toJS(JSObjectMakeFunction(toRef(exec), entry->callAsFunction));
420 thisObj->putDirect(propertyName, v, entry->attributes);
421 return v;
422 }
423 }
424 }
425
426 return jsUndefined();
427}
428
429JSValue* JSCallbackObject::callbackGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
430{
431 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
432 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
433
434 JSObjectRef thisRef = toRef(thisObj);
435 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
436
437 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parent)
438 if (JSObjectGetPropertyCallback getProperty = jsClass->callbacks.getProperty)
439 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
440 return toJS(value);
441
442 return jsUndefined();
443}
444
445} // namespace KJS
Note: See TracBrowser for help on using the repository browser.