]> git.saurik.com Git - apple/javascriptcore.git/blame - API/ObjCCallbackFunction.mm
JavaScriptCore-1218.34.tar.gz
[apple/javascriptcore.git] / API / ObjCCallbackFunction.mm
CommitLineData
93a37866
A
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
12899fa2 31#import "APICallbackFunction.h"
93a37866
A
32#import "APICast.h"
33#import "APIShims.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), "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 {
12899fa2 388 CallbackInitMethod,
93a37866
A
389 CallbackInstanceMethod,
390 CallbackClassMethod,
391 CallbackBlock
392};
393
394namespace JSC {
395
396class ObjCCallbackFunctionImpl {
397public:
398 ObjCCallbackFunctionImpl(JSContext *context, NSInvocation *invocation, CallbackType type, Class instanceClass, PassOwnPtr<CallbackArgument> arguments, PassOwnPtr<CallbackResult> result)
399 : m_context(context)
400 , m_type(type)
401 , m_instanceClass([instanceClass retain])
402 , m_invocation(invocation)
403 , m_arguments(arguments)
404 , m_result(result)
405 {
12899fa2 406 ASSERT((type != CallbackInstanceMethod && type != CallbackInitMethod) || instanceClass);
93a37866
A
407 }
408
409 ~ObjCCallbackFunctionImpl()
410 {
12899fa2
A
411 // We need to explicity release the target since we didn't call
412 // -retainArguments on m_invocation (and we don't want to do so).
413 if (m_type == CallbackBlock || m_type == CallbackClassMethod)
93a37866
A
414 [[m_invocation.get() target] release];
415 [m_instanceClass release];
416 }
417
418 JSValueRef call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
419
420 JSContext *context()
421 {
422 return m_context.get();
423 }
424
425 void setContext(JSContext *context)
426 {
427 ASSERT(!m_context.get());
428 m_context.set(context);
429 }
430
431 id wrappedBlock()
432 {
433 return m_type == CallbackBlock ? [m_invocation target] : nil;
434 }
435
12899fa2
A
436 id wrappedConstructor()
437 {
438 switch (m_type) {
439 case CallbackBlock:
440 return [m_invocation target];
441 case CallbackInitMethod:
442 return m_instanceClass;
443 default:
444 return nil;
445 }
446 }
447
448 bool isConstructible()
449 {
450 return !!wrappedBlock() || m_type == CallbackInitMethod;
451 }
452
453 String name();
454
93a37866
A
455private:
456 WeakContextRef m_context;
457 CallbackType m_type;
458 Class m_instanceClass;
459 RetainPtr<NSInvocation> m_invocation;
460 OwnPtr<CallbackArgument> m_arguments;
461 OwnPtr<CallbackResult> m_result;
462};
463
464static JSValueRef objCCallbackFunctionCallAsFunction(JSContextRef callerContext, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
465{
466 // Retake the API lock - we need this for a few reasons:
467 // (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.
468 // (2) We're calling some JSC internals that require us to be on the 'inside' - e.g. createTypeError.
469 // (3) We need to be locked (per context would be fine) against conflicting usage of the ObjCCallbackFunction's NSInvocation.
470 JSC::APIEntryShim entryShim(toJS(callerContext));
471
472 ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(toJS(function));
473 ObjCCallbackFunctionImpl* impl = callback->impl();
474 JSContext *context = impl->context();
475 if (!context) {
476 context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(toJS(callerContext)->lexicalGlobalObject()->globalExec())];
477 impl->setContext(context);
478 }
479
480 CallbackData callbackData;
481 JSValueRef result;
482 @autoreleasepool {
483 [context beginCallbackWithData:&callbackData thisValue:thisObject argumentCount:argumentCount arguments:arguments];
484 result = impl->call(context, thisObject, argumentCount, arguments, exception);
485 if (context.exception)
486 *exception = valueInternalValue(context.exception);
487 [context endCallbackWithData:&callbackData];
488 }
489 return result;
490}
491
12899fa2
A
492static JSObjectRef objCCallbackFunctionCallAsConstructor(JSContextRef callerContext, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
493{
494 JSC::APIEntryShim entryShim(toJS(callerContext));
495
496 ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(toJS(constructor));
497 ObjCCallbackFunctionImpl* impl = callback->impl();
498 JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(toJS(callerContext)->lexicalGlobalObject()->globalExec())];
499
500 CallbackData callbackData;
501 JSValueRef result;
502 @autoreleasepool {
503 [context beginCallbackWithData:&callbackData thisValue:nil argumentCount:argumentCount arguments:arguments];
504 result = impl->call(context, NULL, argumentCount, arguments, exception);
505 if (context.exception)
506 *exception = valueInternalValue(context.exception);
507 [context endCallbackWithData:&callbackData];
508 }
509
510 JSGlobalContextRef contextRef = [context JSGlobalContextRef];
511 if (*exception)
512 return 0;
513
514 if (!JSValueIsObject(contextRef, result)) {
515 *exception = toRef(JSC::createTypeError(toJS(contextRef), "Objective-C blocks called as constructors must return an object."));
516 return 0;
517 }
518 return (JSObjectRef)result;
519}
520
93a37866
A
521const JSC::ClassInfo ObjCCallbackFunction::s_info = { "CallbackFunction", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCCallbackFunction) };
522
12899fa2
A
523ObjCCallbackFunction::ObjCCallbackFunction(JSC::VM&, JSC::JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback functionCallback, JSObjectCallAsConstructorCallback constructCallback, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
524 : Base(globalObject, globalObject->objcCallbackFunctionStructure())
525 , m_functionCallback(functionCallback)
526 , m_constructCallback(constructCallback)
93a37866
A
527 , m_impl(impl)
528{
529}
530
12899fa2 531ObjCCallbackFunction* ObjCCallbackFunction::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
93a37866 532{
12899fa2
A
533 ObjCCallbackFunction* function = new (NotNull, allocateCell<ObjCCallbackFunction>(vm.heap)) ObjCCallbackFunction(vm, globalObject, objCCallbackFunctionCallAsFunction, objCCallbackFunctionCallAsConstructor, impl);
534 function->finishCreation(vm, name);
93a37866
A
535 return function;
536}
537
538void ObjCCallbackFunction::destroy(JSCell* cell)
539{
540 static_cast<ObjCCallbackFunction*>(cell)->ObjCCallbackFunction::~ObjCCallbackFunction();
541}
542
12899fa2
A
543
544CallType ObjCCallbackFunction::getCallData(JSCell*, CallData& callData)
545{
546 callData.native.function = APICallbackFunction::call<ObjCCallbackFunction>;
547 return CallTypeHost;
548}
549
550ConstructType ObjCCallbackFunction::getConstructData(JSCell* cell, ConstructData& constructData)
551{
552 ObjCCallbackFunction* callback = jsCast<ObjCCallbackFunction*>(cell);
553 if (!callback->impl()->isConstructible())
554 return Base::getConstructData(cell, constructData);
555 constructData.native.function = APICallbackFunction::construct<ObjCCallbackFunction>;
556 return ConstructTypeHost;
557}
558
559String ObjCCallbackFunctionImpl::name()
560{
561 if (m_type == CallbackInitMethod)
562 return class_getName(m_instanceClass);
563 // FIXME: Maybe we could support having the selector as the name of the non-init
564 // functions to make it a bit more user-friendly from the JS side?
565 return "";
566}
567
93a37866
A
568JSValueRef ObjCCallbackFunctionImpl::call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
569{
570 JSGlobalContextRef contextRef = [context JSGlobalContextRef];
571
12899fa2 572 id target;
93a37866
A
573 size_t firstArgument;
574 switch (m_type) {
12899fa2
A
575 case CallbackInitMethod: {
576 RELEASE_ASSERT(!thisObject);
577 target = [m_instanceClass alloc];
578 if (!target || ![target isKindOfClass:m_instanceClass]) {
579 *exception = toRef(JSC::createTypeError(toJS(contextRef), "self type check failed for Objective-C instance method"));
580 return JSValueMakeUndefined(contextRef);
581 }
582 [m_invocation setTarget:target];
583 firstArgument = 2;
584 break;
585 }
93a37866 586 case CallbackInstanceMethod: {
12899fa2 587 target = tryUnwrapObjcObject(contextRef, thisObject);
93a37866
A
588 if (!target || ![target isKindOfClass:m_instanceClass]) {
589 *exception = toRef(JSC::createTypeError(toJS(contextRef), "self type check failed for Objective-C instance method"));
590 return JSValueMakeUndefined(contextRef);
591 }
592 [m_invocation setTarget:target];
12899fa2
A
593 firstArgument = 2;
594 break;
93a37866 595 }
93a37866
A
596 case CallbackClassMethod:
597 firstArgument = 2;
598 break;
599 case CallbackBlock:
600 firstArgument = 1;
601 }
602
603 size_t argumentNumber = 0;
604 for (CallbackArgument* argument = m_arguments.get(); argument; argument = argument->m_next.get()) {
605 JSValueRef value = argumentNumber < argumentCount ? arguments[argumentNumber] : JSValueMakeUndefined(contextRef);
606 argument->set(m_invocation.get(), argumentNumber + firstArgument, context, value, exception);
607 if (*exception)
608 return JSValueMakeUndefined(contextRef);
609 ++argumentNumber;
610 }
611
612 [m_invocation invoke];
613
12899fa2
A
614 JSValueRef result = m_result->get(m_invocation.get(), context, exception);
615
616 // Balance our call to -alloc with a call to -autorelease. We have to do this after calling -init
617 // because init family methods are allowed to release the allocated object and return something
618 // else in its place.
619 if (m_type == CallbackInitMethod) {
620 id objcResult = tryUnwrapObjcObject(contextRef, result);
621 if (objcResult)
622 [objcResult autorelease];
623 }
624
625 return result;
93a37866
A
626}
627
628} // namespace JSC
629
630static bool blockSignatureContainsClass()
631{
632 static bool containsClass = ^{
633 id block = ^(NSString *string){ return string; };
634 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
635 }();
636 return containsClass;
637}
638
639inline bool skipNumber(const char*& position)
640{
641 if (!isASCIIDigit(*position))
642 return false;
643 while (isASCIIDigit(*++position)) { }
644 return true;
645}
646
647static JSObjectRef objCCallbackFunctionForInvocation(JSContext *context, NSInvocation *invocation, CallbackType type, Class instanceClass, const char* signatureWithObjcClasses)
648{
649 const char* position = signatureWithObjcClasses;
650
651 OwnPtr<CallbackResult> result = adoptPtr(parseObjCType<ResultTypeDelegate>(position));
652 if (!result || !skipNumber(position))
653 return nil;
654
655 switch (type) {
12899fa2 656 case CallbackInitMethod:
93a37866
A
657 case CallbackInstanceMethod:
658 case CallbackClassMethod:
659 // Methods are passed two implicit arguments - (id)self, and the selector.
660 if ('@' != *position++ || !skipNumber(position) || ':' != *position++ || !skipNumber(position))
661 return nil;
662 break;
663 case CallbackBlock:
664 // Blocks are passed one implicit argument - the block, of type "@?".
665 if (('@' != *position++) || ('?' != *position++) || !skipNumber(position))
666 return nil;
667 // Only allow arguments of type 'id' if the block signature contains the NS type information.
668 if ((!blockSignatureContainsClass() && strchr(position, '@')))
669 return nil;
670 break;
671 }
672
673 OwnPtr<CallbackArgument> arguments = 0;
674 OwnPtr<CallbackArgument>* nextArgument = &arguments;
675 unsigned argumentCount = 0;
676 while (*position) {
677 OwnPtr<CallbackArgument> argument = adoptPtr(parseObjCType<ArgumentTypeDelegate>(position));
678 if (!argument || !skipNumber(position))
679 return nil;
680
681 *nextArgument = argument.release();
682 nextArgument = &(*nextArgument)->m_next;
683 ++argumentCount;
684 }
685
686 JSC::ExecState* exec = toJS([context JSGlobalContextRef]);
687 JSC::APIEntryShim shim(exec);
688 OwnPtr<JSC::ObjCCallbackFunctionImpl> impl = adoptPtr(new JSC::ObjCCallbackFunctionImpl(context, invocation, type, instanceClass, arguments.release(), result.release()));
12899fa2
A
689 return toRef(JSC::ObjCCallbackFunction::create(exec->vm(), exec->lexicalGlobalObject(), impl->name(), impl.release()));
690}
691
692JSObjectRef objCCallbackFunctionForInit(JSContext *context, Class cls, Protocol *protocol, SEL sel, const char* types)
693{
694 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]];
695 [invocation setSelector:sel];
696 return objCCallbackFunctionForInvocation(context, invocation, CallbackInitMethod, cls, _protocol_getMethodTypeEncoding(protocol, sel, YES, YES));
93a37866
A
697}
698
699JSObjectRef objCCallbackFunctionForMethod(JSContext *context, Class cls, Protocol *protocol, BOOL isInstanceMethod, SEL sel, const char* types)
700{
701 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]];
702 [invocation setSelector:sel];
12899fa2
A
703 // We need to retain the target Class because m_invocation doesn't retain it
704 // by default (and we don't want it to).
93a37866 705 if (!isInstanceMethod)
12899fa2 706 [invocation setTarget:[cls retain]];
93a37866
A
707 return objCCallbackFunctionForInvocation(context, invocation, isInstanceMethod ? CallbackInstanceMethod : CallbackClassMethod, isInstanceMethod ? cls : nil, _protocol_getMethodTypeEncoding(protocol, sel, YES, isInstanceMethod));
708}
709
710JSObjectRef objCCallbackFunctionForBlock(JSContext *context, id target)
711{
712 if (!_Block_has_signature(target))
713 return 0;
714 const char* signature = _Block_signature(target);
715 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:signature]];
12899fa2
A
716
717 // We don't want to use -retainArguments because that leaks memory. Arguments
718 // would be retained indefinitely between invocations of the callback.
719 // Additionally, we copy the target because we want the block to stick around
720 // until the ObjCCallbackFunctionImpl is destroyed.
93a37866 721 [invocation setTarget:[target copy]];
12899fa2 722
93a37866
A
723 return objCCallbackFunctionForInvocation(context, invocation, CallbackBlock, nil, signature);
724}
725
12899fa2 726id tryUnwrapConstructor(JSObjectRef object)
93a37866
A
727{
728 if (!toJS(object)->inherits(&JSC::ObjCCallbackFunction::s_info))
729 return nil;
12899fa2
A
730 JSC::ObjCCallbackFunctionImpl* impl = static_cast<JSC::ObjCCallbackFunction*>(toJS(object))->impl();
731 if (!impl->isConstructible())
732 return nil;
733 return impl->wrappedConstructor();
93a37866
A
734}
735
736#endif