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

Last change on this file since 15310 was 15310, checked in by ggaren, 19 years ago

Reviewed by Darin.

Improved type safety by implementing opaque JSValue/JSObject typing through
abuse of 'const', not void*. Also fixed an alarming number of bugs
exposed by this new type safety.


I made one design change in JavaScriptCore, which is that the JSObject
constructor should take a JSValue* as its prototype argument, not a JSObject*,
since we allow the prototype to be any JSValue*, including jsNull(), for
example.


  • API/APICast.h: (toJS):
  • API/JSBase.h:
  • API/JSCallbackConstructor.cpp: (KJS::JSCallbackConstructor::construct):
  • API/JSCallbackFunction.cpp: (KJS::JSCallbackFunction::callAsFunction):
  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::JSCallbackObject): (KJS::JSCallbackObject::getOwnPropertySlot): (KJS::JSCallbackObject::put): (KJS::JSCallbackObject::construct): (KJS::JSCallbackObject::callAsFunction): (KJS::JSCallbackObject::staticFunctionGetter):
  • API/JSCallbackObject.h:
  • API/JSContextRef.cpp: (JSEvaluate):
  • API/JSNode.c: (JSNodePrototype_appendChild): (JSNodePrototype_removeChild): (JSNodePrototype_replaceChild):
  • API/JSObjectRef.cpp: (JSObjectMake): (JSFunctionMakeWithBody): (JSObjectGetProperty): (JSObjectCallAsFunction): (JSObjectCallAsConstructor):
  • API/JSObjectRef.h:
  • API/testapi.c: (main):
  • ChangeLog:
  • kjs/object.h: (KJS::JSObject::JSObject):
