@@ -206,6 +206,7 @@ MCTypeInfoRef kMCJavaNativeMethodCallErrorTypeInfo;
206206MCTypeInfoRef kMCJavaBindingStringSignatureErrorTypeInfo ;
207207MCTypeInfoRef kMCJavaCouldNotInitialiseJREErrorTypeInfo ;
208208MCTypeInfoRef kMCJavaJRENotSupportedErrorTypeInfo ;
209+ MCTypeInfoRef kMCJavaInterfaceCallbackSignatureErrorTypeInfo ;
209210
210211bool MCJavaPrivateErrorsInitialize ()
211212{
@@ -223,6 +224,9 @@ bool MCJavaPrivateErrorsInitialize()
223224
224225 if (!MCNamedErrorTypeInfoCreate (MCNAME (" livecode.java.JRENotSupported" ), MCNAME (" java" ), MCSTR (" Java Runtime Environment no supported with current configuration" ), kMCJavaJRENotSupportedErrorTypeInfo ))
225226 return false ;
227+
228+ if (!MCNamedErrorTypeInfoCreate (MCNAME (" livecode.java.InterfaceCallbackSignatureError" ), MCNAME (" java" ), MCSTR (" Handler for interface callback does not match callback signature" ), kMCJavaInterfaceCallbackSignatureErrorTypeInfo ))
229+ return false ;
226230
227231 return true ;
228232}
@@ -234,6 +238,7 @@ void MCJavaPrivateErrorsFinalize()
234238 MCValueRelease (kMCJavaBindingStringSignatureErrorTypeInfo );
235239 MCValueRelease (kMCJavaCouldNotInitialiseJREErrorTypeInfo );
236240 MCValueRelease (kMCJavaJRENotSupportedErrorTypeInfo );
241+ MCValueRelease (kMCJavaInterfaceCallbackSignatureErrorTypeInfo );
237242}
238243
239244bool MCJavaPrivateErrorThrow (MCTypeInfoRef p_error_type)
@@ -1204,24 +1209,162 @@ static jclass MCJavaPrivateFindClass(MCNameRef p_class_name)
12041209 return s_env->FindClass (*t_class_cstring);
12051210}
12061211
1207- bool MCJavaCreateInterfaceProxy (MCNameRef p_class_name, MCTypeInfoRef p_signature, void *p_method_id, void *r_result, void **p_args, uindex_t p_arg_count )
1212+ static bool __MCJavaIsHandlerSuitableForListener (MCNameRef p_class_name, MCValueRef p_handlers )
12081213{
1209- if (MCHandlerTypeInfoGetParameterCount (p_signature) != 1 )
1210- return false ;
1214+ jclass t_class_class = s_env->FindClass (" java/lang/Class" );
1215+ jmethodID t_get_methods = s_env->GetMethodID (t_class_class, " getMethods" ,
1216+ " ()[Ljava/lang/reflect/Method;" );
1217+
1218+ jclass t_class = MCJavaPrivateFindClass (p_class_name);
12111219
1212- MCValueRef t_handlers = *(static_cast <MCValueRef *>(p_args[0 ]));
1213- if (MCValueGetTypeCode (t_handlers) == kMCValueTypeCodeArray )
1220+ jobjectArray t_methods =
1221+ static_cast <jobjectArray>(s_env->CallObjectMethod (t_class,
1222+ t_get_methods));
1223+
1224+ jclass t_method_class = s_env->FindClass (" java/lang/reflect/Method" );
1225+
1226+ jmethodID t_get_parameters = s_env->GetMethodID (t_method_class,
1227+ " getParameterTypes" ,
1228+ " ()[Ljava/lang/Class;" );
1229+
1230+ // Lambda to check if a handler is suitable for the given method
1231+ auto t_check_handler = [&](MCHandlerRef p_handler, jobject p_method)
12141232 {
1215- // Array of handlers for interface proxy
1233+ MCTypeInfoRef t_type_info = MCValueGetTypeInfo (p_handler);
1234+
1235+ // Ensure all callback handler parameters are of JavaObject type
1236+ uindex_t t_param_count = MCHandlerTypeInfoGetParameterCount (t_type_info);
1237+
1238+ for (uindex_t i = 0 ; i < t_param_count; ++i)
1239+ {
1240+ if (!__MCTypeInfoConformsToJavaType (MCHandlerTypeInfoGetParameterType (t_type_info, i),
1241+ kMCJavaTypeObject ))
1242+ {
1243+ return MCErrorCreateAndThrowWithMessage (kMCJavaInterfaceCallbackSignatureErrorTypeInfo ,
1244+ MCSTR (" Callback handler %{handler} parameters must conform to JObject type" ),
1245+ " handler" , p_handler,
1246+ nullptr );
1247+ }
1248+ }
1249+
1250+ // Ensure the correct number of parameters
1251+ jobjectArray t_params =
1252+ static_cast <jobjectArray>(s_env->CallObjectMethod (p_method,
1253+ t_get_parameters));
1254+ uindex_t t_expected_param_count =
1255+ static_cast <uindex_t >(s_env->GetArrayLength (t_params));
1256+ if (t_param_count != t_expected_param_count)
1257+ {
1258+ MCAutoNumberRef t_exp;
1259+ if (!MCNumberCreateWithUnsignedInteger (t_expected_param_count,
1260+ &t_exp))
1261+ return false ;
1262+
1263+ return MCErrorCreateAndThrowWithMessage (kMCJavaInterfaceCallbackSignatureErrorTypeInfo ,
1264+ MCSTR (" Wrong number of parameters for callback handler %{handler}: expected %{number}" ),
1265+ " handler" , p_handler,
1266+ " number" , *t_exp,
1267+ nullptr );
1268+ }
1269+
1270+ return true ;
1271+ };
1272+
1273+ uindex_t t_num_methods = s_env->GetArrayLength (t_methods);
1274+ if (t_num_methods == 0 )
1275+ {
1276+ return MCErrorCreateAndThrowWithMessage (kMCJavaInterfaceCallbackSignatureErrorTypeInfo ,
1277+ MCSTR (" Target interface has no callback methods" ),
1278+ nullptr );
12161279 }
1217- else if (MCValueGetTypeCode (t_handlers) == kMCValueTypeCodeHandler )
1280+
1281+ if (MCValueGetTypeCode (p_handlers) == kMCValueTypeCodeArray )
12181282 {
1219- // Single handler for listener interface
1283+ // Collect all the method names of this interface
1284+ jmethodID t_get_method_name = s_env->GetMethodID (t_method_class,
1285+ " getName" ,
1286+ " ()Ljava/lang/String;" );
1287+ MCAutoStringRefArray t_names;
1288+ for (uindex_t i = 0 ; i < t_num_methods; i++)
1289+ {
1290+ jobject t_object = s_env->GetObjectArrayElement (t_methods, i);
1291+ jstring t_name =
1292+ static_cast <jstring>(s_env->CallObjectMethod (t_object,
1293+ t_get_method_name));
1294+ MCAutoStringRef t_name_stringref;
1295+ if (!__MCJavaStringFromJString (t_name, &t_name_stringref))
1296+ return false ;
1297+
1298+ if (!t_names.Push (*t_name_stringref))
1299+ return false ;
1300+ }
1301+
1302+ // Array of handlers for interface proxy
1303+ uintptr_t t_iterator = 0 ;
1304+ MCNameRef t_key;
1305+ MCValueRef t_value;
1306+ while (MCArrayIterate (static_cast <MCArrayRef>(p_handlers),
1307+ t_iterator, t_key, t_value))
1308+ {
1309+ MCStringRef t_match = nullptr ;
1310+ uindex_t j = 0 ;
1311+ for (; j < t_names.Size (); j++)
1312+ {
1313+ if (MCStringIsEqualTo (MCNameGetString (t_key),
1314+ t_names[j],
1315+ kMCStringOptionCompareCaseless ))
1316+ {
1317+ t_match = t_names[j];
1318+ break ;
1319+ }
1320+ }
1321+ if (t_match == nullptr )
1322+ {
1323+ // No method with matching name found
1324+ return MCErrorCreateAndThrowWithMessage (kMCJavaInterfaceCallbackSignatureErrorTypeInfo ,
1325+ MCSTR (" No callback method with name %{name}" ),
1326+ " name" , t_key,
1327+ nullptr );
1328+ }
1329+
1330+ // If we get here, we have a matching name, so check the handler
1331+ if (!t_check_handler (static_cast <MCHandlerRef>(t_value),
1332+ s_env->GetObjectArrayElement (t_methods, j)))
1333+ return false ;
1334+ }
1335+
1336+ // If we get here, then all handlers were assigned to valid callbacks
1337+ // in the interface
1338+ return true ;
1339+
12201340 }
1221- else
1341+ else if ( MCValueGetTypeCode (p_handlers) == kMCValueTypeCodeHandler )
12221342 {
1223- return false ;
1343+ // Only one handler provided - ensure there is only one callback
1344+ if (t_num_methods != 1 )
1345+ {
1346+ return MCErrorCreateAndThrowWithMessage (kMCJavaInterfaceCallbackSignatureErrorTypeInfo ,
1347+ MCSTR (" Ambiguous callback assignment - target interface has multiple callback methods" ),
1348+ nullptr );
1349+ }
1350+
1351+ return t_check_handler (static_cast <MCHandlerRef>(p_handlers),
1352+ s_env->GetObjectArrayElement (t_methods, 0 ));
12241353 }
1354+
1355+ // Value was not of correct type
1356+ return false ;
1357+ }
1358+
1359+ bool MCJavaCreateInterfaceProxy (MCNameRef p_class_name, MCTypeInfoRef p_signature, void *p_method_id, void *r_result, void **p_args, uindex_t p_arg_count)
1360+ {
1361+ if (MCHandlerTypeInfoGetParameterCount (p_signature) != 1 )
1362+ return false ;
1363+
1364+ MCValueRef t_handlers = *(static_cast <MCValueRef *>(p_args[0 ]));
1365+
1366+ if (!__MCJavaIsHandlerSuitableForListener (p_class_name, t_handlers))
1367+ return false ;
12251368
12261369 jclass t_inv_handler_class =
12271370 MCJavaPrivateFindClass (MCNAME (" com.runrev.android.LCBInvocationHandler" ));
@@ -1240,6 +1383,7 @@ bool MCJavaCreateInterfaceProxy(MCNameRef p_class_name, MCTypeInfoRef p_signatur
12401383 MCJavaObjectRef t_result_value;
12411384 if (!MCJavaObjectCreateNullableGlobalRef (t_proxy, t_result_value))
12421385 return false ;
1386+
12431387 *(static_cast <MCJavaObjectRef *>(r_result)) = t_result_value;
12441388 return true ;
12451389}
0 commit comments