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