1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#import "JavaScriptCore.h" 28 29#if JSC_OBJC_API_ENABLED 30 31#import "APICallbackFunction.h" 32#import "APICast.h" 33#import "DelayedReleaseScope.h" 34#import "Error.h" 35#import "JSCJSValueInlines.h" 36#import "JSCell.h" 37#import "JSCellInlines.h" 38#import "JSContextInternal.h" 39#import "JSWrapperMap.h" 40#import "JSValueInternal.h" 41#import "ObjCCallbackFunction.h" 42#import "ObjcRuntimeExtras.h" 43#import <objc/runtime.h> 44#import <wtf/RetainPtr.h> 45 46class CallbackArgument { 47public: 48 virtual ~CallbackArgument(); 49 virtual void set(NSInvocation *, NSInteger, JSContext *, JSValueRef, JSValueRef*) = 0; 50 51 OwnPtr<CallbackArgument> m_next; 52}; 53 54CallbackArgument::~CallbackArgument() 55{ 56} 57 58class CallbackArgumentBoolean : public CallbackArgument { 59 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override 60 { 61 bool value = JSValueToBoolean([context JSGlobalContextRef], argument); 62 [invocation setArgument:&value atIndex:argumentNumber]; 63 } 64}; 65 66template<typename T> 67class CallbackArgumentInteger : public CallbackArgument { 68 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 69 { 70 T value = (T)JSC::toInt32(JSValueToNumber([context JSGlobalContextRef], argument, exception)); 71 [invocation setArgument:&value atIndex:argumentNumber]; 72 } 73}; 74 75template<typename T> 76class CallbackArgumentDouble : public CallbackArgument { 77 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 78 { 79 T value = (T)JSValueToNumber([context JSGlobalContextRef], argument, exception); 80 [invocation setArgument:&value atIndex:argumentNumber]; 81 } 82}; 83 84class CallbackArgumentJSValue : public CallbackArgument { 85 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override 86 { 87 JSValue *value = [JSValue valueWithJSValueRef:argument inContext:context]; 88 [invocation setArgument:&value atIndex:argumentNumber]; 89 } 90}; 91 92class CallbackArgumentId : public CallbackArgument { 93 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override 94 { 95 id value = valueToObject(context, argument); 96 [invocation setArgument:&value atIndex:argumentNumber]; 97 } 98}; 99 100class CallbackArgumentOfClass : public CallbackArgument { 101public: 102 CallbackArgumentOfClass(Class cls) 103 : CallbackArgument() 104 , m_class(cls) 105 { 106 [m_class retain]; 107 } 108 109private: 110 virtual ~CallbackArgumentOfClass() 111 { 112 [m_class release]; 113 } 114 115 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 116 { 117 JSGlobalContextRef contextRef = [context JSGlobalContextRef]; 118 119 id object = tryUnwrapObjcObject(contextRef, argument); 120 if (object && [object isKindOfClass:m_class]) { 121 [invocation setArgument:&object atIndex:argumentNumber]; 122 return; 123 } 124 125 if (JSValueIsNull(contextRef, argument) || JSValueIsUndefined(contextRef, argument)) { 126 object = nil; 127 [invocation setArgument:&object atIndex:argumentNumber]; 128 return; 129 } 130 131 *exception = toRef(JSC::createTypeError(toJS(contextRef), ASCIILiteral("Argument does not match Objective-C Class"))); 132 } 133 134 Class m_class; 135}; 136 137class CallbackArgumentNSNumber : public CallbackArgument { 138 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 139 { 140 id value = valueToNumber([context JSGlobalContextRef], argument, exception); 141 [invocation setArgument:&value atIndex:argumentNumber]; 142 } 143}; 144 145class CallbackArgumentNSString : public CallbackArgument { 146 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 147 { 148 id value = valueToString([context JSGlobalContextRef], argument, exception); 149 [invocation setArgument:&value atIndex:argumentNumber]; 150 } 151}; 152 153class CallbackArgumentNSDate : public CallbackArgument { 154 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 155 { 156 id value = valueToDate([context JSGlobalContextRef], argument, exception); 157 [invocation setArgument:&value atIndex:argumentNumber]; 158 } 159}; 160 161class CallbackArgumentNSArray : public CallbackArgument { 162 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 163 { 164 id value = valueToArray([context JSGlobalContextRef], argument, exception); 165 [invocation setArgument:&value atIndex:argumentNumber]; 166 } 167}; 168 169class CallbackArgumentNSDictionary : public CallbackArgument { 170 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override 171 { 172 id value = valueToDictionary([context JSGlobalContextRef], argument, exception); 173 [invocation setArgument:&value atIndex:argumentNumber]; 174 } 175}; 176 177class CallbackArgumentStruct : public CallbackArgument { 178public: 179 CallbackArgumentStruct(NSInvocation *conversionInvocation, const char* encodedType) 180 : m_conversionInvocation(conversionInvocation) 181 , m_buffer(encodedType) 182 { 183 } 184 185private: 186 virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override 187 { 188 JSValue *value = [JSValue valueWithJSValueRef:argument inContext:context]; 189 [m_conversionInvocation invokeWithTarget:value]; 190 [m_conversionInvocation getReturnValue:m_buffer]; 191 [invocation setArgument:m_buffer atIndex:argumentNumber]; 192 } 193 194 RetainPtr<NSInvocation> m_conversionInvocation; 195 StructBuffer m_buffer; 196}; 197 198class ArgumentTypeDelegate { 199public: 200 typedef CallbackArgument* ResultType; 201 202 template<typename T> 203 static ResultType typeInteger() 204 { 205 return new CallbackArgumentInteger<T>; 206 } 207 208 template<typename T> 209 static ResultType typeDouble() 210 { 211 return new CallbackArgumentDouble<T>; 212 } 213 214 static ResultType typeBool() 215 { 216 return new CallbackArgumentBoolean; 217 } 218 219 static ResultType typeVoid() 220 { 221 RELEASE_ASSERT_NOT_REACHED(); 222 return 0; 223 } 224 225 static ResultType typeId() 226 { 227 return new CallbackArgumentId; 228 } 229 230 static ResultType typeOfClass(const char* begin, const char* end) 231 { 232 StringRange copy(begin, end); 233 Class cls = objc_getClass(copy); 234 if (!cls) 235 return 0; 236 237 if (cls == [JSValue class]) 238 return new CallbackArgumentJSValue; 239 if (cls == [NSString class]) 240 return new CallbackArgumentNSString; 241 if (cls == [NSNumber class]) 242 return new CallbackArgumentNSNumber; 243 if (cls == [NSDate class]) 244 return new CallbackArgumentNSDate; 245 if (cls == [NSArray class]) 246 return new CallbackArgumentNSArray; 247 if (cls == [NSDictionary class]) 248 return new CallbackArgumentNSDictionary; 249 250 return new CallbackArgumentOfClass(cls); 251 } 252 253 static ResultType typeBlock(const char*, const char*) 254 { 255 return nil; 256 } 257 258 static ResultType typeStruct(const char* begin, const char* end) 259 { 260 StringRange copy(begin, end); 261 if (NSInvocation *invocation = valueToTypeInvocationFor(copy)) 262 return new CallbackArgumentStruct(invocation, copy); 263 return 0; 264 } 265}; 266 267class CallbackResult { 268public: 269 virtual ~CallbackResult() 270 { 271 } 272 273 virtual JSValueRef get(NSInvocation *, JSContext *, JSValueRef*) = 0; 274}; 275 276class CallbackResultVoid : public CallbackResult { 277 virtual JSValueRef get(NSInvocation *, JSContext *context, JSValueRef*) override 278 { 279 return JSValueMakeUndefined([context JSGlobalContextRef]); 280 } 281}; 282 283class CallbackResultId : public CallbackResult { 284 virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override 285 { 286 id value; 287 [invocation getReturnValue:&value]; 288 return objectToValue(context, value); 289 } 290}; 291 292template<typename T> 293class CallbackResultNumeric : public CallbackResult { 294 virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override 295 { 296 T value; 297 [invocation getReturnValue:&value]; 298 return JSValueMakeNumber([context JSGlobalContextRef], value); 299 } 300}; 301 302class CallbackResultBoolean : public CallbackResult { 303 virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override 304 { 305 bool value; 306 [invocation getReturnValue:&value]; 307 return JSValueMakeBoolean([context JSGlobalContextRef], value); 308 } 309}; 310 311class CallbackResultStruct : public CallbackResult { 312public: 313 CallbackResultStruct(NSInvocation *conversionInvocation, const char* encodedType) 314 : m_conversionInvocation(conversionInvocation) 315 , m_buffer(encodedType) 316 { 317 } 318 319private: 320 virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override 321 { 322 [invocation getReturnValue:m_buffer]; 323 324 [m_conversionInvocation setArgument:m_buffer atIndex:2]; 325 [m_conversionInvocation setArgument:&context atIndex:3]; 326 [m_conversionInvocation invokeWithTarget:[JSValue class]]; 327 328 JSValue *value; 329 [m_conversionInvocation getReturnValue:&value]; 330 return valueInternalValue(value); 331 } 332 333 RetainPtr<NSInvocation> m_conversionInvocation; 334 StructBuffer m_buffer; 335}; 336 337class ResultTypeDelegate { 338public: 339 typedef CallbackResult* ResultType; 340 341 template<typename T> 342 static ResultType typeInteger() 343 { 344 return new CallbackResultNumeric<T>; 345 } 346 347 template<typename T> 348 static ResultType typeDouble() 349 { 350 return new CallbackResultNumeric<T>; 351 } 352 353 static ResultType typeBool() 354 { 355 return new CallbackResultBoolean; 356 } 357 358 static ResultType typeVoid() 359 { 360 return new CallbackResultVoid; 361 } 362 363 static ResultType typeId() 364 { 365 return new CallbackResultId(); 366 } 367 368 static ResultType typeOfClass(const char*, const char*) 369 { 370 return new CallbackResultId(); 371 } 372 373 static ResultType typeBlock(const char*, const char*) 374 { 375 return new CallbackResultId(); 376 } 377 378 static ResultType typeStruct(const char* begin, const char* end) 379 { 380 StringRange copy(begin, end); 381 if (NSInvocation *invocation = typeToValueInvocationFor(copy)) 382 return new CallbackResultStruct(invocation, copy); 383 return 0; 384 } 385}; 386 387enum CallbackType { 388 CallbackInitMethod, 389 CallbackInstanceMethod, 390 CallbackClassMethod, 391 CallbackBlock 392}; 393 394namespace JSC { 395 396class ObjCCallbackFunctionImpl { 397public: 398 ObjCCallbackFunctionImpl(NSInvocation *invocation, CallbackType type, Class instanceClass, PassOwnPtr<CallbackArgument> arguments, PassOwnPtr<CallbackResult> result) 399 : m_type(type) 400 , m_instanceClass([instanceClass retain]) 401 , m_invocation(invocation) 402 , m_arguments(arguments) 403 , m_result(result) 404 { 405 ASSERT((type != CallbackInstanceMethod && type != CallbackInitMethod) || instanceClass); 406 } 407 408 void destroy(Heap& heap) 409 { 410 // We need to explicitly release the target since we didn't call 411 // -retainArguments on m_invocation (and we don't want to do so). 412 if (m_type == CallbackBlock || m_type == CallbackClassMethod) 413 heap.releaseSoon(adoptNS([m_invocation.get() target])); 414 [m_instanceClass release]; 415 } 416 417 JSValueRef call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); 418 419 id wrappedBlock() 420 { 421 return m_type == CallbackBlock ? [m_invocation target] : nil; 422 } 423 424 id wrappedConstructor() 425 { 426 switch (m_type) { 427 case CallbackBlock: 428 return [m_invocation target]; 429 case CallbackInitMethod: 430 return m_instanceClass; 431 default: 432 return nil; 433 } 434 } 435 436 bool isConstructible() 437 { 438 return !!wrappedBlock() || m_type == CallbackInitMethod; 439 } 440 441 String name(); 442 443private: 444 CallbackType m_type; 445 Class m_instanceClass; 446 RetainPtr<NSInvocation> m_invocation; 447 OwnPtr<CallbackArgument> m_arguments; 448 OwnPtr<CallbackResult> m_result; 449}; 450 451static JSValueRef objCCallbackFunctionCallAsFunction(JSContextRef callerContext, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 452{ 453 // Retake the API lock - we need this for a few reasons: 454 // (1) We don't want to support the C-API's confusing drops-locks-once policy - should only drop locks if we can do so recursively. 455 // (2) We're calling some JSC internals that require us to be on the 'inside' - e.g. createTypeError. 456 // (3) We need to be locked (per context would be fine) against conflicting usage of the ObjCCallbackFunction's NSInvocation. 457 JSC::JSLockHolder locker(toJS(callerContext)); 458 459 ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(toJS(function)); 460 ObjCCallbackFunctionImpl* impl = callback->impl(); 461 JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(callback->globalObject()->globalExec())]; 462 463 CallbackData callbackData; 464 JSValueRef result; 465 @autoreleasepool { 466 [context beginCallbackWithData:&callbackData calleeValue:function thisValue:thisObject argumentCount:argumentCount arguments:arguments]; 467 result = impl->call(context, thisObject, argumentCount, arguments, exception); 468 if (context.exception) 469 *exception = valueInternalValue(context.exception); 470 [context endCallbackWithData:&callbackData]; 471 } 472 return result; 473} 474 475static JSObjectRef objCCallbackFunctionCallAsConstructor(JSContextRef callerContext, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 476{ 477 JSC::JSLockHolder locker(toJS(callerContext)); 478 479 ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(toJS(constructor)); 480 ObjCCallbackFunctionImpl* impl = callback->impl(); 481 JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(toJS(callerContext)->lexicalGlobalObject()->globalExec())]; 482 483 CallbackData callbackData; 484 JSValueRef result; 485 @autoreleasepool { 486 [context beginCallbackWithData:&callbackData calleeValue:constructor thisValue:nil argumentCount:argumentCount arguments:arguments]; 487 result = impl->call(context, NULL, argumentCount, arguments, exception); 488 if (context.exception) 489 *exception = valueInternalValue(context.exception); 490 [context endCallbackWithData:&callbackData]; 491 } 492 493 JSGlobalContextRef contextRef = [context JSGlobalContextRef]; 494 if (*exception) 495 return 0; 496 497 if (!JSValueIsObject(contextRef, result)) { 498 *exception = toRef(JSC::createTypeError(toJS(contextRef), ASCIILiteral("Objective-C blocks called as constructors must return an object."))); 499 return 0; 500 } 501 return (JSObjectRef)result; 502} 503 504const JSC::ClassInfo ObjCCallbackFunction::s_info = { "CallbackFunction", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCCallbackFunction) }; 505 506ObjCCallbackFunction::ObjCCallbackFunction(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback functionCallback, JSObjectCallAsConstructorCallback constructCallback, PassOwnPtr<ObjCCallbackFunctionImpl> impl) 507 : Base(vm, globalObject->objcCallbackFunctionStructure()) 508 , m_functionCallback(functionCallback) 509 , m_constructCallback(constructCallback) 510 , m_impl(impl) 511{ 512} 513 514ObjCCallbackFunction* ObjCCallbackFunction::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl> impl) 515{ 516 ObjCCallbackFunction* function = new (NotNull, allocateCell<ObjCCallbackFunction>(vm.heap)) ObjCCallbackFunction(vm, globalObject, objCCallbackFunctionCallAsFunction, objCCallbackFunctionCallAsConstructor, impl); 517 function->finishCreation(vm, name); 518 return function; 519} 520 521void ObjCCallbackFunction::destroy(JSCell* cell) 522{ 523 ObjCCallbackFunction& function = *jsCast<ObjCCallbackFunction*>(cell); 524 function.impl()->destroy(*Heap::heap(cell)); 525 function.~ObjCCallbackFunction(); 526} 527 528 529CallType ObjCCallbackFunction::getCallData(JSCell*, CallData& callData) 530{ 531 callData.native.function = APICallbackFunction::call<ObjCCallbackFunction>; 532 return CallTypeHost; 533} 534 535ConstructType ObjCCallbackFunction::getConstructData(JSCell* cell, ConstructData& constructData) 536{ 537 ObjCCallbackFunction* callback = jsCast<ObjCCallbackFunction*>(cell); 538 if (!callback->impl()->isConstructible()) 539 return Base::getConstructData(cell, constructData); 540 constructData.native.function = APICallbackFunction::construct<ObjCCallbackFunction>; 541 return ConstructTypeHost; 542} 543 544String ObjCCallbackFunctionImpl::name() 545{ 546 if (m_type == CallbackInitMethod) 547 return class_getName(m_instanceClass); 548 // FIXME: Maybe we could support having the selector as the name of the non-init 549 // functions to make it a bit more user-friendly from the JS side? 550 return ""; 551} 552 553JSValueRef ObjCCallbackFunctionImpl::call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 554{ 555 JSGlobalContextRef contextRef = [context JSGlobalContextRef]; 556 557 id target; 558 size_t firstArgument; 559 switch (m_type) { 560 case CallbackInitMethod: { 561 RELEASE_ASSERT(!thisObject); 562 target = [m_instanceClass alloc]; 563 if (!target || ![target isKindOfClass:m_instanceClass]) { 564 *exception = toRef(JSC::createTypeError(toJS(contextRef), ASCIILiteral("self type check failed for Objective-C instance method"))); 565 return JSValueMakeUndefined(contextRef); 566 } 567 [m_invocation setTarget:target]; 568 firstArgument = 2; 569 break; 570 } 571 case CallbackInstanceMethod: { 572 target = tryUnwrapObjcObject(contextRef, thisObject); 573 if (!target || ![target isKindOfClass:m_instanceClass]) { 574 *exception = toRef(JSC::createTypeError(toJS(contextRef), ASCIILiteral("self type check failed for Objective-C instance method"))); 575 return JSValueMakeUndefined(contextRef); 576 } 577 [m_invocation setTarget:target]; 578 firstArgument = 2; 579 break; 580 } 581 case CallbackClassMethod: 582 firstArgument = 2; 583 break; 584 case CallbackBlock: 585 firstArgument = 1; 586 } 587 588 size_t argumentNumber = 0; 589 for (CallbackArgument* argument = m_arguments.get(); argument; argument = argument->m_next.get()) { 590 JSValueRef value = argumentNumber < argumentCount ? arguments[argumentNumber] : JSValueMakeUndefined(contextRef); 591 argument->set(m_invocation.get(), argumentNumber + firstArgument, context, value, exception); 592 if (*exception) 593 return JSValueMakeUndefined(contextRef); 594 ++argumentNumber; 595 } 596 597 [m_invocation invoke]; 598 599 JSValueRef result = m_result->get(m_invocation.get(), context, exception); 600 601 // Balance our call to -alloc with a call to -autorelease. We have to do this after calling -init 602 // because init family methods are allowed to release the allocated object and return something 603 // else in its place. 604 if (m_type == CallbackInitMethod) { 605 id objcResult = tryUnwrapObjcObject(contextRef, result); 606 if (objcResult) 607 [objcResult autorelease]; 608 } 609 610 return result; 611} 612 613} // namespace JSC 614 615static bool blockSignatureContainsClass() 616{ 617 static bool containsClass = ^{ 618 id block = ^(NSString *string){ return string; }; 619 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString"); 620 }(); 621 return containsClass; 622} 623 624inline bool skipNumber(const char*& position) 625{ 626 if (!isASCIIDigit(*position)) 627 return false; 628 while (isASCIIDigit(*++position)) { } 629 return true; 630} 631 632static JSObjectRef objCCallbackFunctionForInvocation(JSContext *context, NSInvocation *invocation, CallbackType type, Class instanceClass, const char* signatureWithObjcClasses) 633{ 634 if (!signatureWithObjcClasses) 635 return nil; 636 637 const char* position = signatureWithObjcClasses; 638 639 OwnPtr<CallbackResult> result = adoptPtr(parseObjCType<ResultTypeDelegate>(position)); 640 if (!result || !skipNumber(position)) 641 return nil; 642 643 switch (type) { 644 case CallbackInitMethod: 645 case CallbackInstanceMethod: 646 case CallbackClassMethod: 647 // Methods are passed two implicit arguments - (id)self, and the selector. 648 if ('@' != *position++ || !skipNumber(position) || ':' != *position++ || !skipNumber(position)) 649 return nil; 650 break; 651 case CallbackBlock: 652 // Blocks are passed one implicit argument - the block, of type "@?". 653 if (('@' != *position++) || ('?' != *position++) || !skipNumber(position)) 654 return nil; 655 // Only allow arguments of type 'id' if the block signature contains the NS type information. 656 if ((!blockSignatureContainsClass() && strchr(position, '@'))) 657 return nil; 658 break; 659 } 660 661 OwnPtr<CallbackArgument> arguments = 0; 662 OwnPtr<CallbackArgument>* nextArgument = &arguments; 663 unsigned argumentCount = 0; 664 while (*position) { 665 OwnPtr<CallbackArgument> argument = adoptPtr(parseObjCType<ArgumentTypeDelegate>(position)); 666 if (!argument || !skipNumber(position)) 667 return nil; 668 669 *nextArgument = argument.release(); 670 nextArgument = &(*nextArgument)->m_next; 671 ++argumentCount; 672 } 673 674 JSC::ExecState* exec = toJS([context JSGlobalContextRef]); 675 JSC::JSLockHolder locker(exec); 676 OwnPtr<JSC::ObjCCallbackFunctionImpl> impl = adoptPtr(new JSC::ObjCCallbackFunctionImpl(invocation, type, instanceClass, arguments.release(), result.release())); 677 return toRef(JSC::ObjCCallbackFunction::create(exec->vm(), exec->lexicalGlobalObject(), impl->name(), impl.release())); 678} 679 680JSObjectRef objCCallbackFunctionForInit(JSContext *context, Class cls, Protocol *protocol, SEL sel, const char* types) 681{ 682 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]]; 683 [invocation setSelector:sel]; 684 return objCCallbackFunctionForInvocation(context, invocation, CallbackInitMethod, cls, _protocol_getMethodTypeEncoding(protocol, sel, YES, YES)); 685} 686 687JSObjectRef objCCallbackFunctionForMethod(JSContext *context, Class cls, Protocol *protocol, BOOL isInstanceMethod, SEL sel, const char* types) 688{ 689 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]]; 690 [invocation setSelector:sel]; 691 // We need to retain the target Class because m_invocation doesn't retain it 692 // by default (and we don't want it to). 693 if (!isInstanceMethod) 694 [invocation setTarget:[cls retain]]; 695 return objCCallbackFunctionForInvocation(context, invocation, isInstanceMethod ? CallbackInstanceMethod : CallbackClassMethod, isInstanceMethod ? cls : nil, _protocol_getMethodTypeEncoding(protocol, sel, YES, isInstanceMethod)); 696} 697 698JSObjectRef objCCallbackFunctionForBlock(JSContext *context, id target) 699{ 700 if (!_Block_has_signature(target)) 701 return 0; 702 const char* signature = _Block_signature(target); 703 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:signature]]; 704 705 // We don't want to use -retainArguments because that leaks memory. Arguments 706 // would be retained indefinitely between invocations of the callback. 707 // Additionally, we copy the target because we want the block to stick around 708 // until the ObjCCallbackFunctionImpl is destroyed. 709 [invocation setTarget:[target copy]]; 710 711 return objCCallbackFunctionForInvocation(context, invocation, CallbackBlock, nil, signature); 712} 713 714id tryUnwrapConstructor(JSObjectRef object) 715{ 716 if (!toJS(object)->inherits(JSC::ObjCCallbackFunction::info())) 717 return nil; 718 JSC::ObjCCallbackFunctionImpl* impl = static_cast<JSC::ObjCCallbackFunction*>(toJS(object))->impl(); 719 if (!impl->isConstructible()) 720 return nil; 721 return impl->wrappedConstructor(); 722} 723 724#endif 725