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

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

JavaScriptCore:

2008-08-17 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Made room for a free word in JSCell.


SunSpider says no change.


I changed JSCallbackObjectData, Arguments, JSArray, and RegExpObject to
store auxiliary data in a secondary structure.

I changed InternalFunction to store the function's name in the property
map.


I changed JSGlobalObjectData to use a virtual destructor, so WebCore's
JSDOMWindowBaseData could inherit from it safely. (It's a strange design
for JSDOMWindowBase to allocate an object that JSGlobalObject deletes,
but that's really our only option, given the size constraint.)


I also added a bunch of compile-time ASSERTs, and removed lots of comments
in JSObject.h because they were often out of date, and they got in the
way of reading what was actually going on.


Also renamed JSArray::getLength to JSArray::length, to match our style
guidelines.

WebCore:

2008-08-17 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Made room for a free word in JSCell.


Changed JSDOMWindowBase to store its auxiliary data in a subclass of
JSGlobalData, so the two could share a pointer.


Added a bunch of ASSERTs, to help catch over-sized objects.

WebKit/mac:

2008-08-17 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Made room for a free word in JSCell.


(Updated for JavaScriptCore changes.)

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