Changeset 27022 in webkit for trunk/JavaScriptCore/API/JSCallbackObject.cpp
- Timestamp:
- Oct 24, 2007, 11:38:35 PM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/API/JSCallbackObject.cpp
r25258 r27022 2 2 /* 3 3 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 4 * Copyright (C) 2007 Eric Seidel <[email protected]> 4 5 * 5 6 * Redistribution and use in source and binary forms, with or without … … 25 26 */ 26 27 27 #include <wtf/Platform.h>28 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 29 39 30 namespace KJS { 40 31 41 const ClassInfo JSCallbackObject::info = { "CallbackObject", 0, 0, 0 }; 42 43 JSCallbackObject::JSCallbackObject(ExecState* exec, JSClassRef jsClass, JSValue* prototype, void* data) 44 : JSObject(prototype) 45 , m_class(0) 46 , m_isInitialized(false) 47 { 48 init(exec, jsClass, data); 49 } 50 51 void JSCallbackObject::init(ExecState* exec, JSClassRef jsClass, void* data) 52 { 53 m_privateData = data; 54 JSClassRef oldClass = m_class; 55 m_class = JSClassRetain(jsClass); 56 if (oldClass) 57 JSClassRelease(oldClass); 58 59 if (!exec) 60 return; 61 62 Vector<JSObjectInitializeCallback, 16> initRoutines; 63 do { 64 if (JSObjectInitializeCallback initialize = jsClass->initialize) 65 initRoutines.append(initialize); 66 } while ((jsClass = jsClass->parentClass)); 67 68 // initialize from base to derived 69 for (int i = static_cast<int>(initRoutines.size()) - 1; i >= 0; i--) { 70 JSLock::DropAllLocks dropAllLocks; 71 JSObjectInitializeCallback initialize = initRoutines[i]; 72 initialize(toRef(exec), toRef(this)); 73 } 74 m_isInitialized = true; 75 } 76 77 JSCallbackObject::~JSCallbackObject() 78 { 79 JSObjectRef thisRef = toRef(this); 80 81 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 82 if (JSObjectFinalizeCallback finalize = jsClass->finalize) { 83 finalize(thisRef); 84 } 85 86 JSClassRelease(m_class); 87 } 88 89 void JSCallbackObject::initializeIfNeeded(ExecState* exec) 90 { 91 if (m_isInitialized) 92 return; 93 init(exec, m_class, m_privateData); 94 } 95 96 UString JSCallbackObject::className() const 97 { 98 if (!m_class->className.isNull()) 99 return m_class->className; 100 101 return JSObject::className(); 102 } 103 104 bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 105 { 106 JSContextRef ctx = toRef(exec); 107 JSObjectRef thisRef = toRef(this); 108 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep()); 109 110 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) { 111 // optional optimization to bypass getProperty in cases when we only need to know if the property exists 112 if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) { 113 JSLock::DropAllLocks dropAllLocks; 114 if (hasProperty(ctx, thisRef, propertyNameRef)) { 115 slot.setCustom(this, callbackGetter); 116 return true; 117 } 118 } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { 119 JSLock::DropAllLocks dropAllLocks; 120 if (JSValueRef value = getProperty(ctx, thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) { 121 // cache the value so we don't have to compute it again 122 // FIXME: This violates the PropertySlot design a little bit. 123 // We should either use this optimization everywhere, or nowhere. 124 slot.setCustom(reinterpret_cast<JSObject*>(toJS(value)), cachedValueGetter); 125 return true; 126 } 127 } 128 129 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) { 130 if (staticValues->contains(propertyName.ustring().rep())) { 131 slot.setCustom(this, staticValueGetter); 132 return true; 133 } 134 } 135 136 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) { 137 if (staticFunctions->contains(propertyName.ustring().rep())) { 138 slot.setCustom(this, staticFunctionGetter); 139 return true; 140 } 141 } 142 } 143 144 return JSObject::getOwnPropertySlot(exec, propertyName, slot); 145 } 146 147 bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 148 { 149 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot); 150 } 151 152 void JSCallbackObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr) 153 { 154 JSContextRef ctx = toRef(exec); 155 JSObjectRef thisRef = toRef(this); 156 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep()); 157 JSValueRef valueRef = toRef(value); 158 159 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) { 160 if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { 161 JSLock::DropAllLocks dropAllLocks; 162 if (setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot()))) 163 return; 164 } 165 166 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) { 167 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) { 168 if (entry->attributes & kJSPropertyAttributeReadOnly) 169 return; 170 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { 171 JSLock::DropAllLocks dropAllLocks; 172 if (setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot()))) 173 return; 174 } else 175 throwError(exec, ReferenceError, "Attempt to set a property that is not settable."); 176 } 177 } 178 179 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) { 180 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) { 181 if (entry->attributes & kJSPropertyAttributeReadOnly) 182 return; 183 putDirect(propertyName, value, attr); // put as override property 184 return; 185 } 186 } 187 } 188 189 return JSObject::put(exec, propertyName, value, attr); 190 } 191 192 void JSCallbackObject::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr) 193 { 194 return put(exec, Identifier::from(propertyName), value, attr); 195 } 196 197 bool JSCallbackObject::deleteProperty(ExecState* exec, const Identifier& propertyName) 198 { 199 JSContextRef ctx = toRef(exec); 200 JSObjectRef thisRef = toRef(this); 201 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep()); 202 203 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) { 204 if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) { 205 JSLock::DropAllLocks dropAllLocks; 206 if (deleteProperty(ctx, thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) 207 return true; 208 } 209 210 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) { 211 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) { 212 if (entry->attributes & kJSPropertyAttributeDontDelete) 213 return false; 214 return true; 215 } 216 } 217 218 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) { 219 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) { 220 if (entry->attributes & kJSPropertyAttributeDontDelete) 221 return false; 222 return true; 223 } 224 } 225 } 226 227 return JSObject::deleteProperty(exec, propertyName); 228 } 229 230 bool JSCallbackObject::deleteProperty(ExecState* exec, unsigned propertyName) 231 { 232 return deleteProperty(exec, Identifier::from(propertyName)); 233 } 234 235 bool JSCallbackObject::implementsConstruct() const 236 { 237 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 238 if (jsClass->callAsConstructor) 239 return true; 240 241 return false; 242 } 243 244 JSObject* JSCallbackObject::construct(ExecState* exec, const List& args) 245 { 246 JSContextRef execRef = toRef(exec); 247 JSObjectRef thisRef = toRef(this); 248 249 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) { 250 if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) { 251 int argumentCount = static_cast<int>(args.size()); 252 Vector<JSValueRef, 16> arguments(argumentCount); 253 for (int i = 0; i < argumentCount; i++) 254 arguments[i] = toRef(args[i]); 255 JSLock::DropAllLocks dropAllLocks; 256 return toJS(callAsConstructor(execRef, thisRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot()))); 257 } 258 } 259 260 ASSERT(0); // implementsConstruct should prevent us from reaching here 261 return 0; 262 } 263 264 bool JSCallbackObject::implementsHasInstance() const 265 { 266 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 267 if (jsClass->hasInstance) 268 return true; 269 270 return false; 271 } 272 273 bool JSCallbackObject::hasInstance(ExecState *exec, JSValue *value) 274 { 275 JSContextRef execRef = toRef(exec); 276 JSObjectRef thisRef = toRef(this); 277 278 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 279 if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) { 280 JSLock::DropAllLocks dropAllLocks; 281 return hasInstance(execRef, thisRef, toRef(value), toRef(exec->exceptionSlot())); 282 } 283 284 ASSERT(0); // implementsHasInstance should prevent us from reaching here 285 return 0; 286 } 287 288 289 bool JSCallbackObject::implementsCall() const 290 { 291 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 292 if (jsClass->callAsFunction) 293 return true; 294 295 return false; 296 } 297 298 JSValue* JSCallbackObject::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args) 299 { 300 JSContextRef execRef = toRef(exec); 301 JSObjectRef thisRef = toRef(this); 302 JSObjectRef thisObjRef = toRef(thisObj); 303 304 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) { 305 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) { 306 int argumentCount = static_cast<int>(args.size()); 307 Vector<JSValueRef, 16> arguments(argumentCount); 308 for (int i = 0; i < argumentCount; i++) 309 arguments[i] = toRef(args[i]); 310 JSLock::DropAllLocks dropAllLocks; 311 return toJS(callAsFunction(execRef, thisRef, thisObjRef, argumentCount, arguments.data(), toRef(exec->exceptionSlot()))); 312 } 313 } 314 315 ASSERT(0); // implementsCall should prevent us from reaching here 316 return 0; 317 } 318 319 void JSCallbackObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) 320 { 321 JSContextRef execRef = toRef(exec); 322 JSObjectRef thisRef = toRef(this); 323 324 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) { 325 if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) { 326 JSLock::DropAllLocks dropAllLocks; 327 getPropertyNames(execRef, thisRef, toRef(&propertyNames)); 328 } 329 330 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) { 331 typedef OpaqueJSClass::StaticValuesTable::const_iterator iterator; 332 iterator end = staticValues->end(); 333 for (iterator it = staticValues->begin(); it != end; ++it) { 334 UString::Rep* name = it->first.get(); 335 StaticValueEntry* entry = it->second; 336 if (entry->getProperty && !(entry->attributes & kJSPropertyAttributeDontEnum)) 337 propertyNames.add(Identifier(name)); 338 } 339 } 340 341 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) { 342 typedef OpaqueJSClass::StaticFunctionsTable::const_iterator iterator; 343 iterator end = staticFunctions->end(); 344 for (iterator it = staticFunctions->begin(); it != end; ++it) { 345 UString::Rep* name = it->first.get(); 346 StaticFunctionEntry* entry = it->second; 347 if (!(entry->attributes & kJSPropertyAttributeDontEnum)) 348 propertyNames.add(Identifier(name)); 349 } 350 } 351 } 352 353 JSObject::getPropertyNames(exec, propertyNames); 354 } 355 356 double JSCallbackObject::toNumber(ExecState* exec) const 357 { 358 JSContextRef ctx = toRef(exec); 359 JSObjectRef thisRef = toRef(this); 360 361 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 362 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { 363 JSLock::DropAllLocks dropAllLocks; 364 if (JSValueRef value = convertToType(ctx, thisRef, kJSTypeNumber, toRef(exec->exceptionSlot()))) 365 return toJS(value)->getNumber(); 366 } 367 368 return JSObject::toNumber(exec); 369 } 370 371 UString JSCallbackObject::toString(ExecState* exec) const 372 { 373 JSContextRef ctx = toRef(exec); 374 JSObjectRef thisRef = toRef(this); 375 376 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 377 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { 378 JSLock::DropAllLocks dropAllLocks; 379 if (JSValueRef value = convertToType(ctx, thisRef, kJSTypeString, toRef(exec->exceptionSlot()))) 380 return toJS(value)->getString(); 381 } 382 383 return JSObject::toString(exec); 384 } 385 386 void JSCallbackObject::setPrivate(void* data) 387 { 388 m_privateData = data; 389 } 390 391 void* JSCallbackObject::getPrivate() 392 { 393 return m_privateData; 394 } 395 396 bool JSCallbackObject::inherits(JSClassRef c) const 397 { 398 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) 399 if (jsClass == c) 400 return true; 401 402 return false; 403 } 404 405 JSValue* JSCallbackObject::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) 406 { 407 JSValue* v = slot.slotBase(); 408 ASSERT(v); 409 return v; 410 } 411 412 JSValue* JSCallbackObject::staticValueGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) 413 { 414 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info)); 415 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase()); 416 417 JSObjectRef thisRef = toRef(thisObj); 418 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep()); 419 420 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) 421 if (OpaqueJSClass::StaticValuesTable* staticValues = jsClass->staticValues) 422 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) 423 if (JSObjectGetPropertyCallback getProperty = entry->getProperty) { 424 JSLock::DropAllLocks dropAllLocks; 425 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) 426 return toJS(value); 427 } 428 429 return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback."); 430 } 431 432 JSValue* JSCallbackObject::staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) 433 { 434 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info)); 435 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase()); 436 437 if (JSValue* cachedOrOverrideValue = thisObj->getDirect(propertyName)) 438 return cachedOrOverrideValue; 439 440 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) { 441 if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) { 442 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) { 443 if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) { 444 JSObject* o = new JSCallbackFunction(exec, callAsFunction, propertyName); 445 thisObj->putDirect(propertyName, o, entry->attributes); 446 return o; 447 } 448 } 449 } 450 } 451 452 return throwError(exec, ReferenceError, "Static function property defined with NULL callAsFunction callback."); 453 } 454 455 JSValue* JSCallbackObject::callbackGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) 456 { 457 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info)); 458 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase()); 459 460 JSObjectRef thisRef = toRef(thisObj); 461 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep()); 462 463 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) 464 if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { 465 JSLock::DropAllLocks dropAllLocks; 466 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) 467 return toJS(value); 468 } 469 470 return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist."); 471 } 32 // Define the two types of JSCallbackObjects we support. 33 template <> const ClassInfo JSCallbackObject<JSObject>::info = { "CallbackObject", 0, 0, 0 }; 34 template <> const ClassInfo JSCallbackObject<JSGlobalObject>::info = { "CallbackGlobalObject", 0, 0, 0 }; 472 35 473 36 } // namespace KJS
Note:
See TracChangeset
for help on using the changeset viewer.