• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
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 "Error.h"
29 #include "JSCallbackFunction.h"
30 #include "JSClassRef.h"
31 #include "JSGlobalObject.h"
32 #include "JSLock.h"
33 #include "JSObjectRef.h"
34 #include "JSString.h"
35 #include "JSStringRef.h"
36 #include "OpaqueJSString.h"
37 #include "PropertyNameArray.h"
38 #include <wtf/Vector.h>
39 
40 namespace JSC {
41 
42 template <class Base>
asCallbackObject(JSValuePtr value)43 inline JSCallbackObject<Base>* JSCallbackObject<Base>::asCallbackObject(JSValuePtr value)
44 {
45     ASSERT(asObject(value)->inherits(&info));
46     return static_cast<JSCallbackObject*>(asObject(value));
47 }
48 
49 template <class Base>
JSCallbackObject(ExecState * exec,PassRefPtr<Structure> structure,JSClassRef jsClass,void * data)50 JSCallbackObject<Base>::JSCallbackObject(ExecState* exec, PassRefPtr<Structure> structure, JSClassRef jsClass, void* data)
51     : Base(structure)
52     , m_callbackObjectData(new JSCallbackObjectData(data, jsClass))
53 {
54     init(exec);
55 }
56 
57 // Global object constructor.
58 // FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one.
59 template <class Base>
JSCallbackObject(JSClassRef jsClass)60 JSCallbackObject<Base>::JSCallbackObject(JSClassRef jsClass)
61     : Base()
62     , m_callbackObjectData(new JSCallbackObjectData(0, jsClass))
63 {
64     ASSERT(Base::isGlobalObject());
65     init(static_cast<JSGlobalObject*>(this)->globalExec());
66 }
67 
68 template <class Base>
init(ExecState * exec)69 void JSCallbackObject<Base>::init(ExecState* exec)
70 {
71     ASSERT(exec);
72 
73     Vector<JSObjectInitializeCallback, 16> initRoutines;
74     JSClassRef jsClass = classRef();
75     do {
76         if (JSObjectInitializeCallback initialize = jsClass->initialize)
77             initRoutines.append(initialize);
78     } while ((jsClass = jsClass->parentClass));
79 
80     // initialize from base to derived
81     for (int i = static_cast<int>(initRoutines.size()) - 1; i >= 0; i--) {
82         JSLock::DropAllLocks dropAllLocks(exec);
83         JSObjectInitializeCallback initialize = initRoutines[i];
84         initialize(toRef(exec), toRef(this));
85     }
86 }
87 
88 template <class Base>
~JSCallbackObject()89 JSCallbackObject<Base>::~JSCallbackObject()
90 {
91     JSObjectRef thisRef = toRef(this);
92 
93     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass)
94         if (JSObjectFinalizeCallback finalize = jsClass->finalize)
95             finalize(thisRef);
96 }
97 
98 template <class Base>
className()99 UString JSCallbackObject<Base>::className() const
100 {
101     UString thisClassName = classRef()->className();
102     if (!thisClassName.isNull())
103         return thisClassName;
104 
105     return Base::className();
106 }
107 
108 template <class Base>
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)109 bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
110 {
111     JSContextRef ctx = toRef(exec);
112     JSObjectRef thisRef = toRef(this);
113     RefPtr<OpaqueJSString> propertyNameRef;
114 
115     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
116         // optional optimization to bypass getProperty in cases when we only need to know if the property exists
117         if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) {
118             if (!propertyNameRef)
119                 propertyNameRef = OpaqueJSString::create(propertyName.ustring());
120             JSLock::DropAllLocks dropAllLocks(exec);
121             if (hasProperty(ctx, thisRef, propertyNameRef.get())) {
122                 slot.setCustom(this, callbackGetter);
123                 return true;
124             }
125         } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
126             if (!propertyNameRef)
127                 propertyNameRef = OpaqueJSString::create(propertyName.ustring());
128             JSLock::DropAllLocks dropAllLocks(exec);
129             if (JSValueRef value = getProperty(ctx, thisRef, propertyNameRef.get(), toRef(exec->exceptionSlot()))) {
130                 slot.setValue(toJS(value));
131                 return true;
132             }
133         }
134 
135         if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) {
136             if (staticValues->contains(propertyName.ustring().rep())) {
137                 slot.setCustom(this, staticValueGetter);
138                 return true;
139             }
140         }
141 
142         if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) {
143             if (staticFunctions->contains(propertyName.ustring().rep())) {
144                 slot.setCustom(this, staticFunctionGetter);
145                 return true;
146             }
147         }
148     }
149 
150     return Base::getOwnPropertySlot(exec, propertyName, slot);
151 }
152 
153 template <class Base>
getOwnPropertySlot(ExecState * exec,unsigned propertyName,PropertySlot & slot)154 bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
155 {
156     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
157 }
158 
159 template <class Base>
put(ExecState * exec,const Identifier & propertyName,JSValuePtr value,PutPropertySlot & slot)160 void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
161 {
162     JSContextRef ctx = toRef(exec);
163     JSObjectRef thisRef = toRef(this);
164     RefPtr<OpaqueJSString> propertyNameRef;
165     JSValueRef valueRef = toRef(value);
166 
167     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
168         if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) {
169             if (!propertyNameRef)
170                 propertyNameRef = OpaqueJSString::create(propertyName.ustring());
171             JSLock::DropAllLocks dropAllLocks(exec);
172             if (setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, toRef(exec->exceptionSlot())))
173                 return;
174         }
175 
176         if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) {
177             if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
178                 if (entry->attributes & kJSPropertyAttributeReadOnly)
179                     return;
180                 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
181                     if (!propertyNameRef)
182                         propertyNameRef = OpaqueJSString::create(propertyName.ustring());
183                     JSLock::DropAllLocks dropAllLocks(exec);
184                     if (setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, toRef(exec->exceptionSlot())))
185                         return;
186                 } else
187                     throwError(exec, ReferenceError, "Attempt to set a property that is not settable.");
188             }
189         }
190 
191         if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) {
192             if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
193                 if (entry->attributes & kJSPropertyAttributeReadOnly)
194                     return;
195                 JSCallbackObject<Base>::putDirect(propertyName, value); // put as override property
196                 return;
197             }
198         }
199     }
200 
201     return Base::put(exec, propertyName, value, slot);
202 }
203 
204 template <class Base>
deleteProperty(ExecState * exec,const Identifier & propertyName)205 bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, const Identifier& propertyName)
206 {
207     JSContextRef ctx = toRef(exec);
208     JSObjectRef thisRef = toRef(this);
209     RefPtr<OpaqueJSString> propertyNameRef;
210 
211     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
212         if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) {
213             if (!propertyNameRef)
214                 propertyNameRef = OpaqueJSString::create(propertyName.ustring());
215             JSLock::DropAllLocks dropAllLocks(exec);
216             if (deleteProperty(ctx, thisRef, propertyNameRef.get(), toRef(exec->exceptionSlot())))
217                 return true;
218         }
219 
220         if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) {
221             if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
222                 if (entry->attributes & kJSPropertyAttributeDontDelete)
223                     return false;
224                 return true;
225             }
226         }
227 
228         if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) {
229             if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
230                 if (entry->attributes & kJSPropertyAttributeDontDelete)
231                     return false;
232                 return true;
233             }
234         }
235     }
236 
237     return Base::deleteProperty(exec, propertyName);
238 }
239 
240 template <class Base>
deleteProperty(ExecState * exec,unsigned propertyName)241 bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, unsigned propertyName)
242 {
243     return deleteProperty(exec, Identifier::from(exec, propertyName));
244 }
245 
246 template <class Base>
getConstructData(ConstructData & constructData)247 ConstructType JSCallbackObject<Base>::getConstructData(ConstructData& constructData)
248 {
249     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
250         if (jsClass->callAsConstructor) {
251             constructData.native.function = construct;
252             return ConstructTypeHost;
253         }
254     }
255     return ConstructTypeNone;
256 }
257 
258 template <class Base>
construct(ExecState * exec,JSObject * constructor,const ArgList & args)259 JSObject* JSCallbackObject<Base>::construct(ExecState* exec, JSObject* constructor, const ArgList& args)
260 {
261     JSContextRef execRef = toRef(exec);
262     JSObjectRef constructorRef = toRef(constructor);
263 
264     for (JSClassRef jsClass = static_cast<JSCallbackObject<Base>*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) {
265         if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) {
266             int argumentCount = static_cast<int>(args.size());
267             Vector<JSValueRef, 16> arguments(argumentCount);
268             for (int i = 0; i < argumentCount; i++)
269                 arguments[i] = toRef(args.at(exec, i));
270             JSLock::DropAllLocks dropAllLocks(exec);
271             return toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot())));
272         }
273     }
274 
275     ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
276     return 0;
277 }
278 
279 template <class Base>
hasInstance(ExecState * exec,JSValuePtr value,JSValuePtr)280 bool JSCallbackObject<Base>::hasInstance(ExecState* exec, JSValuePtr value, JSValuePtr)
281 {
282     JSContextRef execRef = toRef(exec);
283     JSObjectRef thisRef = toRef(this);
284 
285     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
286         if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) {
287             JSLock::DropAllLocks dropAllLocks(exec);
288             return hasInstance(execRef, thisRef, toRef(value), toRef(exec->exceptionSlot()));
289         }
290     }
291     return false;
292 }
293 
294 template <class Base>
getCallData(CallData & callData)295 CallType JSCallbackObject<Base>::getCallData(CallData& callData)
296 {
297     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
298         if (jsClass->callAsFunction) {
299             callData.native.function = call;
300             return CallTypeHost;
301         }
302     }
303     return CallTypeNone;
304 }
305 
306 template <class Base>
call(ExecState * exec,JSObject * functionObject,JSValuePtr thisValue,const ArgList & args)307 JSValuePtr JSCallbackObject<Base>::call(ExecState* exec, JSObject* functionObject, JSValuePtr thisValue, const ArgList& args)
308 {
309     JSContextRef execRef = toRef(exec);
310     JSObjectRef functionRef = toRef(functionObject);
311     JSObjectRef thisObjRef = toRef(thisValue.toThisObject(exec));
312 
313     for (JSClassRef jsClass = static_cast<JSCallbackObject<Base>*>(functionObject)->classRef(); 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.at(exec, i));
319             JSLock::DropAllLocks dropAllLocks(exec);
320             return toJS(callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot())));
321         }
322     }
323 
324     ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
325     return noValue();
326 }
327 
328 template <class Base>
getPropertyNames(ExecState * exec,PropertyNameArray & propertyNames)329 void JSCallbackObject<Base>::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
330 {
331     JSContextRef execRef = toRef(exec);
332     JSObjectRef thisRef = toRef(this);
333 
334     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
335         if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) {
336             JSLock::DropAllLocks dropAllLocks(exec);
337             getPropertyNames(execRef, thisRef, toRef(&propertyNames));
338         }
339 
340         if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) {
341             typedef OpaqueJSClassStaticValuesTable::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 (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) {
352             typedef OpaqueJSClassStaticFunctionsTable::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 
366 template <class Base>
toNumber(ExecState * exec)367 double 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 = classRef(); jsClass; jsClass = jsClass->parentClass)
378         if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
379             JSLock::DropAllLocks dropAllLocks(exec);
380             if (JSValueRef value = convertToType(ctx, thisRef, kJSTypeNumber, toRef(exec->exceptionSlot()))) {
381                 double dValue;
382                 return toJS(value).getNumber(dValue) ? dValue : NaN;
383             }
384         }
385 
386     return Base::toNumber(exec);
387 }
388 
389 template <class Base>
toString(ExecState * exec)390 UString JSCallbackObject<Base>::toString(ExecState* exec) const
391 {
392     JSContextRef ctx = toRef(exec);
393     JSObjectRef thisRef = toRef(this);
394 
395     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass)
396         if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
397             JSValueRef value;
398             {
399                 JSLock::DropAllLocks dropAllLocks(exec);
400                 value = convertToType(ctx, thisRef, kJSTypeString, toRef(exec->exceptionSlot()));
401             }
402             if (value)
403                 return toJS(value).getString();
404         }
405 
406     return Base::toString(exec);
407 }
408 
409 template <class Base>
setPrivate(void * data)410 void JSCallbackObject<Base>::setPrivate(void* data)
411 {
412     m_callbackObjectData->privateData = data;
413 }
414 
415 template <class Base>
getPrivate()416 void* JSCallbackObject<Base>::getPrivate()
417 {
418     return m_callbackObjectData->privateData;
419 }
420 
421 template <class Base>
inherits(JSClassRef c)422 bool JSCallbackObject<Base>::inherits(JSClassRef c) const
423 {
424     for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass)
425         if (jsClass == c)
426             return true;
427 
428     return false;
429 }
430 
431 template <class Base>
staticValueGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot & slot)432 JSValuePtr JSCallbackObject<Base>::staticValueGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
433 {
434     JSCallbackObject* thisObj = asCallbackObject(slot.slotBase());
435 
436     JSObjectRef thisRef = toRef(thisObj);
437     RefPtr<OpaqueJSString> propertyNameRef;
438 
439     for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass)
440         if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec))
441             if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep()))
442                 if (JSObjectGetPropertyCallback getProperty = entry->getProperty) {
443                     if (!propertyNameRef)
444                         propertyNameRef = OpaqueJSString::create(propertyName.ustring());
445                     JSLock::DropAllLocks dropAllLocks(exec);
446                     if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), toRef(exec->exceptionSlot())))
447                         return toJS(value);
448                 }
449 
450     return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback.");
451 }
452 
453 template <class Base>
staticFunctionGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot & slot)454 JSValuePtr JSCallbackObject<Base>::staticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
455 {
456     JSCallbackObject* thisObj = asCallbackObject(slot.slotBase());
457 
458     // Check for cached or override property.
459     PropertySlot slot2(thisObj);
460     if (thisObj->Base::getOwnPropertySlot(exec, propertyName, slot2))
461         return slot2.getValue(exec, propertyName);
462 
463     for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) {
464         if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) {
465             if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
466                 if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) {
467                     JSObject* o = new (exec) JSCallbackFunction(exec, callAsFunction, propertyName);
468                     thisObj->putDirect(propertyName, o, entry->attributes);
469                     return o;
470                 }
471             }
472         }
473     }
474 
475     return throwError(exec, ReferenceError, "Static function property defined with NULL callAsFunction callback.");
476 }
477 
478 template <class Base>
callbackGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot & slot)479 JSValuePtr JSCallbackObject<Base>::callbackGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
480 {
481     JSCallbackObject* thisObj = asCallbackObject(slot.slotBase());
482 
483     JSObjectRef thisRef = toRef(thisObj);
484     RefPtr<OpaqueJSString> propertyNameRef;
485 
486     for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass)
487         if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
488             if (!propertyNameRef)
489                 propertyNameRef = OpaqueJSString::create(propertyName.ustring());
490             JSLock::DropAllLocks dropAllLocks(exec);
491             if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), toRef(exec->exceptionSlot())))
492                 return toJS(value);
493         }
494 
495     return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist.");
496 }
497 
498 } // namespace JSC
499