source: webkit/trunk/JavaScriptCore/API/JSCallbackObjectFunctions.h@ 34607

Last change on this file since 34607 was 34607, checked in by [email protected], 17 years ago

Reviewed by Geoff Garen.

Make Identifier construction use an explicitly passed IdentifierTable.

No change on SunSpider total.

  • Property svn:eol-style set to native
File size: 19.4 KB
Line 
1// -*- mode: c++; c-basic-offset: 4 -*-
2/*
3 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2007 Eric Seidel <[email protected]>
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 "JSString.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(exec, propertyName), slot);
150}
151
152template <class Base>
153void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
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); // put as override property
185 return;
186 }
187 }
188 }
189
190 return Base::put(exec, propertyName, value);
191}
192
193template <class Base>
194void JSCallbackObject<Base>::put(ExecState* exec, unsigned propertyName, JSValue* value)
195{
196 return put(exec, Identifier::from(exec, propertyName), value);
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(exec, propertyName));
237}
238
239template <class Base>
240ConstructType JSCallbackObject<Base>::getConstructData(ConstructData&)
241{
242 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
243 if (jsClass->callAsConstructor)
244 return ConstructTypeNative;
245
246 return ConstructTypeNone;
247}
248
249template <class Base>
250JSObject* JSCallbackObject<Base>::construct(ExecState* exec, const ArgList& 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); // getConstructData 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
296template <class Base>
297CallType JSCallbackObject<Base>::getCallData(CallData&)
298{
299 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
300 if (jsClass->callAsFunction)
301 return CallTypeNative;
302
303 return CallTypeNone;
304}
305
306template <class Base>
307JSValue* JSCallbackObject<Base>::callAsFunction(ExecState* exec, JSObject* thisObj, const ArgList &args)
308{
309 JSContextRef execRef = toRef(exec);
310 JSObjectRef thisRef = toRef(this);
311 JSObjectRef thisObjRef = toRef(thisObj);
312
313 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
314 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) {
315 int argumentCount = static_cast<int>(args.size());
316 Vector<JSValueRef, 16> arguments(argumentCount);
317 for (int i = 0; i < argumentCount; i++)
318 arguments[i] = toRef(args[i]);
319 JSLock::DropAllLocks dropAllLocks;
320 return toJS(callAsFunction(execRef, thisRef, thisObjRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot())));
321 }
322 }
323
324 ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
325 return 0;
326}
327
328template <class Base>
329void JSCallbackObject<Base>::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
330{
331 JSContextRef execRef = toRef(exec);
332 JSObjectRef thisRef = toRef(this);
333
334 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
335 if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) {
336 JSLock::DropAllLocks dropAllLocks;
337 getPropertyNames(execRef, thisRef, toRef(&propertyNames));
338 }
339
340 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
341 typedef OpaqueJSClass::StaticValuesTable::const_iterator iterator;
342 iterator end = staticValues->end();
343 for (iterator it = staticValues->begin(); it != end; ++it) {
344 UString::Rep* name = it->first.get();
345 StaticValueEntry* entry = it->second;
346 if (entry->getProperty && !(entry->attributes & kJSPropertyAttributeDontEnum))
347 propertyNames.add(Identifier(exec, name));
348 }
349 }
350
351 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
352 typedef OpaqueJSClass::StaticFunctionsTable::const_iterator iterator;
353 iterator end = staticFunctions->end();
354 for (iterator it = staticFunctions->begin(); it != end; ++it) {
355 UString::Rep* name = it->first.get();
356 StaticFunctionEntry* entry = it->second;
357 if (!(entry->attributes & kJSPropertyAttributeDontEnum))
358 propertyNames.add(Identifier(exec, name));
359 }
360 }
361 }
362
363 Base::getPropertyNames(exec, propertyNames);
364}
365
366template <class Base>
367double JSCallbackObject<Base>::toNumber(ExecState* exec) const
368{
369 // We need this check to guard against the case where this object is rhs of
370 // a binary expression where lhs threw an exception in its conversion to
371 // primitive
372 if (exec->hadException())
373 return NaN;
374 JSContextRef ctx = toRef(exec);
375 JSObjectRef thisRef = toRef(this);
376
377 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
378 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
379 JSLock::DropAllLocks dropAllLocks;
380 if (JSValueRef value = convertToType(ctx, thisRef, kJSTypeNumber, toRef(exec->exceptionSlot())))
381 return toJS(value)->getNumber();
382 }
383
384 return Base::toNumber(exec);
385}
386
387template <class Base>
388UString JSCallbackObject<Base>::toString(ExecState* exec) const
389{
390 JSContextRef ctx = toRef(exec);
391 JSObjectRef thisRef = toRef(this);
392
393 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
394 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
395 JSValueRef value;
396 {
397 JSLock::DropAllLocks dropAllLocks;
398 value = convertToType(ctx, thisRef, kJSTypeString, toRef(exec->exceptionSlot()));
399 }
400 if (value)
401 return toJS(value)->getString();
402 }
403
404 return Base::toString(exec);
405}
406
407template <class Base>
408void JSCallbackObject<Base>::setPrivate(void* data)
409{
410 m_privateData = data;
411}
412
413template <class Base>
414void* JSCallbackObject<Base>::getPrivate()
415{
416 return m_privateData;
417}
418
419template <class Base>
420bool JSCallbackObject<Base>::inherits(JSClassRef c) const
421{
422 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
423 if (jsClass == c)
424 return true;
425
426 return false;
427}
428
429template <class Base>
430JSValue* JSCallbackObject<Base>::cachedValueGetter(ExecState*, const Identifier&, const PropertySlot& slot)
431{
432 JSValue* v = slot.slotBase();
433 ASSERT(v);
434 return v;
435}
436
437template <class Base>
438JSValue* JSCallbackObject<Base>::staticValueGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
439{
440 ASSERT(slot.slotBase()->isObject(&JSCallbackObject::info));
441 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
442
443 JSObjectRef thisRef = toRef(thisObj);
444 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
445
446 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass)
447 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues)
448 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep()))
449 if (JSObjectGetPropertyCallback getProperty = entry->getProperty) {
450 JSLock::DropAllLocks dropAllLocks;
451 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
452 return toJS(value);
453 }
454
455 return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback.");
456}
457
458template <class Base>
459JSValue* JSCallbackObject<Base>::staticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
460{
461 ASSERT(slot.slotBase()->isObject(&JSCallbackObject::info));
462 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
463
464 // Check for cached or override property.
465 PropertySlot slot2(thisObj);
466 if (thisObj->Base::getOwnPropertySlot(exec, propertyName, slot2))
467 return slot2.getValue(exec, propertyName);
468
469 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) {
470 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
471 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
472 if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) {
473 JSObject* o = new JSCallbackFunction(exec, callAsFunction, propertyName);
474 thisObj->putDirect(propertyName, o, entry->attributes);
475 return o;
476 }
477 }
478 }
479 }
480
481 return throwError(exec, ReferenceError, "Static function property defined with NULL callAsFunction callback.");
482}
483
484template <class Base>
485JSValue* JSCallbackObject<Base>::callbackGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
486{
487 ASSERT(slot.slotBase()->isObject(&JSCallbackObject::info));
488 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
489
490 JSObjectRef thisRef = toRef(thisObj);
491 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
492
493 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass)
494 if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
495 JSLock::DropAllLocks dropAllLocks;
496 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
497 return toJS(value);
498 }
499
500 return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist.");
501}
502
503} // namespace KJS
Note: See TracBrowser for help on using the repository browser.