File size: 16.2 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 m_privateData = 0;
55 m_class = JSClassRetain(jsClass);
56
57 JSObjectRef thisRef = toRef(this);
58
59 do {
60 if (JSInitializeCallback initialize = jsClass->callbacks.initialize)
61 initialize(context, thisRef);
62 } while ((jsClass = jsClass->parent));
63}
64
65JSCallbackObject::~JSCallbackObject()
66{
67 JSObjectRef thisRef = toRef(this);
68
69 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
70 if (JSFinalizeCallback finalize = jsClass->callbacks.finalize)
71 finalize(thisRef);
72
73 JSClassRelease(m_class);
74}
75
76UString JSCallbackObject::className() const
77{
78 return classInfo()->className;
79}
80
81bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
82{
83 JSContextRef context = toRef(exec);
84 JSObjectRef thisRef = toRef(this);
85 JSInternalStringRef propertyNameRef = toRef(propertyName.ustring().rep());
86
87 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
88 // optional optimization to bypass getProperty in cases when we only need to know if the property exists
89 if (JSHasPropertyCallback hasPropertyCallback = jsClass->callbacks.hasProperty) {
90 if (hasPropertyCallback(context, thisRef, propertyNameRef)) {
91 slot.setCustom(this, callbackGetter);
92 return true;
93 }
94 } else if (JSGetPropertyCallback getPropertyCallback = jsClass->callbacks.getProperty) {
95 JSValueRef returnValue;
96 if (getPropertyCallback(context, thisRef, propertyNameRef, &returnValue)) {
97 // cache the value so we don't have to compute it again
98 // FIXME: This violates the PropertySlot design a little bit.
99 // We should either use this optimization everywhere, or nowhere.
100 slot.setCustom(reinterpret_cast<JSObject*>(toJS(returnValue)), cachedValueGetter);
101 return true;
102 }
103 }
104
105 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
106 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
107 if (entry->getProperty) {
108 slot.setCustom(this, staticValueGetter);
109 return true;
110 }
111 }
112 }
113
114 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
115 if (staticFunctions->contains(propertyName.ustring().rep())) {
116 slot.setCustom(this, staticFunctionGetter);
117 return true;
118 }
119 }
120 }
121
122 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
123}
124
125bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
126{
127 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
128}
129
130void JSCallbackObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
131{
132 JSContextRef context = toRef(exec);
133 JSObjectRef thisRef = toRef(this);
134 JSInternalStringRef propertyNameRef = toRef(propertyName.ustring().rep());
135 JSValueRef valueRef = toRef(value);
136
137 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
138 if (JSSetPropertyCallback setPropertyCallback = jsClass->callbacks.setProperty) {
139 if (setPropertyCallback(context, thisRef, propertyNameRef, valueRef))
140 return;
141 }
142
143 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
144 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
145 if (entry->attributes & kJSPropertyAttributeReadOnly)
146 return;
147 if (JSSetPropertyCallback setPropertyCallback = entry->setProperty) {
148 if (setPropertyCallback(context, thisRef, propertyNameRef, valueRef))
149 return;
150 }
151 }
152 }
153
154 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
155 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
156 if (entry->attributes & kJSPropertyAttributeReadOnly)
157 return;
158 putDirect(propertyName, value, attr); // put as override property
159 return;
160 }
161 }
162 }
163 return JSObject::put(exec, propertyName, value, attr);
164}
165
166void JSCallbackObject::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr)
167{
168 return put(exec, Identifier::from(propertyName), value, attr);
169}
170
171bool JSCallbackObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
172{
173 JSContextRef context = toRef(exec);
174 JSObjectRef thisRef = toRef(this);
175 JSInternalStringRef propertyNameRef = toRef(propertyName.ustring().rep());
176
177 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
178 if (JSDeletePropertyCallback deletePropertyCallback = jsClass->callbacks.deleteProperty) {
179 if (deletePropertyCallback(context, thisRef, propertyNameRef))
180 return true;
181 }
182
183 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
184 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
185 if (entry->attributes & kJSPropertyAttributeDontDelete)
186 return false;
187 return true;
188 }
189 }
190
191 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
192 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
193 if (entry->attributes & kJSPropertyAttributeDontDelete)
194 return false;
195 return true;
196 }
197 }
198 }
199 return JSObject::deleteProperty(exec, propertyName);
200}
201
202bool JSCallbackObject::deleteProperty(ExecState* exec, unsigned propertyName)
203{
204 return deleteProperty(exec, Identifier::from(propertyName));
205}
206
207bool JSCallbackObject::implementsConstruct() const
208{
209 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
210 if (jsClass->callbacks.callAsConstructor)
211 return true;
212
213 return false;
214}
215
216JSObject* JSCallbackObject::construct(ExecState* exec, const List& args)
217{
218 JSContextRef execRef = toRef(exec);
219 JSObjectRef thisRef = toRef(this);
220
221 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
222 if (JSCallAsConstructorCallback callAsConstructorCallback = jsClass->callbacks.callAsConstructor) {
223 size_t argc = args.size();
224 JSValueRef argv[argc];
225 for (size_t i = 0; i < argc; i++)
226 argv[i] = toRef(args[i]);
227 return toJS(callAsConstructorCallback(execRef, thisRef, argc, argv));
228 }
229 }
230
231 ASSERT(0); // implementsConstruct should prevent us from reaching here
232 return 0;
233}
234
235bool JSCallbackObject::implementsCall() const
236{
237 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
238 if (jsClass->callbacks.callAsFunction)
239 return true;
240
241 return false;
242}
243
244JSValue* JSCallbackObject::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
245{
246 JSContextRef execRef = toRef(exec);
247 JSObjectRef thisRef = toRef(this);
248 JSObjectRef thisObjRef = toRef(thisObj);
249
250 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
251 if (JSCallAsFunctionCallback callAsFunctionCallback = jsClass->callbacks.callAsFunction) {
252 size_t argc = args.size();
253 JSValueRef argv[argc];
254 for (size_t i = 0; i < argc; i++)
255 argv[i] = toRef(args[i]);
256 return toJS(callAsFunctionCallback(execRef, thisRef, thisObjRef, argc, argv));
257 }
258 }
259
260 ASSERT(0); // implementsCall should prevent us from reaching here
261 return 0;
262}
263
264void JSCallbackObject::getPropertyList(ExecState* exec, ReferenceList& propertyList, bool recursive)
265{
266 JSContextRef context = toRef(exec);
267 JSObjectRef thisRef = toRef(this);
268
269 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
270 if (JSGetPropertyListCallback getPropertyListCallback = jsClass->callbacks.getPropertyList)
271 getPropertyListCallback(context, thisRef, toRef(&propertyList));
272
273 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
274 typedef __JSClass::StaticValuesTable::const_iterator iterator;
275 iterator end = staticValues->end();
276 for (iterator it = staticValues->begin(); it != end; ++it) {
277 UString::Rep* name = it->first.get();
278 StaticValueEntry* entry = it->second;
279 if (entry->getProperty && !(entry->attributes & kJSPropertyAttributeDontEnum))
280 propertyList.append(Reference(this, Identifier(name)));
281 }
282 }
283
284 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
285 typedef __JSClass::StaticFunctionsTable::const_iterator iterator;
286 iterator end = staticFunctions->end();
287 for (iterator it = staticFunctions->begin(); it != end; ++it) {
288 UString::Rep* name = it->first.get();
289 StaticFunctionEntry* entry = it->second;
290 if (!(entry->attributes & kJSPropertyAttributeDontEnum))
291 propertyList.append(Reference(this, Identifier(name)));
292 }
293 }
294 }
295
296 JSObject::getPropertyList(exec, propertyList, recursive);
297}
298
299bool JSCallbackObject::toBoolean(ExecState* exec) const
300{
301 JSContextRef context = toRef(exec);
302 JSObjectRef thisRef = toRef(this);
303
304 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
305 if (JSConvertToTypeCallback convertToTypeCallback = jsClass->callbacks.convertToType) {
306 JSValueRef returnValue;
307 if (convertToTypeCallback(context, thisRef, kJSTypeBoolean, &returnValue))
308 return toJS(returnValue)->getBoolean();
309 }
310 }
311 return JSObject::toBoolean(exec);
312}
313
314double JSCallbackObject::toNumber(ExecState* exec) const
315{
316 JSContextRef context = toRef(exec);
317 JSObjectRef thisRef = toRef(this);
318
319 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
320 if (JSConvertToTypeCallback convertToTypeCallback = jsClass->callbacks.convertToType) {
321 JSValueRef returnValue;
322 if (convertToTypeCallback(context, thisRef, kJSTypeNumber, &returnValue))
323 return toJS(returnValue)->getNumber();
324 }
325 }
326 return JSObject::toNumber(exec);
327}
328
329UString JSCallbackObject::toString(ExecState* exec) const
330{
331 JSContextRef context = toRef(exec);
332 JSObjectRef thisRef = toRef(this);
333
334 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
335 if (JSConvertToTypeCallback convertToTypeCallback = jsClass->callbacks.convertToType) {
336 JSValueRef returnValue;
337 if (convertToTypeCallback(context, thisRef, kJSTypeString, &returnValue))
338 return toJS(returnValue)->getString();
339 }
340 }
341 return JSObject::toString(exec);
342}
343
344void JSCallbackObject::setPrivate(void* data)
345{
346 m_privateData = data;
347}
348
349void* JSCallbackObject::getPrivate()
350{
351 return m_privateData;
352}
353
354bool JSCallbackObject::inherits(JSClassRef c) const
355{
356 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
357 if (jsClass == c)
358 return true;
359 return false;
360}
361
362JSValue* JSCallbackObject::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
363{
364 JSValue* v = slot.slotBase();
365 ASSERT(v);
366 return v;
367}
368
369JSValue* JSCallbackObject::staticValueGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
370{
371 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
372 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
373
374 JSObjectRef thisRef = toRef(thisObj);
375 JSInternalStringRef propertyNameRef = toRef(propertyName.ustring().rep());
376
377 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parent) {
378 JSValueRef returnValue;
379
380 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues)
381 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep()))
382 if (JSGetPropertyCallback getPropertyCallback = entry->getProperty)
383 if (getPropertyCallback(toRef(exec), thisRef, propertyNameRef, &returnValue))
384 return toJS(returnValue);
385 }
386
387 return jsUndefined();
388}
389
390JSValue* JSCallbackObject::staticFunctionGetter(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 if (JSValue* cachedOrOverrideValue = thisObj->getDirect(propertyName))
396 return cachedOrOverrideValue;
397
398 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parent) {
399 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
400 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
401 JSValue* v = toJS(JSFunctionMake(toRef(exec), entry->callAsFunction));
402 thisObj->putDirect(propertyName, v, entry->attributes);
403 return v;
404 }
405 }
406 }
407
408 return jsUndefined();
409}
410
411JSValue* JSCallbackObject::callbackGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
412{
413 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
414 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
415
416 JSObjectRef thisRef = toRef(thisObj);
417 JSInternalStringRef propertyNameRef = toRef(propertyName.ustring().rep());
418
419 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parent) {
420 JSValueRef returnValue;
421
422 if (JSGetPropertyCallback getPropertyCallback = jsClass->callbacks.getProperty)
423 if (getPropertyCallback(toRef(exec), thisRef, propertyNameRef, &returnValue))
424 return toJS(returnValue);
425 }
426
427 return jsUndefined();
428}
429
430} // namespace KJS
Note: See TracBrowser for help on using the repository browser.