Skip to content

Commit cfff134

Browse files
committed
[[ ObjcDelegates ]] Check protocol mappings more strictly
- Ensure that required protocol methods are present in callback mapping - Ensure that callback signatures exhaust typecodes as required
1 parent 1abf75d commit cfff134

1 file changed

Lines changed: 73 additions & 23 deletions

File tree

libfoundation/src/foundation-objc.mm

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,12 @@ static bool _convert_type(char p_type, void *p_buffer, MCValueRef& r_value)
536536
return true;
537537
}
538538

539-
static bool _get_method_description(Protocol *p_protocol, SEL p_selector, objc_method_description& r_desc)
539+
static bool _get_method_description(Protocol *p_protocol, SEL p_selector, BOOL p_instance, objc_method_description& r_desc)
540540
{
541541
objc_method_description t_desc =
542-
protocol_getMethodDescription(p_protocol, p_selector, NO, YES);
542+
protocol_getMethodDescription(p_protocol, p_selector, NO, p_instance);
543543
if (t_desc.types == nullptr)
544-
t_desc = protocol_getMethodDescription(p_protocol, p_selector, YES, YES);
544+
t_desc = protocol_getMethodDescription(p_protocol, p_selector, YES, p_instance);
545545

546546
if (t_desc.types == nullptr)
547547
return false;
@@ -674,6 +674,23 @@ static bool _get_informal_protocol_method_types(MCStringRef p_selector, MCArrayR
674674
return MCStringCopy(*t_types, r_types);
675675
}
676676

677+
static MCHandlerRef _fetch_handler_for_selector(SEL p_selector, MCArrayRef p_mapping)
678+
{
679+
MCAutoStringRef t_selector;
680+
if (!MCStringCreateWithCString(sel_getName(p_selector), &t_selector))
681+
return nullptr;
682+
683+
MCNewAutoNameRef t_key;
684+
if (!MCNameCreate(*t_selector, &t_key))
685+
return nullptr;
686+
687+
MCValueRef t_handler;
688+
if (!MCArrayFetchValue(p_mapping, false, *t_key, t_handler))
689+
return nullptr;
690+
691+
return static_cast<MCHandlerRef>(t_handler);
692+
}
693+
677694
@implementation com_livecode_MCObjcFormalDelegate
678695
- (id)initForProtocol:(Protocol *)aProtocol withHandlerMapping:(MCArrayRef)aHandlerMapping withContext:(MCValueRef)aContext
679696
{
@@ -696,7 +713,8 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
696713
// Return the method signature of the protocol method we have an LCB
697714
// implementation for. This causes forwardInvocation to be called.
698715
objc_method_description t_desc;
699-
if (!_get_method_description(m_protocol, aSelector, t_desc))
716+
if (!_get_method_description(m_protocol, aSelector, YES, t_desc) &&
717+
!_get_method_description(m_protocol, aSelector, NO, t_desc))
700718
return nullptr;
701719

702720
return [NSMethodSignature signatureWithObjCTypes:t_desc.types];
@@ -766,19 +784,7 @@ - (void)dealloc
766784

767785
-(MCHandlerRef)handlerFromSelector:(SEL)aSelector
768786
{
769-
MCAutoStringRef t_selector;
770-
if (!MCStringCreateWithCString(sel_getName(aSelector), &t_selector))
771-
return nullptr;
772-
773-
MCNewAutoNameRef t_key;
774-
if (!MCNameCreate(*t_selector, &t_key))
775-
return nullptr;
776-
777-
MCValueRef t_handler;
778-
if (!MCArrayFetchValue(m_handlers, false, *t_key, t_handler))
779-
return nullptr;
780-
781-
return static_cast<MCHandlerRef>(t_handler);
787+
return _fetch_handler_for_selector(aSelector, m_handlers);
782788
}
783789

784790
-(BOOL)respondsToSelector:(SEL)aSelector
@@ -944,8 +950,10 @@ static bool __MCTypeInfoConformsToObjcTypeCode(MCTypeInfoRef p_typeinfo, const c
944950
return true;
945951
}
946952

947-
static bool __HandlerConformsToMethodSignature(MCHandlerRef p_handler, const char* p_typecodes, MCValueRef p_context)
953+
static bool __HandlerConformsToMethodSignature(MCHandlerRef p_handler, const char* p_typecodes, bool p_is_instance, MCValueRef p_context)
948954
{
955+
/* TODO: Check how things work with protocol class methods */
956+
949957
MCTypeInfoRef t_handler_typeinfo = MCValueGetTypeInfo(p_handler);
950958

951959
// Check return type, the first code in the signature
@@ -988,6 +996,10 @@ static bool __HandlerConformsToMethodSignature(MCHandlerRef p_handler, const cha
988996
}
989997
}
990998

999+
// Ensure all the parameters required by the typecode are present
1000+
if (t_handler_conforms)
1001+
t_handler_conforms = strlen(p_typecodes) == 0;
1002+
9911003
if (!t_handler_conforms)
9921004
{
9931005
return MCErrorCreateAndThrowWithMessage(kMCObjcDelegateCallbackSignatureErrorTypeInfo,
@@ -1004,7 +1016,7 @@ static bool __HandlerConformsToInformalProtocolMethod(MCHandlerRef p_handler, MC
10041016
MCAutoStringRef t_types;
10051017
if (!_get_informal_protocol_method_types(MCNameGetString(p_selector), p_protocol, &t_types))
10061018
{
1007-
return MCErrorCreateAndThrowWithMessage(kMCObjcDelegateCallbackSignatureErrorTypeInfo,
1019+
return MCErrorCreateAndThrowWithMessage(kMCObjcDelegateMappingErrorTypeInfo,
10081020
MCSTR("Invalid protocol definition for selector %{sel}"),
10091021
"sel", p_selector,
10101022
nullptr);
@@ -1014,7 +1026,7 @@ static bool __HandlerConformsToInformalProtocolMethod(MCHandlerRef p_handler, MC
10141026
if (!t_typecodes.Lock(*t_types))
10151027
return false;
10161028

1017-
return __HandlerConformsToMethodSignature(p_handler, *t_typecodes, p_context);
1029+
return __HandlerConformsToMethodSignature(p_handler, *t_typecodes, false, p_context);
10181030
}
10191031

10201032
static bool __HandlerConformsToProtocolMethod(MCHandlerRef p_handler, MCStringRef t_method, MCValueRef p_context, Protocol *p_protocol)
@@ -1025,16 +1037,44 @@ static bool __HandlerConformsToProtocolMethod(MCHandlerRef p_handler, MCStringRe
10251037

10261038
SEL t_proxy_selector = NSSelectorFromString((NSString *)*t_selector_cfstring);
10271039

1040+
bool t_is_instance = false;
10281041
objc_method_description t_desc;
1029-
if (!_get_method_description(p_protocol, t_proxy_selector, t_desc))
1042+
if (!_get_method_description(p_protocol, t_proxy_selector, YES, t_desc))
1043+
t_is_instance = true;
1044+
1045+
if (t_is_instance && !_get_method_description(p_protocol, t_proxy_selector, NO, t_desc))
10301046
{
1031-
return MCErrorCreateAndThrowWithMessage(kMCObjcDelegateCallbackSignatureErrorTypeInfo,
1047+
return MCErrorCreateAndThrowWithMessage(kMCObjcDelegateMappingErrorTypeInfo,
10321048
MCSTR("Protocol method %{method} not found"),
10331049
"method", t_method,
10341050
nullptr);
10351051
}
10361052

1037-
return __HandlerConformsToMethodSignature(p_handler, t_desc.types, p_context);
1053+
return __HandlerConformsToMethodSignature(p_handler, t_desc.types, t_is_instance, p_context);
1054+
}
1055+
1056+
static bool __AllRequiredProtocolMethodsImplemented(MCArrayRef p_handler_mapping, BOOL p_instance, Protocol *p_protocol)
1057+
{
1058+
uindex_t t_out_count = 0;
1059+
objc_method_description *t_required_methods =
1060+
protocol_copyMethodDescriptionList(p_protocol, YES, p_instance, &t_out_count);
1061+
for (uindex_t i = 0; i < t_out_count; ++i)
1062+
{
1063+
if (_fetch_handler_for_selector(t_required_methods->name,
1064+
p_handler_mapping) == nullptr)
1065+
{
1066+
MCAutoStringRef t_selector;
1067+
if (!MCStringCreateWithCString(sel_getName(t_required_methods->name), &t_selector))
1068+
return false;
1069+
1070+
return MCErrorCreateAndThrowWithMessage(kMCObjcDelegateMappingErrorTypeInfo,
1071+
MCSTR("No mapping found for required protocol method %{method}"),
1072+
"method", *t_selector,
1073+
nullptr);
1074+
}
1075+
t_required_methods++;
1076+
}
1077+
return true;
10381078
}
10391079

10401080
extern "C" MC_DLLEXPORT_DEF
@@ -1066,6 +1106,12 @@ bool MCObjcCreateDelegateWithContext(MCStringRef p_protocol_name, MCArrayRef p_h
10661106
return false;
10671107
}
10681108

1109+
// Check all the required protocol methods have callbacks implemented
1110+
if (!__AllRequiredProtocolMethodsImplemented(p_handler_mapping, YES, t_protocol))
1111+
return false;
1112+
if (!__AllRequiredProtocolMethodsImplemented(p_handler_mapping, NO, t_protocol))
1113+
return false;
1114+
10691115
com_livecode_MCObjcFormalDelegate *t_proxy =
10701116
[[com_livecode_MCObjcFormalDelegate alloc] initForProtocol: t_protocol withHandlerMapping:p_handler_mapping withContext:p_context];
10711117

@@ -1177,12 +1223,16 @@ bool MCObjcCreateInformalDelegate(MCProperListRef p_foreign_handlers, MCArrayRef
11771223
////////////////////////////////////////////////////////////////////////////////
11781224

11791225
MCTypeInfoRef kMCObjcDelegateCallbackSignatureErrorTypeInfo;
1226+
MCTypeInfoRef kMCObjcDelegateMappingErrorTypeInfo;
11801227

11811228
bool MCObjcErrorsInitialize()
11821229
{
11831230
if (!MCNamedErrorTypeInfoCreate(MCNAME("livecode.objc.DelegateCallbackSignatureError"), MCNAME("objc"), MCSTR("Could not create delegate"), kMCObjcDelegateCallbackSignatureErrorTypeInfo))
11841231
return false;
11851232

1233+
if (!MCNamedErrorTypeInfoCreate(MCNAME("livecode.objc.DelegateMappingError"), MCNAME("objc"), MCSTR("Could not create delegate"), kMCObjcDelegateMappingErrorTypeInfo))
1234+
return false;
1235+
11861236
return true;
11871237
}
11881238

0 commit comments

Comments
 (0)