diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index 88263d3f4..4adda8d5d 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -351,3 +351,21 @@ TEST_F(DynInterfaceTests, testEmptyMethod) { dynInterface_destroy(dynIntf); } +TEST_F(DynInterfaceTests, testFindMethod) { + int status = 0; + dyn_interface_type *dynIntf = NULL; + FILE *desc = fopen("descriptors/example1.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(0, status); + fclose(desc); + + const struct method_entry* mInfo = dynInterface_findMethod(dynIntf, "add(DD)D"); + ASSERT_TRUE(mInfo != NULL); + ASSERT_STREQ("add(DD)D", mInfo->id); + + mInfo = dynInterface_findMethod(dynIntf, "add(D)D"); + ASSERT_TRUE(mInfo == NULL); + + dynInterface_destroy(dynIntf); +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index f74fffdfc..aa92c5326 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -21,9 +21,9 @@ #include #include -#include "celix_err.h" extern "C" { +#include #include #include #include @@ -37,7 +37,11 @@ extern "C" { #include "dyn_function.h" #include "json_serializer.h" #include "json_rpc.h" +#include "celix_compiler.h" #include "celix_errno.h" +#include "celix_err.h" + +#include void prepareTest(void) { @@ -372,16 +376,8 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); + dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc; + assert(func != nullptr); const char *reply = R"({"r":{"input":[1.0,2.0],"max":2.0,"average":1.5,"min":1.0}})"; @@ -413,16 +409,8 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); + dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc; + assert(func != nullptr); const char *reply = R"({})"; @@ -451,16 +439,9 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "add") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); + + dyn_function_type *func = dynInterface_findMethod(intf, "add(DD)D")->dynFunc; + assert(func != nullptr); const char *reply = R"({"e":33554433})"; @@ -509,19 +490,8 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "items") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); - - //dyn_type *arg = dynFunction_argumentTypeForIndex(func, 1); - //dynType_print(arg, stdout); + dyn_function_type *func = dynInterface_findMethod(intf, "example1")->dynFunc; + assert(func != nullptr); const char *reply = R"({"r":[{"a":1.0,"b":1.5},{"a":2.0,"b":2.5}]})"; @@ -561,16 +531,8 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "action") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); + dyn_function_type *func = dynInterface_findMethod(intf, "action(V)")->dynFunc; + assert(func != nullptr); const char *reply = R"({})"; @@ -613,17 +575,8 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "getName") == 0) { - func = entry->dynFunc; - break; - } - } - - ASSERT_TRUE(func != nullptr); + dyn_function_type *func = dynInterface_findMethod(intf, "getName(V)t")->dynFunc; + assert(func != nullptr); const char *reply = R"({"r": "this is a test string"})"; char *result = nullptr; @@ -633,11 +586,9 @@ extern "C" { args[0] = nullptr; args[1] = &out; - if (func != nullptr) { // Check needed just to satisfy Coverity - int rsErrno = 0; - jsonRpc_handleReply(func, reply, args, &rsErrno); - ASSERT_EQ(0, rsErrno); - } + int rsErrno = 0; + jsonRpc_handleReply(func, reply, args, &rsErrno); + ASSERT_EQ(0, rsErrno); ASSERT_STREQ("this is a test string", result); @@ -746,7 +697,7 @@ TEST_F(JsonRpcTests, handleTestOut) { handleTestOut(); } -TEST_F(JsonRpcTests, handleTestNullOut) { +TEST_F(JsonRpcTests, handleTestNullOutResult) { dyn_interface_type *intf = nullptr; FILE *desc = fopen("descriptors/example1.descriptor", "r"); ASSERT_TRUE(desc != nullptr); @@ -754,17 +705,39 @@ TEST_F(JsonRpcTests, handleTestNullOut) { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) { - func = entry->dynFunc; - break; - } - } + dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc; ASSERT_TRUE(func != nullptr); + const char *reply = R"({"r":null})"; + + void *args[3]; + args[0] = nullptr; + args[1] = nullptr; + args[2] = nullptr; + + struct tst_StatsResult *result = nullptr; + void *out = &result; + args[2] = &out; + + int rsErrno = 0; + rc = jsonRpc_handleReply(func, reply, args, &rsErrno); + ASSERT_EQ(0, rc); + ASSERT_EQ(0, rsErrno); + ASSERT_EQ(nullptr, result); + dynInterface_destroy(intf); +} + +TEST_F(JsonRpcTests, handleTestNullOut) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/example1.descriptor", "r"); + ASSERT_TRUE(desc != nullptr); + int rc = dynInterface_parse(desc, &intf); + ASSERT_EQ(0, rc); + fclose(desc); + + dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc; + assert(func != nullptr); + const char *reply = R"({"r":{"input":[1.0,2.0],"max":2.0,"average":1.5,"min":1.0}})"; void *args[3]; @@ -813,6 +786,32 @@ TEST_F(JsonRpcTests, callOut) { callTestOutput(); } + +TEST_F(JsonRpcTests, callOutNullResult) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/example1.descriptor", "r"); + ASSERT_TRUE(desc != nullptr); + int rc = dynInterface_parse(desc, &intf); + ASSERT_EQ(0, rc); + fclose(desc); + + char *result = nullptr; + tst_serv serv {nullptr, nullptr, nullptr, nullptr, [](void*, struct tst_seq, struct tst_StatsResult **out)->int { + assert(out != nullptr); + assert(*out == nullptr); + *out = nullptr; + return 0; + }}; + + rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": [[1.0,2.0]]})", &result); + ASSERT_EQ(0, rc); + + json_auto_t* replyJson = json_loads(result, JSON_DECODE_ANY, nullptr); + EXPECT_TRUE(json_is_null(json_object_get(replyJson, "r"))); + free(result); + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, callOutReference) { dyn_interface_type *intf = nullptr; FILE *desc = fopen("descriptors/example7.descriptor", "r"); @@ -844,14 +843,63 @@ TEST_F(JsonRpcTests, handleOutSeq) { handleTestOutputSequence(); } -TEST_F(JsonRpcTests, callTestOutChar) { +TEST_F(JsonRpcTests, callTestOutText) { callTestOutChar(); } -TEST_F(JsonRpcTests, handleOutChar) { +TEST_F(JsonRpcTests, callTestOutNullTextResult) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/example4.descriptor", "r"); + ASSERT_TRUE(desc != nullptr); + int rc = dynInterface_parse(desc, &intf); + ASSERT_EQ(0, rc); + fclose(desc); + + char *result = nullptr; + tst_serv_example4 serv {nullptr, [](void *, char** result)->int { + *result = nullptr; + return 0; + }, nullptr, nullptr}; + + rc = jsonRpc_call(intf, &serv, R"({"m": "getName(V)t", "a": []})", &result); + ASSERT_EQ(0, rc); + + json_auto_t* replyJson = json_loads(result, JSON_DECODE_ANY, nullptr); + EXPECT_TRUE(json_is_null(json_object_get(replyJson, "r"))); + free(result); + dynInterface_destroy(intf); +} + +TEST_F(JsonRpcTests, handleOutText) { handleTestOutChar(); } +TEST_F(JsonRpcTests, handleNullOutTextResult) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/example4.descriptor", "r"); + ASSERT_TRUE(desc != nullptr); + int rc = dynInterface_parse(desc, &intf); + ASSERT_EQ(0, rc); + fclose(desc); + + dyn_function_type *func = dynInterface_findMethod(intf, "getName(V)t")->dynFunc; + assert(func != nullptr); + + const char *reply = R"({"r":null})"; + char *result = nullptr; + void *out = &result; + + void *args[2]; + args[0] = nullptr; + args[1] = &out; + + int rsErrno = 0; + jsonRpc_handleReply(func, reply, args, &rsErrno); + ASSERT_EQ(0, rsErrno); + EXPECT_EQ(nullptr, result); + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, handleInvalidOutChar) { dyn_interface_type *intf = nullptr; @@ -861,17 +909,8 @@ TEST_F(JsonRpcTests, handleInvalidOutChar) { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head* head = dynInterface_methods(intf); - dyn_function_type *func = nullptr; - struct method_entry *entry = nullptr; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(dynFunction_getName(entry->dynFunc), "getName") == 0) { - func = entry->dynFunc; - break; - } - } - - ASSERT_TRUE(func != nullptr); + dyn_function_type *func = dynInterface_findMethod(intf, "getName(V)t")->dynFunc; + assert(func != nullptr); const char *reply = R"({"r": 12345})"; char *result = nullptr; diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index 35e15d2df..b548fdb1e 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -35,7 +35,7 @@ extern "C" { * Dyn function argument meta (am) as meta info, with the following possible values * am=handle #void pointer for the handle * am=pre #output pointer with memory pre-allocated, it should be pointer to trivial types, check `dynType_isTrivial` for more info. - * am=out #output pointer, it should be pointer to text or double pointer to serializable types + * am=out #output pointer, it should be pointers to text or pointer to serializable point type * * Without meta info the argument is considered to be a standard argument, which can be of any serializable type. * diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index fe0adf8bd..fa9dad443 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -136,6 +136,16 @@ CELIX_DFI_EXPORT const struct methods_head* dynInterface_methods(const dyn_inter */ CELIX_DFI_EXPORT int dynInterface_nrOfMethods(const dyn_interface_type* intf); +/** + * @brief Finds and returns the method_entry structure for a given method id in the dynamic interface type instance. + * The dynamic interface type instance is the owner of the returned method_entry structure and it should not be freed. + * + * @param[in] intf The dynamic interface type instance. + * @param[in] id The id of the method to find. + * @return The method_entry structure for the given method id, or NULL if no matching method is found. + */ +CELIX_DFI_EXPORT const struct method_entry* dynInterface_findMethod(const dyn_interface_type* intf, const char* id); + #ifdef __cplusplus } diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 4908d5382..dfec535c2 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -213,3 +213,13 @@ int dynInterface_nrOfMethods(const dyn_interface_type* intf) { struct method_entry* last = TAILQ_LAST(&intf->methods, methods_head); return last == NULL ? 0 : (last->index+1); } + +const struct method_entry* dynInterface_findMethod(const dyn_interface_type* intf, const char* id) { + const struct method_entry* entry = NULL; + TAILQ_FOREACH(entry, &intf->methods, entries) { + if (strcmp(entry->id, id) == 0) { + break; + } + } + return entry; +} \ No newline at end of file diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 2833045c3..707d81bc3 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -61,27 +61,16 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ return ERROR; } - const struct methods_head* methods = dynInterface_methods(intf); - struct method_entry* entry = NULL; - struct method_entry* method = NULL; - TAILQ_FOREACH(entry, methods, entries) { - if (strcmp(sig, entry->id) == 0) { - method = entry; - break; - } - } - + const struct method_entry* method = dynInterface_findMethod(intf, sig); if (method == NULL) { celix_err_pushf("Cannot find method with sig '%s'", sig); return ERROR; } struct generic_service_layout* serv = service; - void* handle = serv->handle; - void (*fp)(void) = serv->methods[method->index]; - dyn_function_type* func = method->dynFunc; - int nrOfArgs = dynFunction_nrOfArguments(method->dynFunc); + const dyn_function_type* func = method->dynFunc; + int nrOfArgs = dynFunction_nrOfArguments(func); void* args[nrOfArgs]; json_t* value = NULL; @@ -91,7 +80,6 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ void* ptr = NULL; void* ptrToPtr = &ptr; - void* instPtr = NULL; //setup and deserialize input for (i = 0; i < nrOfArgs; ++i) { @@ -106,15 +94,16 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ void* inst = NULL; const dyn_type *subType = dynType_typedPointer_getTypedType(argType); dynType_alloc(subType, &inst); - instPtr = inst; - args[i] = &instPtr; + ptr = inst; + args[i] = &ptr; } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { args[i] = &ptrToPtr; } else if (meta == DYN_FUNCTION_ARGUMENT_META__HANDLE) { - args[i] = &handle; + args[i] = &serv->handle; } if (status != OK) { + // FIXME: part of args uninitialized break; } } @@ -123,7 +112,7 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ ffi_sarg returnVal = 1; if (status == OK) { - status = dynFunction_call(func, fp, (void *) &returnVal, args); + status = dynFunction_call(func, serv->methods[method->index], (void *) &returnVal, args); } int funcCallStatus = (int)returnVal; @@ -158,32 +147,18 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ status = jsonSerializer_serializeJson(argType, args[i], &jsonResult); } const dyn_type* subType = dynType_typedPointer_getTypedType(argType); - void** ptrToInst = (void**)args[i]; - dynType_free(subType, *ptrToInst); + dynType_free(subType, ptr); } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - if (funcCallStatus == 0 && ptr != NULL) { - const dyn_type* typedType = NULL; - if (status == OK) { - typedType = dynType_typedPointer_getTypedType(argType); - } - if (status == OK && dynType_descriptorType(typedType) == 't') { - status = jsonSerializer_serializeJson(typedType, (void*) &ptr, &jsonResult); - free(ptr); - } else { - const dyn_type* typedTypedType = NULL; - if (status == OK) { - typedTypedType = dynType_typedPointer_getTypedType(typedType); - } - - if(status == OK){ - status = jsonSerializer_serializeJson(typedTypedType, ptr, &jsonResult); - } - - if (status == OK) { - dynType_free(typedTypedType, ptr); - } - } - + const dyn_type* typedType = NULL; + typedType = dynType_typedPointer_getTypedType(argType); + if (funcCallStatus == 0 && status == OK) { + status = jsonSerializer_serializeJson(typedType, (void*) &ptr, &jsonResult); + } + if (dynType_descriptorType(typedType) == 't') { + free(ptr); + } else { + const dyn_type* typedTypedType = dynType_typedPointer_getTypedType(typedType); + dynType_free(typedTypedType, ptr); } } @@ -332,7 +307,11 @@ int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* } else { const dyn_type* subSubType = dynType_typedPointer_getTypedType(subType); void*** out = (void ***) lastArg; - status = jsonSerializer_deserializeJson(subSubType, result, *out); + if (json_is_null(result)) { + **out = NULL; + } else { + status = jsonSerializer_deserializeJson(subSubType, result, *out); + } } }