]>
Commit | Line | Data |
---|---|---|
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 | ||
46 | class CallbackArgument { | |
47 | public: | |
48 | virtual ~CallbackArgument(); | |
49 | virtual void set(NSInvocation *, NSInteger, JSContext *, JSValueRef, JSValueRef*) = 0; | |
50 | ||
51 | OwnPtr<CallbackArgument> m_next; | |
52 | }; | |
53 | ||
54 | CallbackArgument::~CallbackArgument() | |
55 | { | |
56 | } | |
57 | ||
58 | class 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 | ||
66 | template<typename T> | |
67 | class 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 | ||
75 | template<typename T> | |
76 | class 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 | ||
84 | class 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 | ||
92 | class 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 | ||
100 | class CallbackArgumentOfClass : public CallbackArgument { | |
101 | public: | |
102 | CallbackArgumentOfClass(Class cls) | |
103 | : CallbackArgument() | |
104 | , m_class(cls) | |
105 | { | |
106 | [m_class retain]; | |
107 | } | |
108 | ||
109 | private: | |
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 | ||
137 | class 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 | ||
145 | class 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 | ||
153 | class 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 | ||
161 | class 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 | ||
169 | class 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 | ||
177 | class CallbackArgumentStruct : public CallbackArgument { | |
178 | public: | |
179 | CallbackArgumentStruct(NSInvocation *conversionInvocation, const char* encodedType) | |
180 | : m_conversionInvocation(conversionInvocation) | |
181 | , m_buffer(encodedType) | |
182 | { | |
183 | } | |
184 | ||
185 | private: | |
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 | ||
198 | class ArgumentTypeDelegate { | |
199 | public: | |
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 | ||
267 | class CallbackResult { | |
268 | public: | |
269 | virtual ~CallbackResult() | |
270 | { | |
271 | } | |
272 | ||
273 | virtual JSValueRef get(NSInvocation *, JSContext *, JSValueRef*) = 0; | |
274 | }; | |
275 | ||
276 | class CallbackResultVoid : public CallbackResult { | |
277 | virtual JSValueRef get(NSInvocation *, JSContext *context, JSValueRef*) override | |
278 | { | |
279 | return JSValueMakeUndefined([context JSGlobalContextRef]); | |
280 | } | |
281 | }; | |
282 | ||
283 | class 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 | ||
292 | template<typename T> | |
293 | class 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 | ||
302 | class 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 | ||
311 | class CallbackResultStruct : public CallbackResult { | |
312 | public: | |
313 | CallbackResultStruct(NSInvocation *conversionInvocation, const char* encodedType) | |
314 | : m_conversionInvocation(conversionInvocation) | |
315 | , m_buffer(encodedType) | |
316 | { | |
317 | } | |
318 | ||
319 | private: | |
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 | ||
337 | class ResultTypeDelegate { | |
338 | public: | |
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 | ||
387 | enum CallbackType { | |
12899fa2 | 388 | CallbackInitMethod, |
93a37866 A |
389 | CallbackInstanceMethod, |
390 | CallbackClassMethod, | |
391 | CallbackBlock | |
392 | }; | |
393 | ||
394 | namespace JSC { | |
395 | ||
396 | class ObjCCallbackFunctionImpl { | |
397 | public: | |
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 |
455 | private: |
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 | ||
464 | static 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 |
492 | static 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 |
521 | const JSC::ClassInfo ObjCCallbackFunction::s_info = { "CallbackFunction", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCCallbackFunction) }; |
522 | ||
12899fa2 A |
523 | ObjCCallbackFunction::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 | 531 | ObjCCallbackFunction* 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 | ||
538 | void ObjCCallbackFunction::destroy(JSCell* cell) | |
539 | { | |
540 | static_cast<ObjCCallbackFunction*>(cell)->ObjCCallbackFunction::~ObjCCallbackFunction(); | |
541 | } | |
542 | ||
12899fa2 A |
543 | |
544 | CallType ObjCCallbackFunction::getCallData(JSCell*, CallData& callData) | |
545 | { | |
546 | callData.native.function = APICallbackFunction::call<ObjCCallbackFunction>; | |
547 | return CallTypeHost; | |
548 | } | |
549 | ||
550 | ConstructType 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 | ||
559 | String 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 |
568 | JSValueRef 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 | ||
630 | static 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 | ||
639 | inline bool skipNumber(const char*& position) | |
640 | { | |
641 | if (!isASCIIDigit(*position)) | |
642 | return false; | |
643 | while (isASCIIDigit(*++position)) { } | |
644 | return true; | |
645 | } | |
646 | ||
647 | static 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 | ||
692 | JSObjectRef 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 | ||
699 | JSObjectRef 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 | ||
710 | JSObjectRef 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 | 726 | id 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 |