This repository was archived by the owner on Aug 31, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 225
Expand file tree
/
Copy pathinterop-objc.lcb
More file actions
561 lines (437 loc) · 20.9 KB
/
interop-objc.lcb
File metadata and controls
561 lines (437 loc) · 20.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
module __VMTEST.interop_objc
use com.livecode.foreign
use com.livecode.objc
use com.livecode.__INTERNAL._testlib
---------
__safe foreign handler NSObjectAlloc() returns ObjcRetainedId binds to "objc:NSObject.+alloc"
__safe foreign handler NSNumberCreateWithInt(in pInt as CInt) returns ObjcAutoreleasedId binds to "objc:NSNumber.+numberWithInt:"
__safe foreign handler NSNumberGetIntValue(in pObj as ObjcId) returns CInt binds to "objc:NSNumber.intValue"
__safe foreign handler NSNumberCreateWithLongLong(in pInt as CLongLong) returns ObjcAutoreleasedId binds to "objc:NSNumber.+numberWithLongLong:"
__safe foreign handler NSNumberGetLongLongValue(in pObj as ObjcId) returns CLongLong binds to "objc:NSNumber.longLongValue"
__safe foreign handler NSNumberCreateWithFloat(in pInt as CFloat) returns ObjcAutoreleasedId binds to "objc:NSNumber.+numberWithFloat:"
__safe foreign handler NSNumberGetFloatValue(in pObj as ObjcId) returns CFloat binds to "objc:NSNumber.floatValue"
__safe foreign handler NSNumberCreateWithDouble(in pInt as CDouble) returns ObjcAutoreleasedId binds to "objc:NSNumber.+numberWithDouble:"
__safe foreign handler NSNumberGetDoubleValue(in pObj as ObjcId) returns CDouble binds to "objc:NSNumber.doubleValue"
---------
-- NEED TO CHECK class and instance methods, existant and non-existant
-- also protocols and superclasses.
__safe foreign handler CreateObjCObjectDoesntExist() returns ObjcId binds to "objc:NSObject.-foobar"
private handler TestObjcInterop_NonExistantObjC()
CreateObjCObjectDoesntExist()
end handler
private handler TestObjcInterop_ExistantObjC()
variable tObj as ObjcObject
put NSObjectAlloc() into tObj
end handler
public handler TestObjcInterop_Binding()
if not the operating system is in ["mac", "ios"] then
skip test "objc binding succeeds" because "not implemented on" && the operating system
return
end if
MCUnitTestHandlerThrowsNamed(TestObjcInterop_NonExistantObjC, "livecode.lang.ForeignHandlerBindingError", "Failure to bind to non-existant objc function throws error")
MCUnitTestHandlerDoesntThrow(TestObjcInterop_ExistantObjC, "Binding to existant objc function does not throw error")
test "Non-existant objc function gives nothing as handler value" when CreateObjCObjectDoesntExist is nothing
test "Existant objc function gives non-nothing as handler value" when NSObjectAlloc is not nothing
end handler
---------
__safe foreign handler NSObjectGetRetainCount(in pObj as ObjcId) returns CULong binds to "objc:NSObject.-retainCount"
__safe foreign handler NSObjectGetSelf(in pObj as ObjcId) returns ObjcId binds to "objc:NSObject.-self"
public handler TestObjInterop_ObjcObjectLifetime()
if not the operating system is in ["mac", "ios"] then
skip test "objc binding succeeds" because "not implemented on" && the operating system
return
end if
/* Test that ObjcRetainedId gives its reference to ObjcObject */
variable tObjcObjectFromRetainedId as ObjcObject
put NSObjectAlloc() into tObjcObjectFromRetainedId
test "retained id into objc object doesn't retain" when NSObjectGetRetainCount(tObjcObjectFromRetainedId) is 1
/* Test that ObjcId copies its reference to ObjcObject */
variable tObjcObjectFromId as ObjcObject
put NSObjectGetSelf(tObjcObjectFromRetainedId) into tObjcObjectFromId
test "id into objc object retains" when NSObjectGetRetainCount(tObjcObjectFromId) is 2
/* Test that ObjcAutoreleasedId copies its reference to ObjcObject */
variable tObjcObjectFromAutoreleasedId as ObjcObject
put NSNumberCreateWithDouble(3.14159) into tObjcObjectFromAutoreleasedId
test "autoreleased id into objc object retains" when NSObjectGetRetainCount(tObjcObjectFromAutoreleasedId) is 2
end handler
---------
public handler TestObjInterop_RoundtripNumber()
if not the operating system is in ["mac", "ios"] then
skip test "objc binding succeeds" because "not implemented on" && the operating system
return
end if
variable tIntObj as ObjcObject
put NSNumberCreateWithInt(314159) into tIntObj
test "NSNumber roundtrip CInt" when NSNumberGetIntValue(tIntObj) is 314159
variable tLongLongObj as ObjcObject
put NSNumberCreateWithLongLong(314159) into tLongLongObj
test "NSNumber roundtrip CLongLong" when NSNumberGetLongLongValue(tLongLongObj) is 314159
variable tFloatObj as ObjcObject
put NSNumberCreateWithFloat(314159) into tFloatObj
test "NSNumber roundtrip CFloat" when NSNumberGetFloatValue(tFloatObj) is 314159
variable tDoubleObj as ObjcObject
put NSNumberCreateWithDouble(314159) into tDoubleObj
test "NSNumber roundtrip CDouble" when NSNumberGetDoubleValue(tDoubleObj) is 314159
end handler
--------
__safe foreign handler MCHandlerTryToInvokeWithList(in Handler as any, inout Arguments as optional List, out Result as optional any) returns optional any binds to "<builtin>"
__safe foreign handler NSArrayFirstObject(in pObj as ObjcId) returns ObjcId binds to "objc:NSArray.firstObject"
private handler __CallNSNumberMethodOnNSString(in pString as ObjcObject) returns optional any
return NSArrayFirstObject(pString)
end handler
public handler TestObjcInterop_CatchObjcException()
if not the operating system is in ["mac", "ios"] then
skip test "objc exception handler succeeds" because "not implemented on" && the operating system
return
end if
variable tArguments as List
variable tResult as optional any
put [ StringToNSString("foo") ] into tArguments
test "objc exception is caught" when MCHandlerTryToInvokeWithList(__CallNSNumberMethodOnNSString, tArguments, tResult) is not nothing
end handler
---------
// NSRange is a pair of (natural sized) unsigned integers
public foreign type NSRange binds to "MCAggregateTypeInfo:pp"
// NSRange is 8 bytes on 64-bit arch, 4 bytes on 32-bit
// so will use objc_msgSend_stret on 32-bit arch, but objc_msgSend on
// 64 bit.
foreign handler RangeOfString(in pHaystack as ObjcId, in pNeedle as ObjcId) returns NSRange binds to "objc:NSString.-rangeOfString:"
public handler TestReturnNSRange()
if not the operating system is in ["mac", "ios"] then
skip test "return NSRange struct succeeds" because "not implemented on" && the operating system
return
end if
variable tRange as NSRange
unsafe
put RangeOfString(StringToNSString("cartoon"), \
StringToNSString("art")) into tRange
end unsafe
variable tRangeList as List
put tRange into tRangeList
test "return NSRange struct succeeds" when \
tRangeList is [1,3]
end handler
// NSRect is a quadruple of (natural sized) floats
public foreign type NSRect binds to "MCAggregateTypeInfo:qqqq"
foreign handler NSValueWithRect(in pRect as NSRect) returns ObjcId \
binds to "objc:NSValue.+valueWithRect:"
// NSRect is 32 bytes on 64-bit arch, 16 bytes on 32-bit
// so will use objc_msgSend_stret on both archs
foreign handler NSRectFromValue(in pValue as ObjcId) returns NSRect \
binds to "objc:NSValue.-rectValue"
public handler TestReturnNSRect()
if not the operating system is in ["mac", "ios"] then
skip test "return NSRange struct succeeds" because "not implemented on" && the operating system
return
end if
variable tRect as List
variable tExponent as Integer
repeat with tExponent from 1 up to 4
push the exponential of tExponent onto tRect
end repeat
variable tRoundTripped as NSRect
unsafe
variable tValue as ObjcId
put NSValueWithRect(tRect) into tValue
put NSRectFromValue(tValue) into tRoundTripped
end unsafe
variable tRectList as List
put tRoundTripped into tRectList
test "return NSRect struct succeeds" when \
tRectList is tRect
end handler
----------
private foreign handler NSUserNotificationSetTitle(in Obj as ObjcId, in Title as ObjcId) returns nothing binds to "objc:NSUserNotification.-setTitle:"
private foreign handler NSUserNotificationGetTitle(in Obj as ObjcId) returns ObjcId binds to "objc:NSUserNotification.title"
public handler TestObjcDynamicPropertyBinding()
if not the operating system is in ["mac", "ios"] then
skip test "objc property binding succeeds" because "not implemented on" && the operating system
return
end if
test "dynamic objc setter binds" when NSUserNotificationSetTitle is not nothing
test "dynamic objc getter binds" when NSUserNotificationGetTitle is not nothing
end handler
--------
foreign handler DynamicGetInt(in pObject as ObjcId) returns CInt \
binds to "objc:.-intValue"
public handler TestDynamicInstanceMethodBinding()
if not the operating system is in ["mac", "ios"] then
skip test "obj-c dynamic instance method binding" \
because "not implemented on" && the operating system
return
end if
unsafe
variable tNumber as ObjcObject
put NSNumberCreateWithInt(10) into tNumber
test "dynamic method binds" when DynamicGetInt is not nothing
test "dynamic method call succeeds" when DynamicGetInt(tNumber) is 10
end unsafe
end handler
foreign handler DynamicGetIntNoClass(in pObject as ObjcId) returns CInt \
binds to "objc:-intValue"
public handler TestDynamicInstanceMethodNoClassCrash()
if not the operating system is in ["mac", "ios"] then
skip test "obj-c dynamic instance method binding no class crash" \
because "not implemented on" && the operating system
return
end if
unsafe
test "dynamic method binds" when DynamicGetIntNoClass is not nothing
end unsafe
end handler
foreign handler DynamicAlloc() returns ObjcRetainedId \
binds to "objc:.+alloc"
public handler TestDynamicClassMethodBindingFails()
if not the operating system is in ["mac", "ios"] then
skip test "obj-c dynamic class method binding fails" \
because "not implemented on" && the operating system
return
end if
test "dynamic class method binding fails" when DynamicAlloc is nothing
end handler
--------
handler _TestNativeReturnTypeConvert(in pParam as ObjcObject, in pParam2 as ObjcObject) returns Integer
return 1
end handler
foreign handler Objc_NSControlIsValidObject(in pTarget as ObjcId, in pControl as ObjcId, in pObject as ObjcId) returns CSChar binds to "objc:.-control:isValidObject:"
foreign handler Objc_NSControlAlloc() returns ObjcRetainedId \
binds to "objc:AppKit.framework>NSControl.+alloc"
public handler TestDelegateNativeReturnType()
if not the operating system is in ["mac", "ios"] then
skip test "objc delegate returns valid bridge type" \
because "not implemented on" && the operating system
return
end if
plan 2 tests
variable tDelegate as optional ObjcObject
unsafe
variable tControl as ObjcObject
put Objc_NSControlAlloc() into tControl
put CreateObjcDelegate("NSControlTextEditingDelegate", \
{ "control:isValidObject:": \
_TestNativeReturnTypeConvert }) \
into tDelegate
test "create delegate succeeds" when tDelegate is not nothing
test "delegate converts to native return type" when \
Objc_NSControlIsValidObject(tDelegate, tControl, tControl) is 1
end unsafe
end handler
foreign handler Objc_NSMenuAlloc() returns ObjcRetainedId \
binds to "objc:AppKit.framework>NSMenu.+alloc"
foreign handler Objc_NSMenuNumberOfItems(in pTarget as ObjcId, in pMenu as ObjcId) returns CLong binds to "objc:.-numberOfItemsInMenu:"
handler _TestForeignReturnType(in pParam as ObjcObject) returns CLong
return 1
end handler
public handler TestDelegateForeignReturnType()
if not the operating system is in ["mac", "ios"] then
skip test "objc delegate returns correct foreign type" \
because "not implemented on" && the operating system
return
end if
plan 2 tests
variable tDelegate as optional ObjcObject
unsafe
variable tMenu as ObjcObject
put Objc_NSMenuAlloc() into tMenu
put CreateObjcDelegate("NSMenuDelegate", \
{ "numberOfItemsInMenu:": \
_TestForeignReturnType }) \
into tDelegate
test "create delegate succeeds" when tDelegate is not nothing
test "delegate returns foreign type" when \
Objc_NSMenuNumberOfItems(tDelegate, tMenu) is 1
end unsafe
end handler
handler _TestDelegateBadSignatureCallback(in pParam as ObjcObject) returns String
return ""
end handler
private handler _TestDelegateBadSignature()
// Ensure NSMenuDelegate is available
unsafe
variable tMenu as ObjcObject
put Objc_NSMenuAlloc() into tMenu
end unsafe
CreateObjcDelegate("NSMenuDelegate", \
{ "numberOfItemsInMenu:": \
_TestDelegateBadSignatureCallback })
end handler
private handler _TestDelegateContextBadSignature()
// Ensure NSMenuDelegate is available
unsafe
variable tMenu as ObjcObject
put Objc_NSMenuAlloc() into tMenu
end unsafe
// Use correct signature for no-context delegate
CreateObjcDelegateWithContext("NSMenuDelegate", \
{ "numberOfItemsInMenu:": \
_TestForeignReturnType }, \
"")
end handler
public handler TestDelegateBadSignature()
if not the operating system is in ["mac", "ios"] then
skip test "objc delegate error returning invalid bridge type" \
because "not implemented on" && the operating system
return
end if
MCUnitTestHandlerThrowsNamed(_TestDelegateBadSignature, \
"livecode.objc.DelegateCallbackSignatureError", \
"Creating delegate throws error when callback has incorrect signature")
end handler
public handler TestDelegateContextBadSignature()
if not the operating system is in ["mac", "ios"] then
skip test "objc delegate error no context parameter" \
because "not implemented on" && the operating system
return
end if
MCUnitTestHandlerThrowsNamed(_TestDelegateContextBadSignature, \
"livecode.objc.DelegateCallbackSignatureError", \
"Creating delegate throws error when callback omits context parameter")
end handler
foreign handler Objc_NSPortDelegateHandlePortMessage(in pTarget as ObjcId, in pPortMessage as ObjcId) returns nothing \
binds to "objc:.-handlePortMessage:"
foreign handler Objc_NSPortMessageAlloc() returns ObjcRetainedId \
binds to "objc:Foundation.framework>NSPortMessage.+alloc"
private variable mCalled as Boolean
handler _SetTestDelegateMethodCalled(in pValue as Boolean)
put pValue into mCalled
end handler
handler _GetTestDelegateMethodCalled()
return mCalled
end handler
handler _TestDelegateMethodCalled(in pParam as ObjcObject) returns nothing
_SetTestDelegateMethodCalled(true)
end handler
public handler TestInformalDelegateVoidReturn()
if not the operating system is in ["mac", "ios"] then
skip test "objc informal delegatevoid return type" \
because "not implemented on" && the operating system
return
end if
_SetTestDelegateMethodCalled(false)
plan 2 tests
variable tDelegate as optional ObjcObject
unsafe
variable tPortMessage as ObjcObject
put Objc_NSPortMessageAlloc() into tPortMessage
put CreateObjcInformalDelegate([Objc_NSPortDelegateHandlePortMessage], \
{ "handlePortMessage:": \
_TestDelegateMethodCalled }) \
into tDelegate
test "create delegate succeeds" when tDelegate is not nothing
Objc_NSPortDelegateHandlePortMessage(tDelegate, tPortMessage)
test "delegate method void return" when \
_GetTestDelegateMethodCalled()
end unsafe
end handler
handler _TestDelegateMethodCalledWithContext(in pContext as Boolean, in pParam as ObjcObject) returns nothing
_SetTestDelegateMethodCalled(pContext)
end handler
public handler TestInformalDelegateWithContext()
if not the operating system is in ["mac", "ios"] then
skip test "objc informal delegate with context" \
because "not implemented on" && the operating system
return
end if
_SetTestDelegateMethodCalled(false)
plan 2 tests
variable tDelegate as optional ObjcObject
unsafe
variable tPortMessage as ObjcObject
put Objc_NSPortMessageAlloc() into tPortMessage
put CreateObjcInformalDelegateWithContext([Objc_NSPortDelegateHandlePortMessage], \
{ "handlePortMessage:": \
_TestDelegateMethodCalledWithContext }, \
true) \
into tDelegate
test "create delegate succeeds" when tDelegate is not nothing
Objc_NSPortDelegateHandlePortMessage(tDelegate, tPortMessage)
test "delegate method called with context" when \
_GetTestDelegateMethodCalled()
end unsafe
end handler
handler _TestInformalDelegateBadSignatureCallback(in pParam as ObjcObject) returns String
return ""
end handler
private handler _TestInformalDelegateBadSignature()
CreateObjcInformalDelegate([Objc_NSPortDelegateHandlePortMessage], \
{ "handlePortMessage:": \
_TestInformalDelegateBadSignatureCallback })
end handler
private handler _TestInformalDelegateContextBadSignature()
// Correct signature for no-context delegate
CreateObjcInformalDelegateWithContext([Objc_NSMenuNumberOfItems], \
{ "numberOfItemsInMenu:": \
_TestDelegateMethodCalled }, \
"")
end handler
public handler TestInformalDelegateBadSignature()
if not the operating system is in ["mac", "ios"] then
skip test "objc informal delegate error returning invalid type" \
because "not implemented on" && the operating system
return
end if
MCUnitTestHandlerThrowsNamed(_TestInformalDelegateBadSignature, \
"livecode.objc.DelegateCallbackSignatureError", \
"Creating informal delegate throws error when callback has incorrect signature")
end handler
public handler TestInformalDelegateContextBadSignature()
if not the operating system is in ["mac", "ios"] then
skip test "objc informal delegate error omitting context param" \
because "not implemented on" && the operating system
return
end if
MCUnitTestHandlerThrowsNamed(_TestInformalDelegateContextBadSignature, \
"livecode.objc.DelegateCallbackSignatureError", \
"Creating informal delegate throws error when callback omits context parameter")
end handler
handler _TestFindBarViewCallback() returns ObjcId
end handler
handler _TestSetFindBarViewCallback(in pView as ObjcId) returns nothing
end handler
handler _TestIsFindBarVisibleCallback() returns CBool
end handler
handler _TestSetFindBarVisibleCallback(in pVisible as CBool) returns nothing
end handler
handler _TestFindBarViewDidChangeHeightCallback() returns nothing
end handler
private handler _TestDelegateMappedRequiredMethod()
CreateObjcDelegate("NSTextFinderBarContainer", \
{ "findBarView": \
_TestFindBarViewCallback, \
"setFindBarView:": \
_TestSetFindBarViewCallback, \
"isFindBarVisible": \
_TestIsFindBarVisibleCallback, \
"setFindBarVisible:": \
_TestSetFindBarVisibleCallback, \
"findBarViewDidChangeHeight": \
_TestFindBarViewDidChangeHeightCallback })
end handler
private handler _TestDelegateUnmappedRequiredMethod()
CreateObjcDelegate("NSTextFinderBarContainer", \
{ "isFindBarVisible": \
_TestIsFindBarVisibleCallback })
end handler
foreign handler NSTextFinderAlloc() returns ObjcRetainedId \
binds to "objc:AppKit.framework>NSTextFinder.+alloc"
foreign handler NSTextFinderInit(in pObj as ObjcId) returns ObjcId \
binds to "objc:NSTextFinder.-init"
public handler TestDelegateRequiredMethod()
if not the operating system is in ["mac", "ios"] then
skip test "objc delegate required method tests" \
because "not implemented on" && the operating system
return
end if
unsafe
// Ensure NSTextFinder is loaded
variable tFinder as ObjcObject
put NSTextFinderAlloc() into tFinder
put NSTextFinderInit(tFinder) into tFinder
end unsafe
MCUnitTestHandlerDoesntThrow(_TestDelegateMappedRequiredMethod, \
"Creating delegate doesn't throw error when required protocol method is mapped")
MCUnitTestHandlerThrowsNamed(_TestDelegateUnmappedRequiredMethod, \
"livecode.objc.DelegateMappingError", \
"Creating delegate throws error when required protocol method is not mapped")
end handler
end module