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

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

Reviewed by Maciej.


  • Phase 2 in the JS API.


  • Added support for specifying static tables of values -- this should obviate the need for using complicated callbacks for most lookups.


  • API objects are now created with classes (JSClassRef) -- in order to support static values, and in order to prevent API objects from storing their data inline, and thus falling into the oversized (read: slow and prone to giving Maciej the frowny face) heap.


  • Added two specialized JSObject subclasses -- JSCallbackFunction and JSCallbackConstructor -- to allow JSFunctionMake and JSConstructorMake to continue to work with the new class model. Another solution to this problem would be to create a custom class object for each function and constructor you make. This solution is more code but also more efficient.


  • Substantially beefed up the minidom example to demonstrate and test a lot of these techniques. Its output is still pretty haphazard, though.


  • Gave the <kjs/ preface to some includes -- I'm told this matters to building on some versions of Linux.


  • Implemented JSValueIsInstanceOf and JSValueIsObjectOfClass


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