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

Last change on this file since 25257 was 25257, checked in by bdash, 18 years ago

2007-08-26 Mark Rowe <[email protected]>

Reviewed by Darin Adler.

<rdar://problem/4949002> JSGlobalContextCreate can cause crashes because it passes a NULL JSContextRef to the globalObjectClass's initialize callback

JSCallbackObject now tracks whether it was constructed with a null ExecState. This will happen when the object is being used as the global object,
as the Interpreter needs to be created after the global object. In this situation the initialization is deferred until after the Interpreter's
ExecState is available to be passed down to the initialize callbacks.

  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::init): Track whether we successfully initialized. (KJS::JSCallbackObject::initializeIfNeeded): Attempt to initialize with the new ExecState.
  • API/JSCallbackObject.h:
  • API/JSContextRef.cpp: (JSGlobalContextCreate): Initialize the JSCallbackObject with the Interpreter's ExecState.
  • API/testapi.c: (testInitializeOfGlobalObjectClassHasNonNullContext): (main): Verify that the context passed to the initialize callback is non-null.
File size: 18.1 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 <wtf/Platform.h>
28#include "JSCallbackObject.h"
29
30#include "APICast.h"
31#include "JSCallbackFunction.h"
32#include "JSClassRef.h"
33#include "JSObjectRef.h"
34#include "JSStringRef.h"
35#include "PropertyNameArray.h"
36#include "internal.h"
37#include <wtf/Vector.h>
38
39namespace KJS {
40
41const ClassInfo JSCallbackObject::info = { "CallbackObject", 0, 0, 0 };
42
43JSCallbackObject::JSCallbackObject(ExecState* exec, JSClassRef jsClass, JSValue* prototype, void* data)
44 : JSObject(prototype)
45 , m_isInitialized(false)
46{
47 init(exec, jsClass, data);
48}
49
50void JSCallbackObject::init(ExecState* exec, JSClassRef jsClass, void* data)
51{
52 m_privateData = data;
53 JSClassRef oldClass = m_class;
54 m_class = JSClassRetain(jsClass);
55 if (oldClass)
56 JSClassRelease(oldClass);
57
58 if (!exec)
59 return;
60
61 Vector<JSObjectInitializeCallback, 16> initRoutines;
62 do {
63 if (JSObjectInitializeCallback initialize = jsClass->initialize)
64 initRoutines.append(initialize);
65 } while ((jsClass = jsClass->parentClass));
66
67 // initialize from base to derived
68 for (int i = static_cast<int>(initRoutines.size()) - 1; i >= 0; i--) {
69 JSLock::DropAllLocks dropAllLocks;
70 JSObjectInitializeCallback initialize = initRoutines[i];
71 initialize(toRef(exec), toRef(this));
72 }
73 m_isInitialized = true;
74}
75
76JSCallbackObject::~JSCallbackObject()
77{
78 JSObjectRef thisRef = toRef(this);
79
80 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
81 if (JSObjectFinalizeCallback finalize = jsClass->finalize) {
82 finalize(thisRef);
83 }
84
85 JSClassRelease(m_class);
86}
87
88void JSCallbackObject::initializeIfNeeded(ExecState* exec)
89{
90 if (m_isInitialized)
91 return;
92 init(exec, m_class, m_privateData);
93}
94
95UString JSCallbackObject::className() const
96{
97 if (!m_class->className.isNull())
98 return m_class->className;
99
100 return JSObject::className();
101}
102
103bool JSCallbackObject::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 JSObject::getOwnPropertySlot(exec, propertyName, slot);
144}
145
146bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
147{
148 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
149}
150
151void JSCallbackObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
152{
153 JSContextRef ctx = toRef(exec);
154 JSObjectRef thisRef = toRef(this);
155 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
156 JSValueRef valueRef = toRef(value);
157
158 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
159 if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) {
160 JSLock::DropAllLocks dropAllLocks;
161 if (setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
162 return;
163 }
164
165 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
166 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
167 if (entry->attributes & kJSPropertyAttributeReadOnly)
168 return;
169 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
170 JSLock::DropAllLocks dropAllLocks;
171 if (setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
172 return;
173 } else
174 throwError(exec, ReferenceError, "Attempt to set a property that is not settable.");
175 }
176 }
177
178 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
179 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
180 if (entry->attributes & kJSPropertyAttributeReadOnly)
181 return;
182 putDirect(propertyName, value, attr); // put as override property
183 return;
184 }
185 }
186 }
187
188 return JSObject::put(exec, propertyName, value, attr);
189}
190
191void JSCallbackObject::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr)
192{
193 return put(exec, Identifier::from(propertyName), value, attr);
194}
195
196bool JSCallbackObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
197{
198 JSContextRef ctx = toRef(exec);
199 JSObjectRef thisRef = toRef(this);
200 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
201
202 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
203 if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) {
204 JSLock::DropAllLocks dropAllLocks;
205 if (deleteProperty(ctx, thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
206 return true;
207 }
208
209 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
210 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
211 if (entry->attributes & kJSPropertyAttributeDontDelete)
212 return false;
213 return true;
214 }
215 }
216
217 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
218 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
219 if (entry->attributes & kJSPropertyAttributeDontDelete)
220 return false;
221 return true;
222 }
223 }
224 }
225
226 return JSObject::deleteProperty(exec, propertyName);
227}
228
229bool JSCallbackObject::deleteProperty(ExecState* exec, unsigned propertyName)
230{
231 return deleteProperty(exec, Identifier::from(propertyName));
232}
233
234bool JSCallbackObject::implementsConstruct() const
235{
236 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
237 if (jsClass->callAsConstructor)
238 return true;
239
240 return false;
241}
242
243JSObject* JSCallbackObject::construct(ExecState* exec, const List& args)
244{
245 JSContextRef execRef = toRef(exec);
246 JSObjectRef thisRef = toRef(this);
247
248 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
249 if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) {
250 int argumentCount = static_cast<int>(args.size());
251 Vector<JSValueRef, 16> arguments(argumentCount);
252 for (int i = 0; i < argumentCount; i++)
253 arguments[i] = toRef(args[i]);
254 JSLock::DropAllLocks dropAllLocks;
255 return toJS(callAsConstructor(execRef, thisRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot())));
256 }
257 }
258
259 ASSERT(0); // implementsConstruct should prevent us from reaching here
260 return 0;
261}
262
263bool JSCallbackObject::implementsHasInstance() const
264{
265 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
266 if (jsClass->hasInstance)
267 return true;
268
269 return false;
270}
271
272bool JSCallbackObject::hasInstance(ExecState *exec, JSValue *value)
273{
274 JSContextRef execRef = toRef(exec);
275 JSObjectRef thisRef = toRef(this);
276
277 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
278 if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) {
279 JSLock::DropAllLocks dropAllLocks;
280 return hasInstance(execRef, thisRef, toRef(value), toRef(exec->exceptionSlot()));
281 }
282
283 ASSERT(0); // implementsHasInstance should prevent us from reaching here
284 return 0;
285}
286
287
288bool JSCallbackObject::implementsCall() const
289{
290 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
291 if (jsClass->callAsFunction)
292 return true;
293
294 return false;
295}
296
297JSValue* JSCallbackObject::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
298{
299 JSContextRef execRef = toRef(exec);
300 JSObjectRef thisRef = toRef(this);
301 JSObjectRef thisObjRef = toRef(thisObj);
302
303 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
304 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) {
305 int argumentCount = static_cast<int>(args.size());
306 Vector<JSValueRef, 16> arguments(argumentCount);
307 for (int i = 0; i < argumentCount; i++)
308 arguments[i] = toRef(args[i]);
309 JSLock::DropAllLocks dropAllLocks;
310 return toJS(callAsFunction(execRef, thisRef, thisObjRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot())));
311 }
312 }
313
314 ASSERT(0); // implementsCall should prevent us from reaching here
315 return 0;
316}
317
318void JSCallbackObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
319{
320 JSContextRef execRef = toRef(exec);
321 JSObjectRef thisRef = toRef(this);
322
323 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
324 if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) {
325 JSLock::DropAllLocks dropAllLocks;
326 getPropertyNames(execRef, thisRef, toRef(&propertyNames));
327 }
328
329 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
330 typedef OpaqueJSClass::StaticValuesTable::const_iterator iterator;
331 iterator end = staticValues->end();
332 for (iterator it = staticValues->begin(); it != end; ++it) {
333 UString::Rep* name = it->first.get();
334 StaticValueEntry* entry = it->second;
335 if (entry->getProperty && !(entry->attributes & kJSPropertyAttributeDontEnum))
336 propertyNames.add(Identifier(name));
337 }
338 }
339
340 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
341 typedef OpaqueJSClass::StaticFunctionsTable::const_iterator iterator;
342 iterator end = staticFunctions->end();
343 for (iterator it = staticFunctions->begin(); it != end; ++it) {
344 UString::Rep* name = it->first.get();
345 StaticFunctionEntry* entry = it->second;
346 if (!(entry->attributes & kJSPropertyAttributeDontEnum))
347 propertyNames.add(Identifier(name));
348 }
349 }
350 }
351
352 JSObject::getPropertyNames(exec, propertyNames);
353}
354
355double JSCallbackObject::toNumber(ExecState* exec) const
356{
357 JSContextRef ctx = toRef(exec);
358 JSObjectRef thisRef = toRef(this);
359
360 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
361 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
362 JSLock::DropAllLocks dropAllLocks;
363 if (JSValueRef value = convertToType(ctx, thisRef, kJSTypeNumber, toRef(exec->exceptionSlot())))
364 return toJS(value)->getNumber();
365 }
366
367 return JSObject::toNumber(exec);
368}
369
370UString JSCallbackObject::toString(ExecState* exec) const
371{
372 JSContextRef ctx = toRef(exec);
373 JSObjectRef thisRef = toRef(this);
374
375 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
376 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
377 JSLock::DropAllLocks dropAllLocks;
378 if (JSValueRef value = convertToType(ctx, thisRef, kJSTypeString, toRef(exec->exceptionSlot())))
379 return toJS(value)->getString();
380 }
381
382 return JSObject::toString(exec);
383}
384
385void JSCallbackObject::setPrivate(void* data)
386{
387 m_privateData = data;
388}
389
390void* JSCallbackObject::getPrivate()
391{
392 return m_privateData;
393}
394
395bool JSCallbackObject::inherits(JSClassRef c) const
396{
397 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
398 if (jsClass == c)
399 return true;
400
401 return false;
402}
403
404JSValue* JSCallbackObject::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
405{
406 JSValue* v = slot.slotBase();
407 ASSERT(v);
408 return v;
409}
410
411JSValue* JSCallbackObject::staticValueGetter(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 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
418
419 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass)
420 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues)
421 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep()))
422 if (JSObjectGetPropertyCallback getProperty = entry->getProperty) {
423 JSLock::DropAllLocks dropAllLocks;
424 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
425 return toJS(value);
426 }
427
428 return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback.");
429}
430
431JSValue* JSCallbackObject::staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
432{
433 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
434 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
435
436 if (JSValue* cachedOrOverrideValue = thisObj->getDirect(propertyName))
437 return cachedOrOverrideValue;
438
439 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) {
440 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
441 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
442 if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) {
443 JSObject* o = new JSCallbackFunction(exec, callAsFunction, propertyName);
444 thisObj->putDirect(propertyName, o, entry->attributes);
445 return o;
446 }
447 }
448 }
449 }
450
451 return throwError(exec, ReferenceError, "Static function property defined with NULL callAsFunction callback.");
452}
453
454JSValue* JSCallbackObject::callbackGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
455{
456 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
457 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
458
459 JSObjectRef thisRef = toRef(thisObj);
460 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
461
462 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass)
463 if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
464 JSLock::DropAllLocks dropAllLocks;
465 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
466 return toJS(value);
467 }
468
469 return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist.");
470}
471
472} // namespace KJS
Note: See TracBrowser for help on using the repository browser.