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