JSCallbackObjectFunctions.h
1 /* 2 * Copyright (C) 2006-2020 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 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 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 #pragma once 28 29 #include "APICast.h" 30 #include "Error.h" 31 #include "ExceptionHelpers.h" 32 #include "JSCallbackFunction.h" 33 #include "JSClassRef.h" 34 #include "JSFunction.h" 35 #include "JSGlobalObject.h" 36 #include "JSLock.h" 37 #include "JSObjectRef.h" 38 #include "JSString.h" 39 #include "OpaqueJSString.h" 40 #include "PropertyNameArray.h" 41 #include <wtf/Vector.h> 42 43 namespace JSC { 44 45 template <class Parent> 46 inline JSCallbackObject<Parent>* JSCallbackObject<Parent>::asCallbackObject(JSValue value) 47 { 48 ASSERT(asObject(value)->inherits(value.getObject()->vm(), info())); 49 return jsCast<JSCallbackObject*>(asObject(value)); 50 } 51 52 template <class Parent> 53 inline JSCallbackObject<Parent>* JSCallbackObject<Parent>::asCallbackObject(EncodedJSValue encodedValue) 54 { 55 JSValue value = JSValue::decode(encodedValue); 56 ASSERT(asObject(value)->inherits(value.getObject()->vm(), info())); 57 return jsCast<JSCallbackObject*>(asObject(value)); 58 } 59 60 template <class Parent> 61 JSCallbackObject<Parent>::JSCallbackObject(JSGlobalObject* globalObject, Structure* structure, JSClassRef jsClass, void* data) 62 : Parent(getVM(globalObject), structure) 63 , m_callbackObjectData(makeUnique<JSCallbackObjectData>(data, jsClass)) 64 { 65 } 66 67 // Global object constructor. 68 // FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one. 69 template <class Parent> 70 JSCallbackObject<Parent>::JSCallbackObject(VM& vm, JSClassRef jsClass, Structure* structure) 71 : Parent(vm, structure) 72 , m_callbackObjectData(makeUnique<JSCallbackObjectData>(nullptr, jsClass)) 73 { 74 } 75 76 template <class Parent> 77 JSCallbackObject<Parent>::~JSCallbackObject() 78 { 79 VM& vm = this->HeapCell::vm(); 80 vm.currentlyDestructingCallbackObject = this; 81 ASSERT(m_classInfo); 82 vm.currentlyDestructingCallbackObjectClassInfo = m_classInfo; 83 JSObjectRef thisRef = toRef(static_cast<JSObject*>(this)); 84 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 85 if (JSObjectFinalizeCallback finalize = jsClass->finalize) 86 finalize(thisRef); 87 } 88 vm.currentlyDestructingCallbackObject = nullptr; 89 vm.currentlyDestructingCallbackObjectClassInfo = nullptr; 90 } 91 92 template <class Parent> 93 void JSCallbackObject<Parent>::finishCreation(JSGlobalObject* globalObject) 94 { 95 VM& vm = getVM(globalObject); 96 Base::finishCreation(vm); 97 ASSERT(Parent::inherits(vm, info())); 98 init(globalObject); 99 } 100 101 // This is just for Global object, so we can assume that Base::finishCreation is JSGlobalObject::finishCreation. 102 template <class Parent> 103 void JSCallbackObject<Parent>::finishCreation(VM& vm) 104 { 105 ASSERT(Parent::inherits(vm, info())); 106 ASSERT(Parent::isGlobalObject()); 107 Base::finishCreation(vm); 108 init(jsCast<JSGlobalObject*>(this)); 109 } 110 111 template <class Parent> 112 void JSCallbackObject<Parent>::init(JSGlobalObject* globalObject) 113 { 114 ASSERT(globalObject); 115 116 Vector<JSObjectInitializeCallback, 16> initRoutines; 117 JSClassRef jsClass = classRef(); 118 do { 119 if (JSObjectInitializeCallback initialize = jsClass->initialize) 120 initRoutines.append(initialize); 121 } while ((jsClass = jsClass->parentClass)); 122 123 // initialize from base to derived 124 for (int i = static_cast<int>(initRoutines.size()) - 1; i >= 0; i--) { 125 JSLock::DropAllLocks dropAllLocks(globalObject); 126 JSObjectInitializeCallback initialize = initRoutines[i]; 127 initialize(toRef(globalObject), toRef(jsCast<JSObject*>(this))); 128 } 129 130 m_classInfo = this->classInfo(getVM(globalObject)); 131 } 132 133 template <class Parent> 134 String JSCallbackObject<Parent>::className(const JSObject* object, VM& vm) 135 { 136 const JSCallbackObject* thisObject = jsCast<const JSCallbackObject*>(object); 137 String thisClassName = thisObject->classRef()->className(); 138 if (!thisClassName.isEmpty()) 139 return thisClassName; 140 141 return Parent::className(object, vm); 142 } 143 144 template <class Parent> 145 String JSCallbackObject<Parent>::toStringName(const JSObject* object, JSGlobalObject* globalObject) 146 { 147 VM& vm = getVM(globalObject); 148 const ClassInfo* info = object->classInfo(vm); 149 ASSERT(info); 150 return info->methodTable.className(object, vm); 151 } 152 153 template <class Parent> 154 bool JSCallbackObject<Parent>::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) 155 { 156 VM& vm = getVM(globalObject); 157 auto scope = DECLARE_THROW_SCOPE(vm); 158 159 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object); 160 JSContextRef ctx = toRef(globalObject); 161 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject)); 162 RefPtr<OpaqueJSString> propertyNameRef; 163 164 if (StringImpl* name = propertyName.uid()) { 165 unsigned attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum; 166 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 167 // optional optimization to bypass getProperty in cases when we only need to know if the property exists 168 if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) { 169 if (!propertyNameRef) 170 propertyNameRef = OpaqueJSString::tryCreate(name); 171 JSLock::DropAllLocks dropAllLocks(globalObject); 172 if (hasProperty(ctx, thisRef, propertyNameRef.get())) { 173 slot.setCustom(thisObject, attributes, getCallbackGetter()); 174 return true; 175 } 176 } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { 177 if (!propertyNameRef) 178 propertyNameRef = OpaqueJSString::tryCreate(name); 179 JSValueRef exception = nullptr; 180 JSValueRef value; 181 { 182 JSLock::DropAllLocks dropAllLocks(globalObject); 183 value = getProperty(ctx, thisRef, propertyNameRef.get(), &exception); 184 } 185 if (exception) { 186 throwException(globalObject, scope, toJS(globalObject, exception)); 187 slot.setValue(thisObject, attributes, jsUndefined()); 188 return true; 189 } 190 if (value) { 191 slot.setValue(thisObject, attributes, toJS(globalObject, value)); 192 return true; 193 } 194 } 195 196 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { 197 if (staticValues->contains(name)) { 198 JSValue value = thisObject->getStaticValue(globalObject, propertyName); 199 RETURN_IF_EXCEPTION(scope, false); 200 if (value) { 201 slot.setValue(thisObject, attributes, value); 202 return true; 203 } 204 } 205 } 206 207 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { 208 if (staticFunctions->contains(name)) { 209 slot.setCustom(thisObject, attributes, getStaticFunctionGetter()); 210 return true; 211 } 212 } 213 } 214 } 215 216 RELEASE_AND_RETURN(scope, Parent::getOwnPropertySlot(thisObject, globalObject, propertyName, slot)); 217 } 218 219 template <class Parent> 220 bool JSCallbackObject<Parent>::getOwnPropertySlotByIndex(JSObject* object, JSGlobalObject* globalObject, unsigned propertyName, PropertySlot& slot) 221 { 222 VM& vm = getVM(globalObject); 223 return object->methodTable(vm)->getOwnPropertySlot(object, globalObject, Identifier::from(vm, propertyName), slot); 224 } 225 226 template <class Parent> 227 JSValue JSCallbackObject<Parent>::defaultValue(const JSObject* object, JSGlobalObject* globalObject, PreferredPrimitiveType hint) 228 { 229 VM& vm = getVM(globalObject); 230 auto scope = DECLARE_THROW_SCOPE(vm); 231 232 const JSCallbackObject* thisObject = jsCast<const JSCallbackObject*>(object); 233 JSContextRef ctx = toRef(globalObject); 234 JSObjectRef thisRef = toRef(jsCast<const JSObject*>(thisObject)); 235 ::JSType jsHint = hint == PreferString ? kJSTypeString : kJSTypeNumber; 236 237 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 238 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { 239 JSValueRef exception = nullptr; 240 JSValueRef result = convertToType(ctx, thisRef, jsHint, &exception); 241 if (exception) { 242 throwException(globalObject, scope, toJS(globalObject, exception)); 243 return jsUndefined(); 244 } 245 if (result) 246 return toJS(globalObject, result); 247 } 248 } 249 250 RELEASE_AND_RETURN(scope, Parent::defaultValue(object, globalObject, hint)); 251 } 252 253 template <class Parent> 254 bool JSCallbackObject<Parent>::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) 255 { 256 VM& vm = getVM(globalObject); 257 auto scope = DECLARE_THROW_SCOPE(vm); 258 259 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell); 260 JSContextRef ctx = toRef(globalObject); 261 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject)); 262 RefPtr<OpaqueJSString> propertyNameRef; 263 JSValueRef valueRef = toRef(globalObject, value); 264 265 if (StringImpl* name = propertyName.uid()) { 266 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 267 if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { 268 if (!propertyNameRef) 269 propertyNameRef = OpaqueJSString::tryCreate(name); 270 JSValueRef exception = nullptr; 271 bool result; 272 { 273 JSLock::DropAllLocks dropAllLocks(globalObject); 274 result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); 275 } 276 if (exception) 277 throwException(globalObject, scope, toJS(globalObject, exception)); 278 if (result || exception) 279 return result; 280 } 281 282 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { 283 if (StaticValueEntry* entry = staticValues->get(name)) { 284 if (entry->attributes & kJSPropertyAttributeReadOnly) 285 return false; 286 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { 287 JSValueRef exception = nullptr; 288 bool result; 289 { 290 JSLock::DropAllLocks dropAllLocks(globalObject); 291 result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception); 292 } 293 if (exception) 294 throwException(globalObject, scope, toJS(globalObject, exception)); 295 if (result || exception) 296 return result; 297 } 298 } 299 } 300 301 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { 302 if (StaticFunctionEntry* entry = staticFunctions->get(name)) { 303 PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry, &vm); 304 bool found = Parent::getOwnPropertySlot(thisObject, globalObject, propertyName, getSlot); 305 RETURN_IF_EXCEPTION(scope, false); 306 getSlot.disallowVMEntry.reset(); 307 if (found) 308 RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot)); 309 if (entry->attributes & kJSPropertyAttributeReadOnly) 310 return false; 311 return thisObject->JSCallbackObject<Parent>::putDirect(vm, propertyName, value); // put as override property 312 } 313 } 314 } 315 } 316 317 RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot)); 318 } 319 320 template <class Parent> 321 bool JSCallbackObject<Parent>::putByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyIndex, JSValue value, bool shouldThrow) 322 { 323 VM& vm = getVM(globalObject); 324 auto scope = DECLARE_THROW_SCOPE(vm); 325 326 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell); 327 JSContextRef ctx = toRef(globalObject); 328 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject)); 329 RefPtr<OpaqueJSString> propertyNameRef; 330 JSValueRef valueRef = toRef(globalObject, value); 331 Identifier propertyName = Identifier::from(vm, propertyIndex); 332 333 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 334 if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { 335 if (!propertyNameRef) 336 propertyNameRef = OpaqueJSString::tryCreate(propertyName.impl()); 337 JSValueRef exception = nullptr; 338 bool result; 339 { 340 JSLock::DropAllLocks dropAllLocks(globalObject); 341 result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); 342 } 343 if (exception) 344 throwException(globalObject, scope, toJS(globalObject, exception)); 345 if (result || exception) 346 return result; 347 } 348 349 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { 350 if (StaticValueEntry* entry = staticValues->get(propertyName.impl())) { 351 if (entry->attributes & kJSPropertyAttributeReadOnly) 352 return false; 353 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { 354 JSValueRef exception = nullptr; 355 bool result; 356 { 357 JSLock::DropAllLocks dropAllLocks(globalObject); 358 result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception); 359 } 360 if (exception) 361 throwException(globalObject, scope, toJS(globalObject, exception)); 362 if (result || exception) 363 return result; 364 } 365 } 366 } 367 368 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { 369 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.impl())) { 370 if (entry->attributes & kJSPropertyAttributeReadOnly) 371 return false; 372 break; 373 } 374 } 375 } 376 377 RELEASE_AND_RETURN(scope, Parent::putByIndex(thisObject, globalObject, propertyIndex, value, shouldThrow)); 378 } 379 380 template <class Parent> 381 bool JSCallbackObject<Parent>::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, DeletePropertySlot& slot) 382 { 383 VM& vm = getVM(globalObject); 384 auto scope = DECLARE_THROW_SCOPE(vm); 385 386 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell); 387 JSContextRef ctx = toRef(globalObject); 388 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject)); 389 RefPtr<OpaqueJSString> propertyNameRef; 390 391 if (StringImpl* name = propertyName.uid()) { 392 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 393 if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) { 394 if (!propertyNameRef) 395 propertyNameRef = OpaqueJSString::tryCreate(name); 396 JSValueRef exception = nullptr; 397 bool result; 398 { 399 JSLock::DropAllLocks dropAllLocks(globalObject); 400 result = deleteProperty(ctx, thisRef, propertyNameRef.get(), &exception); 401 } 402 if (exception) 403 throwException(globalObject, scope, toJS(globalObject, exception)); 404 if (result || exception) 405 return true; 406 } 407 408 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { 409 if (StaticValueEntry* entry = staticValues->get(name)) { 410 if (entry->attributes & kJSPropertyAttributeDontDelete) 411 return false; 412 return true; 413 } 414 } 415 416 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { 417 if (StaticFunctionEntry* entry = staticFunctions->get(name)) { 418 if (entry->attributes & kJSPropertyAttributeDontDelete) 419 return false; 420 return true; 421 } 422 } 423 } 424 } 425 426 static_assert(std::is_final_v<JSCallbackObject<Parent>>, "Ensure no derived classes have custom deletePropertyByIndex implementation"); 427 if (Optional<uint32_t> index = parseIndex(propertyName)) 428 RELEASE_AND_RETURN(scope, Parent::deletePropertyByIndex(thisObject, globalObject, index.value())); 429 RELEASE_AND_RETURN(scope, Parent::deleteProperty(thisObject, globalObject, propertyName, slot)); 430 } 431 432 template <class Parent> 433 bool JSCallbackObject<Parent>::deletePropertyByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyName) 434 { 435 VM& vm = getVM(globalObject); 436 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell); 437 return JSCell::deleteProperty(thisObject, globalObject, Identifier::from(vm, propertyName)); 438 } 439 440 template <class Parent> 441 CallData JSCallbackObject<Parent>::getConstructData(JSCell* cell) 442 { 443 CallData constructData; 444 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell); 445 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 446 if (jsClass->callAsConstructor) { 447 constructData.type = CallData::Type::Native; 448 constructData.native.function = getConstructFunction(); 449 break; 450 } 451 } 452 return constructData; 453 } 454 455 template <class Parent> 456 EncodedJSValue JSCallbackObject<Parent>::constructImpl(JSGlobalObject* globalObject, CallFrame* callFrame) 457 { 458 VM& vm = getVM(globalObject); 459 auto scope = DECLARE_THROW_SCOPE(vm); 460 461 JSObject* constructor = callFrame->jsCallee(); 462 JSContextRef execRef = toRef(globalObject); 463 JSObjectRef constructorRef = toRef(constructor); 464 465 for (JSClassRef jsClass = jsCast<JSCallbackObject<Parent>*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) { 466 if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) { 467 size_t argumentCount = callFrame->argumentCount(); 468 Vector<JSValueRef, 16> arguments; 469 arguments.reserveInitialCapacity(argumentCount); 470 for (size_t i = 0; i < argumentCount; ++i) 471 arguments.uncheckedAppend(toRef(globalObject, callFrame->uncheckedArgument(i))); 472 JSValueRef exception = nullptr; 473 JSObject* result; 474 { 475 JSLock::DropAllLocks dropAllLocks(globalObject); 476 result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), &exception)); 477 } 478 if (exception) { 479 throwException(globalObject, scope, toJS(globalObject, exception)); 480 return JSValue::encode(jsUndefined()); 481 } 482 return JSValue::encode(result); 483 } 484 } 485 486 RELEASE_ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here 487 return JSValue::encode(JSValue()); 488 } 489 490 template <class Parent> 491 bool JSCallbackObject<Parent>::customHasInstance(JSObject* object, JSGlobalObject* globalObject, JSValue value) 492 { 493 VM& vm = getVM(globalObject); 494 auto scope = DECLARE_THROW_SCOPE(vm); 495 496 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object); 497 JSContextRef execRef = toRef(globalObject); 498 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject)); 499 500 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 501 if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) { 502 JSValueRef valueRef = toRef(globalObject, value); 503 JSValueRef exception = nullptr; 504 bool result; 505 { 506 JSLock::DropAllLocks dropAllLocks(globalObject); 507 result = hasInstance(execRef, thisRef, valueRef, &exception); 508 } 509 if (exception) 510 throwException(globalObject, scope, toJS(globalObject, exception)); 511 return result; 512 } 513 } 514 return false; 515 } 516 517 template <class Parent> 518 CallData JSCallbackObject<Parent>::getCallData(JSCell* cell) 519 { 520 CallData callData; 521 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell); 522 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 523 if (jsClass->callAsFunction) { 524 callData.type = CallData::Type::Native; 525 callData.native.function = getCallFunction(); 526 break; 527 } 528 } 529 return callData; 530 } 531 532 template <class Parent> 533 EncodedJSValue JSCallbackObject<Parent>::callImpl(JSGlobalObject* globalObject, CallFrame* callFrame) 534 { 535 VM& vm = getVM(globalObject); 536 auto scope = DECLARE_THROW_SCOPE(vm); 537 538 JSContextRef execRef = toRef(globalObject); 539 JSObjectRef functionRef = toRef(callFrame->jsCallee()); 540 JSObjectRef thisObjRef = toRef(jsCast<JSObject*>(callFrame->thisValue().toThis(globalObject, ECMAMode::sloppy()))); 541 542 for (JSClassRef jsClass = jsCast<JSCallbackObject<Parent>*>(toJS(functionRef))->classRef(); jsClass; jsClass = jsClass->parentClass) { 543 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) { 544 size_t argumentCount = callFrame->argumentCount(); 545 Vector<JSValueRef, 16> arguments; 546 arguments.reserveInitialCapacity(argumentCount); 547 for (size_t i = 0; i < argumentCount; ++i) 548 arguments.uncheckedAppend(toRef(globalObject, callFrame->uncheckedArgument(i))); 549 JSValueRef exception = nullptr; 550 JSValue result; 551 { 552 JSLock::DropAllLocks dropAllLocks(globalObject); 553 result = toJS(globalObject, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception)); 554 } 555 if (exception) { 556 throwException(globalObject, scope, toJS(globalObject, exception)); 557 return JSValue::encode(jsUndefined()); 558 } 559 return JSValue::encode(result); 560 } 561 } 562 563 RELEASE_ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here 564 return JSValue::encode(JSValue()); 565 } 566 567 template <class Parent> 568 void JSCallbackObject<Parent>::getOwnSpecialPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, DontEnumPropertiesMode mode) 569 { 570 VM& vm = getVM(globalObject); 571 JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object); 572 JSContextRef execRef = toRef(globalObject); 573 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject)); 574 575 for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { 576 if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) { 577 JSLock::DropAllLocks dropAllLocks(globalObject); 578 getPropertyNames(execRef, thisRef, toRef(&propertyNames)); 579 } 580 581 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { 582 typedef OpaqueJSClassStaticValuesTable::const_iterator iterator; 583 iterator end = staticValues->end(); 584 for (iterator it = staticValues->begin(); it != end; ++it) { 585 StringImpl* name = it->key.get(); 586 StaticValueEntry* entry = it->value.get(); 587 if (entry->getProperty && (mode == DontEnumPropertiesMode::Include || !(entry->attributes & kJSPropertyAttributeDontEnum))) { 588 ASSERT(!name->isSymbol()); 589 propertyNames.add(Identifier::fromString(vm, String(name))); 590 } 591 } 592 } 593 594 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { 595 typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator; 596 iterator end = staticFunctions->end(); 597 for (iterator it = staticFunctions->begin(); it != end; ++it) { 598 StringImpl* name = it->key.get(); 599 StaticFunctionEntry* entry = it->value.get(); 600 if (mode == DontEnumPropertiesMode::Include || !(entry->attributes & kJSPropertyAttributeDontEnum)) { 601 ASSERT(!name->isSymbol()); 602 propertyNames.add(Identifier::fromString(vm, String(name))); 603 } 604 } 605 } 606 } 607 } 608 609 template <class Parent> 610 void JSCallbackObject<Parent>::setPrivate(void* data) 611 { 612 m_callbackObjectData->privateData = data; 613 } 614 615 template <class Parent> 616 void* JSCallbackObject<Parent>::getPrivate() 617 { 618 return m_callbackObjectData->privateData; 619 } 620 621 template <class Parent> 622 bool JSCallbackObject<Parent>::inherits(JSClassRef c) const 623 { 624 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 625 if (jsClass == c) 626 return true; 627 } 628 return false; 629 } 630 631 template <class Parent> 632 JSValue JSCallbackObject<Parent>::getStaticValue(JSGlobalObject* globalObject, PropertyName propertyName) 633 { 634 VM& vm = getVM(globalObject); 635 auto scope = DECLARE_THROW_SCOPE(vm); 636 637 JSObjectRef thisRef = toRef(jsCast<JSObject*>(this)); 638 639 if (StringImpl* name = propertyName.uid()) { 640 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 641 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { 642 if (StaticValueEntry* entry = staticValues->get(name)) { 643 if (JSObjectGetPropertyCallback getProperty = entry->getProperty) { 644 JSValueRef exception = nullptr; 645 JSValueRef value; 646 { 647 JSLock::DropAllLocks dropAllLocks(globalObject); 648 value = getProperty(toRef(globalObject), thisRef, entry->propertyNameRef.get(), &exception); 649 } 650 if (exception) { 651 throwException(globalObject, scope, toJS(globalObject, exception)); 652 return jsUndefined(); 653 } 654 if (value) 655 return toJS(globalObject, value); 656 } 657 } 658 } 659 } 660 } 661 662 return JSValue(); 663 } 664 665 template <class Parent> 666 EncodedJSValue JSCallbackObject<Parent>::staticFunctionGetterImpl(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName propertyName) 667 { 668 VM& vm = getVM(globalObject); 669 auto scope = DECLARE_THROW_SCOPE(vm); 670 671 JSCallbackObject* thisObj = asCallbackObject(thisValue); 672 673 // Check for cached or override property. 674 PropertySlot slot2(thisObj, PropertySlot::InternalMethodType::VMInquiry, &vm); 675 bool found = Parent::getOwnPropertySlot(thisObj, globalObject, propertyName, slot2); 676 RETURN_IF_EXCEPTION(scope, { }); 677 slot2.disallowVMEntry.reset(); 678 if (found) 679 return JSValue::encode(slot2.getValue(globalObject, propertyName)); 680 681 if (StringImpl* name = propertyName.uid()) { 682 for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) { 683 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { 684 if (StaticFunctionEntry* entry = staticFunctions->get(name)) { 685 if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) { 686 JSObject* o = JSCallbackFunction::create(vm, thisObj->globalObject(vm), callAsFunction, name); 687 thisObj->putDirect(vm, propertyName, o, entry->attributes); 688 return JSValue::encode(o); 689 } 690 } 691 } 692 } 693 } 694 695 return JSValue::encode(throwException(globalObject, scope, createReferenceError(globalObject, "Static function property defined with NULL callAsFunction callback."_s))); 696 } 697 698 template <class Parent> 699 EncodedJSValue JSCallbackObject<Parent>::callbackGetterImpl(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName propertyName) 700 { 701 VM& vm = getVM(globalObject); 702 auto scope = DECLARE_THROW_SCOPE(vm); 703 704 JSCallbackObject* thisObj = asCallbackObject(thisValue); 705 706 JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObj)); 707 RefPtr<OpaqueJSString> propertyNameRef; 708 709 if (StringImpl* name = propertyName.uid()) { 710 for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) { 711 if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { 712 if (!propertyNameRef) 713 propertyNameRef = OpaqueJSString::tryCreate(name); 714 JSValueRef exception = nullptr; 715 JSValueRef value; 716 { 717 JSLock::DropAllLocks dropAllLocks(globalObject); 718 value = getProperty(toRef(globalObject), thisRef, propertyNameRef.get(), &exception); 719 } 720 if (exception) { 721 throwException(globalObject, scope, toJS(globalObject, exception)); 722 return JSValue::encode(jsUndefined()); 723 } 724 if (value) 725 return JSValue::encode(toJS(globalObject, value)); 726 } 727 } 728 } 729 730 return JSValue::encode(throwException(globalObject, scope, createReferenceError(globalObject, "hasProperty callback returned true for a property that doesn't exist."_s))); 731 } 732 733 } // namespace JSC