Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 50a5692

Browse files
committed
[[ ObjcFFI ]] Add dynamic instance method binding
Previously it was not possible to bind to certain instance methods, eg those defined in a protocol. This patch allows the `class` field of the binding string to be empty, in which case the method is resolved just as a selector, able to be called on an class instance.
1 parent ad8a9e2 commit 50a5692

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# LiveCode Builder Language
2+
## Objective-C dynamic instance method binding
3+
It is now possible to bind to instance methods dynamically, by leaving
4+
empty the name of the class in the binding string. In particular this
5+
enables calling protocol-defined instance methods on class instances,
6+
which was not previously possible.
7+
8+
For example, to directly call the `NSAlertDelegate` method
9+
`alertShowHelp:` on an `NSAlert` instance:
10+
11+
-- bind to delegate protocol method
12+
foreign handler NSAlertDelegateShowHelp(in pTarget as ObjcId, in pAlert as ObjcId) binds to "objc:.-alertShowHelp:"
13+
...
14+
-- call alertShowHelp on an NSAlert, passing the alert itself as the
15+
-- first parameter.
16+
NSAlertDelegateShowHelp(tAlert, tAlert)

libscript/src/script-instance.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -983,16 +983,28 @@ __MCScriptResolveForeignFunctionBindingForObjC(MCScriptInstanceRef p_instance,
983983
}
984984

985985
bool t_valid = true;
986+
bool t_is_dynamic = MCStringIsEmpty(*t_class);
986987

987988
/* Lookup the class, to make sure it exists. */
988989
Class t_objc_class = nullptr;
989990
if (t_valid)
990991
{
991-
t_objc_class = objc_getClass(*t_class_cstring);
992-
if (t_objc_class == nullptr)
992+
if (t_is_dynamic)
993993
{
994-
/* ERROR: should be class not found */
995-
t_valid = false;
994+
if (t_is_class)
995+
{
996+
/* ERROR: should be can't dynamically bind to class methods */
997+
t_valid = false;
998+
}
999+
}
1000+
else
1001+
{
1002+
t_objc_class = objc_getClass(*t_class_cstring);
1003+
if (t_objc_class == nullptr)
1004+
{
1005+
/* ERROR: should be class not found */
1006+
t_valid = false;
1007+
}
9961008
}
9971009
}
9981010

@@ -1007,7 +1019,7 @@ __MCScriptResolveForeignFunctionBindingForObjC(MCScriptInstanceRef p_instance,
10071019
}
10081020

10091021
/* Get the Method - either class or instance - from the class */
1010-
if (t_valid)
1022+
if (t_valid && !t_is_dynamic)
10111023
{
10121024
if (!t_is_class)
10131025
{

tests/lcb/vm/interop-objc.lcb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,38 @@ public handler TestObjcDynamicPropertyBinding()
200200
test "dynamic objc getter binds" when NSUserNotificationGetTitle is not nothing
201201
end handler
202202

203+
--------
204+
205+
foreign handler DynamicGetInt(in pObject as ObjcId) returns CInt \
206+
binds to "objc:.-intValue"
207+
208+
public handler TestDynamicInstanceMethodBinding()
209+
if not the operating system is in ["mac", "ios"] then
210+
skip test "obj-c dynamic instance method binding" \
211+
because "not implemented on" && the operating system
212+
return
213+
end if
214+
215+
unsafe
216+
variable tNumber as ObjcObject
217+
put NSNumberCreateWithInt(10) into tNumber
218+
219+
test "dynamic method binds" when DynamicGetInt is not nothing
220+
test "dynamic method call succeeds" when DynamicGetInt(tNumber) is 10
221+
end unsafe
222+
end handler
223+
224+
foreign handler DynamicAlloc() returns ObjcRetainedId \
225+
binds to "objc:.+alloc"
226+
227+
public handler TestDynamicClassMethodBindingFails()
228+
if not the operating system is in ["mac", "ios"] then
229+
skip test "obj-c dynamic class method binding fails" \
230+
because "not implemented on" && the operating system
231+
return
232+
end if
233+
234+
test "dynamic class method binding fails" when DynamicAlloc is nothing
235+
end handler
236+
203237
end module

0 commit comments

Comments
 (0)