From 3e61888bf48fc2684fc10fc388d200d4914acaf6 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 13 Dec 2023 20:37:38 +0800 Subject: [PATCH 01/62] Replace dfi_log_util.h by celix_err.h. --- .../src/pubsub_json_serialization_provider.c | 13 ---- .../gtest/src/PubSubMatchingTestSuite.cpp | 12 --- .../PubSubSerializationHandlerTestSuite.cc | 12 --- .../src/pubsub_serialization_provider.c | 16 ---- libs/dfi/gtest/src/dyn_closure_tests.cpp | 14 ---- libs/dfi/gtest/src/dyn_function_tests.cpp | 16 ---- libs/dfi/gtest/src/dyn_interface_tests.cpp | 15 ---- libs/dfi/gtest/src/dyn_message_tests.cpp | 14 ---- libs/dfi/gtest/src/dyn_type_tests.cpp | 13 ---- libs/dfi/gtest/src/json_serializer_tests.cpp | 15 ---- libs/dfi/include/dfi_log_util.h | 78 ------------------- libs/dfi/include/dyn_common.h | 4 - libs/dfi/include/dyn_function.h | 3 - libs/dfi/include/dyn_interface.h | 3 - libs/dfi/include/dyn_message.h | 3 - libs/dfi/include/dyn_type.h | 4 - libs/dfi/include/json_rpc.h | 4 - libs/dfi/include/json_serializer.h | 4 - libs/dfi/src/dyn_common.c | 9 +-- libs/dfi/src/dyn_function.c | 15 ++-- libs/dfi/src/dyn_interface.c | 20 ++--- libs/dfi/src/dyn_message.c | 19 +++-- libs/dfi/src/dyn_type.c | 62 +++++++-------- libs/dfi/src/dyn_type_common.c | 6 +- libs/dfi/src/dyn_type_common.h | 3 - libs/dfi/src/json_rpc.c | 24 +++--- libs/dfi/src/json_serializer.c | 29 ++++--- 27 files changed, 88 insertions(+), 342 deletions(-) delete mode 100644 libs/dfi/include/dfi_log_util.h diff --git a/bundles/pubsub/pubsub_serializer_json/src/pubsub_json_serialization_provider.c b/bundles/pubsub/pubsub_serializer_json/src/pubsub_json_serialization_provider.c index 0e7874760..28a87f9f4 100644 --- a/bundles/pubsub/pubsub_serializer_json/src/pubsub_json_serialization_provider.c +++ b/bundles/pubsub/pubsub_serializer_json/src/pubsub_json_serialization_provider.c @@ -29,18 +29,6 @@ #include "celix_log_helper.h" #include "pubsub_message_serialization_service.h" -static void dfi_log(void *handle, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - celix_log_helper_t *log = handle; - char *logStr = NULL; - va_start(ap, msg); - vasprintf(&logStr, msg, ap); - va_end(ap); - celix_logHelper_log(log, level, "FILE:%s, LINE:%i, MSG:%s", file, line, logStr); - free(logStr); -} - - static celix_status_t pubsub_jsonSerializationProvider_serialize(pubsub_serialization_entry_t* entry, const void* msg, struct iovec** output, size_t* outputIovLen) { celix_status_t status = CELIX_SUCCESS; @@ -108,7 +96,6 @@ static void pubsub_jsonSerializationProvider_freeDeserializeMsg(pubsub_serializa pubsub_serialization_provider_t* pubsub_jsonSerializationProvider_create(celix_bundle_context_t* ctx) { pubsub_serialization_provider_t* provider = pubsub_serializationProvider_create(ctx, "json", true, 0, pubsub_jsonSerializationProvider_serialize, pubsub_jsonSerializationProvider_freeSerializeMsg, pubsub_jsonSerializationProvider_deserialize, pubsub_jsonSerializationProvider_freeDeserializeMsg); - jsonSerializer_logSetup(dfi_log, pubsub_serializationProvider_getLogHelper(provider), 1);; return provider; } diff --git a/bundles/pubsub/pubsub_utils/gtest/src/PubSubMatchingTestSuite.cpp b/bundles/pubsub/pubsub_utils/gtest/src/PubSubMatchingTestSuite.cpp index a3b2bb82d..1c0f421e8 100644 --- a/bundles/pubsub/pubsub_utils/gtest/src/PubSubMatchingTestSuite.cpp +++ b/bundles/pubsub/pubsub_utils/gtest/src/PubSubMatchingTestSuite.cpp @@ -33,16 +33,6 @@ #include "pubsub_endpoint.h" #include "pubsub_message_serialization_marker.h" -static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - class PubSubMatchingTestSuite : public ::testing::Test { public: PubSubMatchingTestSuite() { @@ -54,8 +44,6 @@ class PubSubMatchingTestSuite : public ::testing::Test { ctx = std::shared_ptr{ctxPtr, [](auto*){/*nop*/}}; bndId = celix_bundleContext_installBundle(ctx.get(), MATCHING_BUNDLE, true); - - dynMessage_logSetup(stdLog, NULL, 1); } ~PubSubMatchingTestSuite() override { diff --git a/bundles/pubsub/pubsub_utils/gtest/src/PubSubSerializationHandlerTestSuite.cc b/bundles/pubsub/pubsub_utils/gtest/src/PubSubSerializationHandlerTestSuite.cc index 2522fcbdd..d8f466d1c 100644 --- a/bundles/pubsub/pubsub_utils/gtest/src/PubSubSerializationHandlerTestSuite.cc +++ b/bundles/pubsub/pubsub_utils/gtest/src/PubSubSerializationHandlerTestSuite.cc @@ -30,16 +30,6 @@ #include "celix_framework_factory.h" #include "celix_constants.h" -static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - class PubSubSerializationHandlerTestSuite : public ::testing::Test { public: PubSubSerializationHandlerTestSuite() { @@ -50,8 +40,6 @@ class PubSubSerializationHandlerTestSuite : public ::testing::Test { fw = std::shared_ptr{fwPtr, [](auto* f) {celix_frameworkFactory_destroyFramework(f);}}; ctx = std::shared_ptr{ctxPtr, [](auto*){/*nop*/}}; - dynMessage_logSetup(stdLog, NULL, 1); - msgSerSvc.handle = this; msgSerSvc.serialize = [](void* handle, const void*, struct iovec**, size_t*) -> celix_status_t { auto* suite = static_cast(handle); diff --git a/bundles/pubsub/pubsub_utils/src/pubsub_serialization_provider.c b/bundles/pubsub/pubsub_utils/src/pubsub_serialization_provider.c index 3243fde65..b7d491781 100644 --- a/bundles/pubsub/pubsub_utils/src/pubsub_serialization_provider.c +++ b/bundles/pubsub/pubsub_utils/src/pubsub_serialization_provider.c @@ -81,18 +81,6 @@ struct pubsub_serialization_provider { celix_array_list_t *serializationSvcEntries; //key = pubsub_serialization_entry; }; -static void dfi_log(void *handle, int level, const char *file, int line, const char *msg, ...) { - (void)level; - va_list ap; - pubsub_serialization_provider_t *provider = handle; - char *logStr = NULL; - va_start(ap, msg); - vasprintf(&logStr, msg, ap); - va_end(ap); - celix_logHelper_log(provider->logHelper, CELIX_LOG_LEVEL_WARNING, "FILE:%s, LINE:%i, MSG:%s", file, line, logStr); - free(logStr); -} - static descriptor_type_e getDescriptorType(const char* filename) { if (strstr(filename, ".descriptor")) { return FIT_DESCRIPTOR; @@ -610,10 +598,6 @@ pubsub_serialization_provider_t *pubsub_serializationProvider_create( provider->freeDeserializeMsg = freeDeserializeMsg; provider->logHelper = celix_logHelper_create(ctx, "celix_pubsub_serialization_provider"); - dynFunction_logSetup(dfi_log, provider, 1); - dynType_logSetup(dfi_log, provider, 1); - dynCommon_logSetup(dfi_log, provider, 1); - { //Start bundle tracker and register pubsub_message_serialization services celix_bundle_tracking_options_t opts = CELIX_EMPTY_BUNDLE_TRACKING_OPTIONS; diff --git a/libs/dfi/gtest/src/dyn_closure_tests.cpp b/libs/dfi/gtest/src/dyn_closure_tests.cpp index 819a4aa0f..54bea43df 100644 --- a/libs/dfi/gtest/src/dyn_closure_tests.cpp +++ b/libs/dfi/gtest/src/dyn_closure_tests.cpp @@ -35,16 +35,6 @@ extern "C" { static int g_count; -static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - #define EXAMPLE1_DESCRIPTOR "example(III)I" static void example1_binding(void*, void* args[], void *out) { int32_t a = *((int32_t *)args[0]); @@ -151,10 +141,6 @@ static void tests() { class DynClosureTests : public ::testing::Test { public: DynClosureTests() { - int lvl = 1; - dynFunction_logSetup(stdLog, NULL, lvl); - dynType_logSetup(stdLog, NULL, lvl); - dynCommon_logSetup(stdLog, NULL, lvl); g_count = 0; } ~DynClosureTests() override { diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 7f0ba0dc8..6164cbcc7 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -19,24 +19,12 @@ #include "gtest/gtest.h" -#include #include "dyn_example_functions.h" #include "dyn_common.h" #include "dyn_function.h" #include -static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - - #define INVALID_FUNC_DESCRIPTOR "example$[D)V"//$ is an invalid symbol, missing ( #define INVALID_FUNC_TYPE_DESCRIPTOR "example(H)A"//H and A are invalid types @@ -44,10 +32,6 @@ static void stdLog(void*, int level, const char *file, int line, const char *msg class DynFunctionTests : public ::testing::Test { public: DynFunctionTests() { - int lvl = 1; - dynFunction_logSetup(stdLog, nullptr, lvl); - dynType_logSetup(stdLog, nullptr, lvl); - dynCommon_logSetup(stdLog, nullptr, lvl); } ~DynFunctionTests() override = default; diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index 2ddaa1473..d7836cf47 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -40,16 +40,6 @@ extern "C" { #include "fmemopen.h" #endif - static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); - } - static void checkInterfaceVersion(dyn_interface_type* dynIntf, const char* v) { int status; @@ -197,11 +187,6 @@ extern "C" { class DynInterfaceTests : public ::testing::Test { public: DynInterfaceTests() { - int level = 1; - dynCommon_logSetup(stdLog, NULL, level); - dynType_logSetup(stdLog, NULL, level); - dynFunction_logSetup(stdLog, NULL, level); - dynInterface_logSetup(stdLog, NULL, level); } ~DynInterfaceTests() override { } diff --git a/libs/dfi/gtest/src/dyn_message_tests.cpp b/libs/dfi/gtest/src/dyn_message_tests.cpp index 4c1d242de..46d787ab4 100644 --- a/libs/dfi/gtest/src/dyn_message_tests.cpp +++ b/libs/dfi/gtest/src/dyn_message_tests.cpp @@ -40,16 +40,6 @@ extern "C" { #include "fmemopen.h" #endif -static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - static void checkMessageVersion(dyn_message_type* dynMsg, const char* v){ int status = 0; @@ -229,10 +219,6 @@ static void msg_invalid(void) { class DynMessageTests : public ::testing::Test { public: DynMessageTests() { - int level = 1; - dynCommon_logSetup(stdLog, NULL, level); - dynType_logSetup(stdLog, NULL, level); - dynMessage_logSetup(stdLog, NULL, level); } ~DynMessageTests() override { } diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index 9cb23876a..cfa01ee22 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -20,22 +20,10 @@ #include "gtest/gtest.h" extern "C" { - #include - #include "dyn_common.h" #include "dyn_type.h" #include "celix_err.h" - static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); - } - static void runTest(const char *descriptorStr, const char *exName) { dyn_type *type; type = NULL; @@ -70,7 +58,6 @@ extern "C" { class DynTypeTests : public ::testing::Test { public: DynTypeTests() { - dynType_logSetup(stdLog, NULL, 1); } ~DynTypeTests() override { } diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index 6aeb5ec35..6105928d3 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -34,16 +34,6 @@ extern "C" { #include "json_serializer.h" #include "celix_err.h" -static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) { - va_list ap; - const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; - fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); - va_start(ap, msg); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - /*********** example 1 ************************/ /** struct type ******************************/ const char *example1_descriptor = "{DJISF a b c d e}"; @@ -780,11 +770,6 @@ void writeEnumFailed(void) { class JsonSerializerTests : public ::testing::Test { public: JsonSerializerTests() { - int lvl = 1; - dynCommon_logSetup(stdLog, nullptr, lvl); - dynType_logSetup(stdLog, nullptr,lvl); - dynTypeCommon_logSetup(stdLog, nullptr,lvl); - jsonSerializer_logSetup(stdLog, nullptr, lvl); } ~JsonSerializerTests() override { } diff --git a/libs/dfi/include/dfi_log_util.h b/libs/dfi/include/dfi_log_util.h deleted file mode 100644 index 9c65d6b17..000000000 --- a/libs/dfi/include/dfi_log_util.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef _DFI_LOG_UTIL_H_ -#define _DFI_LOG_UTIL_H_ - -/** - * @deprecated The header will be remove, Use celix_err.h instead. - */ - -#include "celix_dfi_export.h" -#include "celix_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*logf_ft)(void *handle, int level, const char *file, int line, const char *format, ...); - -#define DFI_SETUP_LOG_HEADER(cmp) \ - void cmp ## _logSetup(logf_ft logf, void *handle, int currentLogLevel); - -#define DFI_SETUP_LOG(cmp) \ - static logf_ft g_logf = NULL; \ - static void *g_logHandle = NULL; \ - static int g_currentLogLevel = 1; \ - \ - CELIX_DFI_EXPORT void cmp ## _logSetup(logf_ft logf, void *handle, int currentLogLevel) { \ - g_currentLogLevel = currentLogLevel; \ - g_logHandle = handle; \ - g_logf = logf; \ - } - -#define LOG_LVL_ERROR 1 -#define LOG_LVL_WARNING 2 -#define LOG_LVL_INFO 3 -#define LOG_LVL_DEBUG 4 - -#define LOG_ERROR(msg, ...) celix_err_pushf((msg), ##__VA_ARGS__) - -#define LOG_WARNING(msg, ...) \ - if (g_logf != NULL && g_currentLogLevel >= LOG_LVL_WARNING) { \ - g_logf(g_logHandle, LOG_LVL_WARNING, __FILE__, __LINE__, (msg), ##__VA_ARGS__); \ - } - -#define LOG_INFO(msg, ...) \ - if (g_logf != NULL && g_currentLogLevel >= LOG_LVL_INFO) { \ - g_logf(g_logHandle, LOG_LVL_INFO, __FILE__, __LINE__, (msg), ##__VA_ARGS__); \ - } - -#define LOG_DEBUG(msg, ...) \ - if (g_logf != NULL && g_currentLogLevel >= LOG_LVL_DEBUG) { \ - g_logf(g_logHandle, LOG_LVL_DEBUG, __FILE__, __LINE__, (msg), ##__VA_ARGS__); \ - } - - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/libs/dfi/include/dyn_common.h b/libs/dfi/include/dyn_common.h index 1c44280bf..ad17bed0d 100644 --- a/libs/dfi/include/dyn_common.h +++ b/libs/dfi/include/dyn_common.h @@ -25,16 +25,12 @@ #include #include -#include "dfi_log_util.h" #include "celix_dfi_export.h" #ifdef __cplusplus extern "C" { #endif -//logging -DFI_SETUP_LOG_HEADER(dynCommon) ; - TAILQ_HEAD(namvals_head, namval_entry); struct namval_entry { diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index e812eeaf8..c93f3ab6f 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -21,7 +21,6 @@ #define __DYN_FUNCTION_H_ #include "dyn_type.h" -#include "dfi_log_util.h" #include "celix_dfi_export.h" #ifdef __cplusplus @@ -45,8 +44,6 @@ extern "C" { typedef struct _dyn_function_type dyn_function_type; -DFI_SETUP_LOG_HEADER(dynFunction); - enum dyn_function_argument_meta { DYN_FUNCTION_ARGUMENT_META__STD = 0, DYN_FUNCTION_ARGUMENT_META__HANDLE = 1, diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index 0f80bec23..32c4d21dc 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -23,7 +23,6 @@ #include "dyn_common.h" #include "dyn_type.h" #include "dyn_function.h" -#include "dfi_log_util.h" #include "celix_cleanup.h" #include "celix_version.h" #include "celix_dfi_export.h" @@ -32,8 +31,6 @@ extern "C" { #endif -DFI_SETUP_LOG_HEADER(dynInterface); - /* Description string * * Descriptor (interface) = HeaderSection AnnotationSection TypesSection MethodsSection diff --git a/libs/dfi/include/dyn_message.h b/libs/dfi/include/dyn_message.h index 2b719efae..1a29c187f 100644 --- a/libs/dfi/include/dyn_message.h +++ b/libs/dfi/include/dyn_message.h @@ -22,7 +22,6 @@ #include "dyn_common.h" #include "dyn_type.h" -#include "dfi_log_util.h" #include "celix_version.h" #include "celix_dfi_export.h" @@ -30,8 +29,6 @@ extern "C" { #endif -DFI_SETUP_LOG_HEADER(dynMessage); - /* Description string * * Descriptor (message) = HeaderSection AnnotationSection TypesSection MessageSection diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index c8a963eaf..13fe43967 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -25,7 +25,6 @@ #include #include -#include "dfi_log_util.h" #include "celix_dfi_export.h" #ifdef __cplusplus @@ -135,9 +134,6 @@ struct meta_entry { TAILQ_ENTRY(meta_entry) entries; }; -//logging -DFI_SETUP_LOG_HEADER(dynType); - /** * Parses a descriptor stream and creates a dyn_type (dynamic type). * If successful the type output argument points to the newly created dyn type. diff --git a/libs/dfi/include/json_rpc.h b/libs/dfi/include/json_rpc.h index 931a37dfe..cdbf3fc33 100644 --- a/libs/dfi/include/json_rpc.h +++ b/libs/dfi/include/json_rpc.h @@ -21,7 +21,6 @@ #define __JSON_RPC_H_ #include -#include "dfi_log_util.h" #include "dyn_type.h" #include "dyn_function.h" #include "dyn_interface.h" @@ -31,9 +30,6 @@ extern "C" { #endif -//logging -DFI_SETUP_LOG_HEADER(jsonRpc); - /** * @brief Call a remote service using JSON-RPC. * diff --git a/libs/dfi/include/json_serializer.h b/libs/dfi/include/json_serializer.h index 6ef61707d..2e5d2ce93 100644 --- a/libs/dfi/include/json_serializer.h +++ b/libs/dfi/include/json_serializer.h @@ -21,7 +21,6 @@ #define __JSON_SERIALIZER_H_ #include -#include "dfi_log_util.h" #include "dyn_type.h" #include "dyn_function.h" #include "dyn_interface.h" @@ -31,9 +30,6 @@ extern "C" { #endif -//logging -DFI_SETUP_LOG_HEADER(jsonSerializer); - /** * @brief Deserialize a JSON string to a given type. * diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index adc33c8dc..8169978be 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -18,6 +18,7 @@ */ #include "dyn_common.h" +#include "celix_err.h" #include #include @@ -32,8 +33,6 @@ static const int OK = 0; static const int ERROR = 1; -DFI_SETUP_LOG(dynCommon) - static bool dynCommon_charIn(int c, const char *acceptedChars); int dynCommon_parseName(FILE *stream, char **result) { @@ -60,13 +59,13 @@ int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char ungetc(c, stream); } else { status = ERROR; - LOG_ERROR("Error creating mem stream for name. %s", strerror(errno)); + celix_err_pushf("Error creating mem stream for name. %s", strerror(errno)); } if (status == OK) { if (strLen == 0) { status = ERROR; - LOG_ERROR("Parsed empty name"); + celix_err_pushf("Parsed empty name"); } } @@ -114,7 +113,7 @@ int dynCommon_eatChar(FILE *stream, int expected) { int c = fgetc(stream); if (c != expected) { status = ERROR; - LOG_ERROR("Error parsing, expected token '%c' got '%c' at position %li", expected, c, loc); + celix_err_pushf("Error parsing, expected token '%c' got '%c' at position %li", expected, c, loc); } return status; } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index 1b6666cc8..eb9903b13 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -19,6 +19,7 @@ #include "dyn_function.h" #include "dyn_function_common.h" +#include "celix_err.h" #include #include @@ -30,8 +31,6 @@ static const int MEM_ERROR = 1; static const int PARSE_ERROR = 2; static const int ERROR = 2; -DFI_SETUP_LOG(dynFunction) - static int dynFunction_initCif(dyn_function_type *dynFunc); static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descriptor); static void dynFunction_ffiBind(ffi_cif *cif, void *ret, void *args[], void *userData); @@ -51,12 +50,12 @@ int dynFunction_parse(FILE *descriptor, struct types_head *refTypes, dyn_functio if (status == 0) { int rc = dynFunction_initCif(dynFunc); if (rc != 0) { - LOG_ERROR("Error initializing cif"); + celix_err_pushf("Error initializing cif"); status = ERROR; } } } else { - LOG_ERROR("Error allocating memory for dyn function\n"); + celix_err_pushf("Error allocating memory for dyn function\n"); status = MEM_ERROR; } @@ -81,7 +80,7 @@ int dynFunction_parse(FILE *descriptor, struct types_head *refTypes, dyn_functio if (status == OK) { *out = dynFunc; } else { - LOG_ERROR("Failed to Create dyn function"); + celix_err_pushf("Failed to Create dyn function"); if (dynFunc != NULL) { dynFunction_destroy(dynFunc); } @@ -99,7 +98,7 @@ int dynFunction_parseWithStr(const char *descriptor, struct types_head *refTypes fclose(stream); } else { status = MEM_ERROR; - LOG_ERROR("Error creating mem stream for descriptor string. %s", strerror(errno)); + celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); } return status; } @@ -118,7 +117,7 @@ static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descrip int c = fgetc(descriptor); if ( c != '(') { status = PARSE_ERROR; - LOG_ERROR("Expected '(' token got '%c'", c); + celix_err_pushf("Expected '(' token got '%c'", c); } } @@ -143,7 +142,7 @@ static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descrip index += 1; } else { - LOG_ERROR("Error allocating memory"); + celix_err_pushf("Error allocating memory"); status = MEM_ERROR; } } diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index bad963353..aab77bf8e 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -18,6 +18,7 @@ */ #include "dyn_interface.h" +#include "celix_err.h" #include #include @@ -26,7 +27,6 @@ #include "dyn_type.h" #include "dyn_interface_common.h" -DFI_SETUP_LOG(dynInterface); static const int OK = 0; static const int ERROR = 1; @@ -78,12 +78,12 @@ int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { status = intf->version != NULL ? OK : ERROR; } if (status == ERROR) { - LOG_ERROR("Invalid version (%s) in parsed descriptor\n",version); + celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); } } } else { status = ERROR; - LOG_ERROR("Error allocating memory for dynamic interface\n"); + celix_err_pushf("Error allocating memory for dynamic interface\n"); } if (status == OK) { @@ -115,7 +115,7 @@ int dynInterface_checkInterface(dyn_interface_type *intf) { if (!foundType || !foundVersion || !foundName) { status = ERROR; - LOG_ERROR("Parse Error. There must be a header section with a type, version and name entry"); + celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); } struct method_entry *mEntry = NULL; @@ -124,7 +124,7 @@ int dynInterface_checkInterface(dyn_interface_type *intf) { int descriptor = dynType_descriptorType(type); if (descriptor != 'N') { status = ERROR; - LOG_ERROR("Parse Error. Only method with a return type 'N' (native int) are supported. Got return type '%c'\n", descriptor); + celix_err_pushf("Parse Error. Only method with a return type 'N' (native int) are supported. Got return type '%c'\n", descriptor); break; } } @@ -158,7 +158,7 @@ static int dynInterface_parseSection(dyn_interface_type *intf, FILE *stream) { status = dynInterface_parseMethods(intf, stream); } else { status = ERROR; - LOG_ERROR("unsupported section '%s'", sectionName); + celix_err_pushf("unsupported section '%s'", sectionName); } } @@ -201,7 +201,7 @@ static int dynInterface_parseNameValueSection(dyn_interface_type *intf, FILE *st TAILQ_INSERT_TAIL(head, entry, entries); } else { status = ERROR; - LOG_ERROR("Error allocating memory for namval entry"); + celix_err_pushf("Error allocating memory for namval entry"); } } @@ -256,7 +256,7 @@ static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream) { TAILQ_INSERT_TAIL(&intf->types, entry, entries); } else { status = ERROR; - LOG_ERROR("Error allocating memory for type entry"); + celix_err_pushf("Error allocating memory for type entry"); } } @@ -321,7 +321,7 @@ static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream) { TAILQ_INSERT_TAIL(&intf->methods, entry, entries); } else { status = ERROR; - LOG_ERROR("Error allocating memory for method entry"); + celix_err_pushf("Error allocating memory for method entry"); } } @@ -418,7 +418,7 @@ static int dynInterface_getEntryForHead(struct namvals_head *head, const char *n *out = value; } else { status = ERROR; - LOG_ERROR("Cannot find '%s' in list", name); + celix_err_pushf("Cannot find '%s' in list", name); } return status; } diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c index 0571be093..e020a8122 100644 --- a/libs/dfi/src/dyn_message.c +++ b/libs/dfi/src/dyn_message.c @@ -18,6 +18,7 @@ */ #include "dyn_message.h" +#include "celix_err.h" #include #include @@ -25,8 +26,6 @@ #include "dyn_common.h" #include "dyn_type.h" -DFI_SETUP_LOG(dynMessage); - struct _dyn_message_type { struct namvals_head header; struct namvals_head annotations; @@ -83,19 +82,19 @@ int dynMessage_parse(FILE *descriptor, dyn_message_type **out) { status = msg->msgVersion != NULL ? OK : ERROR; } if (status == ERROR) { - LOG_ERROR("Invalid version (%s) in parsed descriptor\n",version); + celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); } } } else { status = ERROR; - LOG_ERROR("Error allocating memory for dynamic message\n"); + celix_err_pushf("Error allocating memory for dynamic message\n"); } if (status == OK) { *out = msg; } else if (msg != NULL) { - LOG_ERROR("Error parsing msg\n"); + celix_err_pushf("Error parsing msg\n"); dynMessage_destroy(msg); } return status; @@ -122,7 +121,7 @@ static int dynMessage_checkMessage(dyn_message_type *msg) { if (!foundType || !foundVersion || !foundName) { status = ERROR; - LOG_ERROR("Parse Error. There must be a header section with a type, version and name entry"); + celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); } } @@ -154,7 +153,7 @@ static int dynMessage_parseSection(dyn_message_type *msg, FILE *stream) { status = dynMessage_parseMessage(msg, stream); } else { status = ERROR; - LOG_ERROR("unsupported section '%s'", sectionName); + celix_err_pushf("unsupported section '%s'", sectionName); } } @@ -197,7 +196,7 @@ static int dynMessage_parseNameValueSection(dyn_message_type *msg, FILE *stream, TAILQ_INSERT_TAIL(head, entry, entries); } else { status = ERROR; - LOG_ERROR("Error allocating memory for namval entry"); + celix_err_pushf("Error allocating memory for namval entry"); } } @@ -249,7 +248,7 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { TAILQ_INSERT_TAIL(&msg->types, entry, entries); } else { status = ERROR; - LOG_ERROR("Error allocating memory for type entry"); + celix_err_pushf("Error allocating memory for type entry"); } } @@ -347,7 +346,7 @@ static int dynMessage_getEntryForHead(struct namvals_head *head, const char *nam *out = value; } else { status = ERROR; - LOG_ERROR("Cannot find '%s' in list", name); + celix_err_pushf("Cannot find '%s' in list", name); } return status; } diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index df2021bfd..507f6c4a1 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -18,18 +18,16 @@ */ #include "dyn_type.h" +#include "dyn_type_common.h" +#include "dyn_type_common.h" +#include "dyn_common.h" +#include "celix_err.h" #include #include #include #include #include -#include "dyn_type_common.h" - -#include "dyn_type_common.h" -#include "dyn_common.h" - -DFI_SETUP_LOG(dynType) static const int OK = 0; static const int ERROR = 1; @@ -97,13 +95,13 @@ int dynType_parseWithStr(const char *descriptor, const char *name, struct types_ status = PARSE_ERROR; dynType_destroy(*type); *type = NULL; - LOG_ERROR("Expected EOF got %c", c); + celix_err_pushf("Expected EOF got %c", c); } } fclose(stream); } else { status = ERROR; - LOG_ERROR("Error creating mem stream for descriptor string. %s", strerror(errno)); + celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); } return status; } @@ -121,7 +119,7 @@ static int dynType_parseWithStream(FILE *stream, const char *name, dyn_type *par type->name = strdup(name); if (type->name == NULL) { status = MEM_ERROR; - LOG_ERROR("Error strdup'ing name '%s'\n", name); + celix_err_pushf("Error strdup'ing name '%s'\n", name); } } if (status == OK) { @@ -134,7 +132,7 @@ static int dynType_parseWithStream(FILE *stream, const char *name, dyn_type *par } } else { status = MEM_ERROR; - LOG_ERROR("Error allocating memory for type"); + celix_err_pushf("Error allocating memory for type"); } return status; } @@ -216,7 +214,7 @@ static int dynType_parseMetaInfo(FILE *stream, dyn_type *type) { entry->value = value; TAILQ_INSERT_TAIL(&type->metaProperties, entry, entries); } else { - LOG_ERROR("Failed to parse meta properties"); + celix_err_pushf("Failed to parse meta properties"); free(name); free(value); free(entry); @@ -266,7 +264,7 @@ static int dynType_parseComplex(FILE *stream, dyn_type *type) { } else { free(entry); status = MEM_ERROR; - LOG_ERROR("Error allocating memory for type"); + celix_err_pushf("Error allocating memory for type"); } if (status != OK) { @@ -310,7 +308,7 @@ static int dynType_parseComplex(FILE *stream, dyn_type *type) { } } else { status = MEM_ERROR; - LOG_ERROR("Error allocating memory for elements"); + celix_err_pushf("Error allocating memory for elements"); } } @@ -323,7 +321,7 @@ static int dynType_parseComplex(FILE *stream, dyn_type *type) { } } else { status = MEM_ERROR; - LOG_ERROR("Error allocating memory for type"); + celix_err_pushf("Error allocating memory for type"); } } @@ -351,14 +349,14 @@ static int dynType_parseNestedType(FILE *stream, dyn_type *type) { entry->type->name = name; } else { status = MEM_ERROR; - LOG_ERROR("Error allocating entry"); + celix_err_pushf("Error allocating entry"); } if (status == OK) { int c = fgetc(stream); if (c != '=') { status = PARSE_ERROR; - LOG_ERROR("Error parsing nested type expected '=' got '%c'", c); + celix_err_pushf("Error parsing nested type expected '=' got '%c'", c); } } @@ -367,7 +365,7 @@ static int dynType_parseNestedType(FILE *stream, dyn_type *type) { int c = fgetc(stream); if (c != ';') { status = PARSE_ERROR; - LOG_ERROR("Expected ';' got '%c'\n", c); + celix_err_pushf("Expected ';' got '%c'\n", c); } } @@ -393,7 +391,7 @@ static int dynType_parseReference(FILE *stream, dyn_type *type) { status = dynType_parseRefByValue(stream, subType); } else { status = MEM_ERROR; - LOG_ERROR("Error allocating memory for subtype\n"); + celix_err_pushf("Error allocating memory for subtype\n"); } return status; @@ -412,7 +410,7 @@ static int dynType_parseRefByValue(FILE *stream, dyn_type *type) { type->ref.ref = ref; } else { status = PARSE_ERROR; - LOG_ERROR("Error cannot find type '%s'", name); + celix_err_pushf("Error cannot find type '%s'", name); } free(name); } @@ -421,7 +419,7 @@ static int dynType_parseRefByValue(FILE *stream, dyn_type *type) { int c = fgetc(stream); if (c != ';') { status = PARSE_ERROR; - LOG_ERROR("Error expected ';' got '%c'", c); + celix_err_pushf("Error expected ';' got '%c'", c); } } @@ -471,7 +469,7 @@ static int dynType_parseSimple(int c, dyn_type *type) { type->ffiType = ffiType; } else { status = PARSE_ERROR; - LOG_ERROR("Error unsupported type '%c'", c); + celix_err_pushf("Error unsupported type '%c'", c); } return status; @@ -583,7 +581,7 @@ int dynType_alloc(dyn_type *type, void **bufLoc) { *bufLoc = inst; } else { status = MEM_ERROR; - LOG_ERROR("Error allocating memory for type '%c'", type->descriptor); + celix_err_pushf("Error allocating memory for type '%c'", type->descriptor); } } @@ -675,11 +673,11 @@ int dynType_sequence_alloc(dyn_type *type, void *inst, uint32_t cap) { } else { seq->cap = 0; status = MEM_ERROR; - LOG_ERROR("Error allocating memory for buf"); + celix_err_pushf("Error allocating memory for buf"); } } else { - status = MEM_ERROR; - LOG_ERROR("Error allocating memory for seq"); + status = MEM_ERROR; + celix_err_pushf("Error allocating memory for seq"); } return status; } @@ -696,11 +694,11 @@ int dynType_sequence_reserve(dyn_type *type, void *inst, uint32_t cap) { } else { seq->cap = 0; status = MEM_ERROR; - LOG_ERROR("Error allocating memory for buf"); + celix_err_pushf("Error allocating memory for buf"); } } else { status = MEM_ERROR; - LOG_ERROR("Error allocating memory for seq"); + celix_err_pushf("Error allocating memory for seq"); } return status; } @@ -737,7 +735,7 @@ void dynType_deepFree(dyn_type *type, void *loc, bool alsoDeleteSelf) { //nop break; default: - LOG_ERROR("Unexpected switch case. cannot free dyn type %c\n", type->descriptor); + celix_err_pushf("Unexpected switch case. cannot free dyn type %c\n", type->descriptor); break; } @@ -785,12 +783,12 @@ int dynType_sequence_locForIndex(dyn_type *type, void *seqLoc, int index, void * if (index >= seq->cap) { status = ERROR; - LOG_ERROR("Requested index (%i) is greater than capacity (%u) of sequence", index, seq->cap); + celix_err_pushf("Requested index (%i) is greater than capacity (%u) of sequence", index, seq->cap); } if (index >= seq->len) { status = ERROR; - LOG_ERROR("Requesting index (%i) outsize defined length (%u) but within capacity", index, seq->len); + celix_err_pushf("Requesting index (%i) outsize defined length (%u) but within capacity", index, seq->len); } if (status == OK) { @@ -811,7 +809,7 @@ int dynType_sequence_increaseLengthAndReturnLastLoc(dyn_type *type, void *seqLoc seq->len += 1; } else { status = ERROR; - LOG_ERROR("Cannot increase sequence length beyond capacity (%u)", seq->cap); + celix_err_pushf("Cannot increase sequence length beyond capacity (%u)", seq->cap); } if (status == OK) { @@ -966,7 +964,7 @@ int dynType_text_allocAndInit(dyn_type *type, void *textLoc, const char *value) *loc = str; } else { status = ERROR; - LOG_ERROR("Cannot allocate memory for string"); + celix_err_pushf("Cannot allocate memory for string"); } return status; } diff --git a/libs/dfi/src/dyn_type_common.c b/libs/dfi/src/dyn_type_common.c index 4db783c2d..6de30f453 100644 --- a/libs/dfi/src/dyn_type_common.c +++ b/libs/dfi/src/dyn_type_common.c @@ -18,8 +18,8 @@ */ #include "dyn_type.h" - #include "dyn_type_common.h" +#include "celix_err.h" #include #include @@ -27,8 +27,6 @@ #include #include -DFI_SETUP_LOG(dynTypeCommon) - dyn_type * dynType_findType(dyn_type *type, char *name) { dyn_type *result = NULL; @@ -62,7 +60,7 @@ dyn_type * dynType_findType(dyn_type *type, char *name) { ffi_type * dynType_ffiType(dyn_type * type) { if (type->type == DYN_TYPE_REF) { if (type->ref.ref == NULL) { - LOG_ERROR("Error. Ref for %s is not (yet) initialized", type->name); + celix_err_pushf("Error. Ref for %s is not (yet) initialized", type->name); return NULL; } return type->ref.ref->ffiType; diff --git a/libs/dfi/src/dyn_type_common.h b/libs/dfi/src/dyn_type_common.h index 856b9d321..45e8098ab 100644 --- a/libs/dfi/src/dyn_type_common.h +++ b/libs/dfi/src/dyn_type_common.h @@ -25,14 +25,11 @@ #include -#include "dfi_log_util.h" #ifdef __cplusplus extern "C" { #endif -DFI_SETUP_LOG_HEADER(dynTypeCommon); - struct _dyn_type { char *name; char descriptor; diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index c2f7f43dc..359ca0ef9 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -21,19 +21,19 @@ #include "json_serializer.h" #include "dyn_type.h" #include "dyn_interface.h" +#include "dyn_type_common.h" +#include "celix_compiler.h" +#include "celix_err.h" + #include #include #include #include #include -#include "celix_compiler.h" -#include "dyn_type_common.h" static int OK = 0; static int ERROR = 1; -DFI_SETUP_LOG(jsonRpc); - typedef void (*gen_func_type)(void); struct generic_service_layout { @@ -52,14 +52,14 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c const char *sig; if (js_request) { if (json_unpack(js_request, "{s:s}", "m", &sig) != 0) { - LOG_ERROR("Got json error '%s'\n", error.text); + celix_err_pushf("Got json error '%s'\n", error.text); json_decref(js_request); return ERROR; } else { arguments = json_object_get(js_request, "a"); } } else { - LOG_ERROR("Got json error '%s' for '%s'\n", error.text, request); + celix_err_pushf("Got json error '%s' for '%s'\n", error.text, request); return ERROR; } @@ -76,7 +76,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c if (method == NULL) { status = ERROR; - LOG_ERROR("Cannot find method with sig '%s'", sig); + celix_err_pushf("Cannot find method with sig '%s'", sig); } else if (status == OK) { returnType = dynFunction_returnType(method->dynFunc); @@ -139,7 +139,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c if (status == OK) { if (dynType_descriptorType(returnType) != 'N') { //NOTE To be able to handle exception only N as returnType is supported - LOG_ERROR("Only interface methods with a native int are supported. Found type '%c'", (char)dynType_descriptorType(returnType)); + celix_err_pushf("Only interface methods with a native int are supported. Found type '%c'", (char)dynType_descriptorType(returnType)); status = ERROR; } } @@ -276,7 +276,7 @@ int jsonRpc_prepareInvokeRequest(dyn_function_type *func, const char *id, void * if (rc == 0) { json_array_append_new(arguments, val); } else { - LOG_ERROR("Failed to serialize args for function '%s'\n", id); + celix_err_pushf("Failed to serialize args for function '%s'\n", id); status = ERROR; break; } @@ -305,7 +305,7 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] json_t *replyJson = json_loads(reply, JSON_DECODE_ANY, &error); if (replyJson == NULL) { status = ERROR; - LOG_ERROR("Error parsing json '%s', got error '%s'", reply, error.text); + celix_err_pushf("Error parsing json '%s', got error '%s'", reply, error.text); } json_t *result = NULL; @@ -332,12 +332,12 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] nrOfOutputArgs += 1; if (nrOfOutputArgs > 1) { status = ERROR; - LOG_ERROR("Only one output argument is supported"); + celix_err_pushf("Only one output argument is supported"); break; } if (result == NULL && !replyHasError) { status = ERROR; - LOG_ERROR("Expected result in reply. got '%s'", reply); + celix_err_pushf("Expected result in reply. got '%s'", reply); break; } } diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index e24cc58d4..dbbc5b742 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -21,6 +21,7 @@ #include "dyn_type.h" #include "dyn_type_common.h" #include "dyn_interface.h" +#include "celix_err.h" #include #include @@ -43,8 +44,6 @@ static int jsonSerializer_writeEnum(dyn_type *type, int32_t enum_value, json_t * static int OK = 0; static int ERROR = 1; -DFI_SETUP_LOG(jsonSerializer); - int jsonSerializer_deserialize(dyn_type *type, const char *input, size_t length, void **result) { assert(dynType_type(type) == DYN_TYPE_COMPLEX || dynType_type(type) == DYN_TYPE_SEQUENCE); int status = 0; @@ -57,11 +56,11 @@ int jsonSerializer_deserialize(dyn_type *type, const char *input, size_t length, json_decref(root); } else { status = ERROR; - LOG_ERROR("Error parsing json input '%.*s'. Error is: %s\n", (int)length, input, error.text); + celix_err_pushf("Error parsing json input '%.*s'. Error is: %s\n", (int)length, input, error.text); } if (status != OK) { - LOG_ERROR("Error cannot deserialize json. Input is '%s'\n", input); + celix_err_pushf("Error cannot deserialize json. Input is '%s'\n", input); } return status; } @@ -84,7 +83,7 @@ static int jsonSerializer_createType(dyn_type *type, json_t *val, void **result) *((char**)inst) = strdup(s); } else { status = ERROR; - LOG_ERROR("Expected json_string type got %i\n", json_typeof(val)); + celix_err_pushf("Expected json_string type got %i\n", json_typeof(val)); } } else { status = dynType_alloc(type, &inst); @@ -229,7 +228,7 @@ static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { status = jsonSerializer_parseEnum(type, json_string_value(val), E); } else { status = ERROR; - LOG_ERROR("Expected json string for enum type but got %i", json_typeof(val)); + celix_err_pushf("Expected json string for enum type but got %i", json_typeof(val)); } break; case 't' : @@ -239,7 +238,7 @@ static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { dynType_text_allocAndInit(type, loc, json_string_value(val)); } else { status = ERROR; - LOG_ERROR("Expected json string type got %i", json_typeof(val)); + celix_err_pushf("Expected json string type got %i", json_typeof(val)); } break; case '[' : @@ -247,7 +246,7 @@ static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { status = jsonSerializer_parseSequence(type, val, loc); } else { status = ERROR; - LOG_ERROR("Expected json array type got '%i'", json_typeof(val)); + celix_err_pushf("Expected json array type got '%i'", json_typeof(val)); } break; case '{' : @@ -263,14 +262,14 @@ static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { break; case 'P' : status = ERROR; - LOG_ERROR("Untyped pointer are not supported for serialization"); + celix_err_pushf("Untyped pointer are not supported for serialization"); break; case 'l': status = jsonSerializer_parseAny(type->ref.ref, loc, val); break; default : status = ERROR; - LOG_ERROR("Error provided type '%c' not supported for JSON\n", dynType_descriptorType(type)); + celix_err_pushf("Error provided type '%c' not supported for JSON\n", dynType_descriptorType(type)); break; } @@ -327,7 +326,7 @@ static int jsonSerializer_parseEnum(dyn_type *type, const char* enum_name, int32 } } - LOG_ERROR("Could not find Enum value %s in enum type", enum_name); + celix_err_pushf("Could not find Enum value %s in enum type", enum_name); return ERROR; } @@ -426,13 +425,13 @@ static int jsonSerializer_writeAny(dyn_type *type, void* input, json_t **out) { break; case 'P' : status = ERROR; - LOG_ERROR("Untyped pointer not supported for serialization."); + celix_err_pushf("Untyped pointer not supported for serialization."); break; case 'l': status = jsonSerializer_writeAny(type->ref.ref, input, out); break; default : - LOG_ERROR("Unsupported descriptor '%c'", descriptor); + celix_err_pushf("Unsupported descriptor '%c'", descriptor); status = ERROR; break; } @@ -498,7 +497,7 @@ static int jsonSerializer_writeComplex(dyn_type *type, void *input, json_t **out dyn_type *subType = NULL; index = dynType_complex_indexForName(type, entry->name); if (index < 0) { - LOG_ERROR("Cannot find index for member '%s'", entry->name); + celix_err_pushf("Cannot find index for member '%s'", entry->name); status = ERROR; } if(status == OK){ @@ -546,6 +545,6 @@ static int jsonSerializer_writeEnum(dyn_type *type, int32_t enum_value, json_t * } } - LOG_ERROR("Could not find Enum value %s in enum type", enum_value_str); + celix_err_pushf("Could not find Enum value %s in enum type", enum_value_str); return ERROR; } From 452bfb36de45da4a37e97772e4bef01f488c2d53 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 14 Dec 2023 15:30:19 +0800 Subject: [PATCH 02/62] Make gcov play nicely with Conan2. `tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,--unresolved-symbols=ignore-in-shared-libs"` would be overriden previously. --- cmake/celix_project/CodeCoverage.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/celix_project/CodeCoverage.cmake b/cmake/celix_project/CodeCoverage.cmake index aa33308e9..7615b38fc 100644 --- a/cmake/celix_project/CodeCoverage.cmake +++ b/cmake/celix_project/CodeCoverage.cmake @@ -70,8 +70,8 @@ IF(ENABLE_CODE_COVERAGE) # Setup compiler options ADD_DEFINITIONS(--coverage) - set(CMAKE_SHARED_LINKER_FLAGS "--coverage") - set(CMAKE_EXE_LINKER_FLAGS "--coverage") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") IF(NOT TARGET coverage) add_custom_target(coverage From ba397036091e6da7f01e339187b3513b0f45c760 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 14 Dec 2023 18:45:34 +0800 Subject: [PATCH 03/62] #509 Remove embedded memstream from utils. --- libs/dfi/gtest/src/dyn_interface_tests.cpp | 5 - libs/dfi/gtest/src/dyn_message_tests.cpp | 5 - libs/dfi/include/dyn_type.h | 5 - libs/dfi/src/dyn_common.c | 5 - libs/framework/gtest/src/ManifestTestSuite.cc | 4 - libs/framework/src/celix_log.c | 4 - libs/utils/CMakeLists.txt | 14 -- .../include_deprecated/celix_utils_api.h | 5 - libs/utils/src/memstream/fmemopen.c | 76 ---------- libs/utils/src/memstream/open_memstream.c | 130 ------------------ 10 files changed, 253 deletions(-) delete mode 100644 libs/utils/src/memstream/fmemopen.c delete mode 100644 libs/utils/src/memstream/open_memstream.c diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index d7836cf47..ab9688fab 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -35,11 +35,6 @@ extern "C" { #include "dyn_interface.h" #include "version.h" -#if NO_MEMSTREAM_AVAILABLE -#include "open_memstream.h" -#include "fmemopen.h" -#endif - static void checkInterfaceVersion(dyn_interface_type* dynIntf, const char* v) { int status; diff --git a/libs/dfi/gtest/src/dyn_message_tests.cpp b/libs/dfi/gtest/src/dyn_message_tests.cpp index 46d787ab4..50109158b 100644 --- a/libs/dfi/gtest/src/dyn_message_tests.cpp +++ b/libs/dfi/gtest/src/dyn_message_tests.cpp @@ -35,11 +35,6 @@ extern "C" { #include "dyn_common.h" #include "dyn_message.h" -#if NO_MEMSTREAM_AVAILABLE -#include "open_memstream.h" -#include "fmemopen.h" -#endif - static void checkMessageVersion(dyn_message_type* dynMsg, const char* v){ int status = 0; diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 13fe43967..4276eaf92 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -31,11 +31,6 @@ extern "C" { #endif -#if defined(NO_MEMSTREAM_AVAILABLE) -#include "memstream/open_memstream.h" -#include "memstream/fmemopen.h" -#endif - /** * dyn type (dynamic type) represent a structure in memory. It can be used to calculate the needed memory size, the size * and offset of struct members and can be used to dynamically alloc, initialize, dealloc, read & write structure diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 8169978be..eb1638290 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -25,11 +25,6 @@ #include #include -#if CELIX_UTILS_NO_MEMSTREAM_AVAILABLE -#include "open_memstream.h" -#include "fmemopen.h" -#endif - static const int OK = 0; static const int ERROR = 1; diff --git a/libs/framework/gtest/src/ManifestTestSuite.cc b/libs/framework/gtest/src/ManifestTestSuite.cc index 6cb668353..7c96903be 100644 --- a/libs/framework/gtest/src/ManifestTestSuite.cc +++ b/libs/framework/gtest/src/ManifestTestSuite.cc @@ -21,10 +21,6 @@ #include #include #include -#if CELIX_UTILS_NO_MEMSTREAM_AVAILABLE -#include "open_memstream.h" -#include "fmemopen.h" -#endif #include "celix_err.h" #include "celix_stdio_cleanup.h" diff --git a/libs/framework/src/celix_log.c b/libs/framework/src/celix_log.c index d765ee9e5..c8606b43a 100644 --- a/libs/framework/src/celix_log.c +++ b/libs/framework/src/celix_log.c @@ -28,10 +28,6 @@ #include "celix_threads.h" #include "celix_array_list.h" -#ifdef CELIX_UTILS_NO_MEMSTREAM_AVAILABLE -#include "memstream/open_memstream.h" -#endif - #define LOG_NAME "celix_framework" struct celix_framework_logger { diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index f9b012602..567c68789 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -21,14 +21,6 @@ if (UTILS) set(MEMSTREAM_SOURCES ) set(MEMSTREAM_INCLUDES ) - include(CheckFunctionExists) - #check_function_exists(fmemopen FMEMOPEN_EXISTS) - check_function_exists(open_memstream OPEN_MEMSTREAM_EXISTS) - if (NOT OPEN_MEMSTREAM_EXISTS) - set(MEMSTREAM_SOURCES src/memstream/open_memstream.c src/memstream/fmemopen.c ${MEMSTREAM_SOURCES}) - set(MEMSTREAM_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/include/memstream ${MEMSTREAM_INCLUDE_DIR}) - install(DIRECTORY include/memstream/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix/memstream COMPONENT framework) - endif() set(UTILS_SRC src/array_list.c @@ -63,10 +55,6 @@ if (UTILS) OUTPUT_NAME "celix_utils") celix_target_hide_symbols(utils) - if (NOT OPEN_MEMSTREAM_EXISTS) - target_compile_definitions(utils PUBLIC -DCELIX_UTILS_NO_MEMSTREAM_AVAILABLE) - endif () - if (ANDROID) target_compile_definitions(utils PRIVATE -DUSE_FILE32API) endif () @@ -78,7 +66,6 @@ if (UTILS) target_include_directories(utils PUBLIC $ $ #Note not installed, only for project internal usage - $ ) target_include_directories(utils PRIVATE src include_deprecated) IF(UNIX AND NOT ANDROID) @@ -129,7 +116,6 @@ if (UTILS) target_include_directories(utils_cut PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/include_internal - ${MEMSTREAM_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/celix/gen/includes/utils ${CMAKE_BINARY_DIR}/celix/gen/src/utils src include_deprecated diff --git a/libs/utils/include_deprecated/celix_utils_api.h b/libs/utils/include_deprecated/celix_utils_api.h index ef3153066..f723528b1 100644 --- a/libs/utils/include_deprecated/celix_utils_api.h +++ b/libs/utils/include_deprecated/celix_utils_api.h @@ -32,9 +32,4 @@ #include "version.h" #include "version_range.h" -#if defined(NO_MEMSTREAM_AVAILABLE) -#include "memstream/open_memstream.h" -#include "memstream/fmemopen.h" -#endif - #endif //CELIX_CELIX_UTILS_API_H_ diff --git a/libs/utils/src/memstream/fmemopen.c b/libs/utils/src/memstream/fmemopen.c deleted file mode 100644 index cb1b0c068..000000000 --- a/libs/utils/src/memstream/fmemopen.c +++ /dev/null @@ -1,76 +0,0 @@ - -/* - * fmem.c : fmemopen() on top of BSD's funopen() - * 20081017 AF - */ - -#include -#include -#include - -struct fmem { - size_t pos; - size_t size; - char *buffer; -}; -typedef struct fmem fmem_t; - -static int readfn(void *handler, char *buf, int size) -{ - int count = 0; - fmem_t *mem = handler; - size_t available = mem->size - mem->pos; - - if(size > available) size = available; - for(count=0; count < size; mem->pos++, count++) - buf[count] = mem->buffer[mem->pos]; - - return count; -} - -static int writefn(void *handler, const char *buf, int size) -{ - int count = 0; - fmem_t *mem = handler; - size_t available = mem->size - mem->pos; - - if(size > available) size = available; - for(count=0; count < size; mem->pos++, count++) - mem->buffer[mem->pos] = buf[count]; - - return count; // ? count : size; -} - -static fpos_t seekfn(void *handler, fpos_t offset, int whence) -{ - size_t pos; - fmem_t *mem = handler; - - switch(whence) { - case SEEK_SET: pos = offset; break; - case SEEK_CUR: pos = mem->pos + offset; break; - case SEEK_END: pos = mem->size + offset; break; - default: return -1; - } - - if(pos > mem->size) return -1; - - mem->pos = pos; - return (fpos_t) pos; -} - -static int closefn(void *handler) -{ - free(handler); - return 0; -} - -/* simple, but portable version of fmemopen for OS X / BSD */ -FILE *fmemopen(void *buf, size_t size, const char *mode) -{ - fmem_t *mem = (fmem_t *) malloc(sizeof(fmem_t)); - - memset(mem, 0, sizeof(fmem_t)); - mem->size = size, mem->buffer = buf; - return funopen(mem, readfn, writefn, seekfn, closefn); -} diff --git a/libs/utils/src/memstream/open_memstream.c b/libs/utils/src/memstream/open_memstream.c deleted file mode 100644 index 39d62e315..000000000 --- a/libs/utils/src/memstream/open_memstream.c +++ /dev/null @@ -1,130 +0,0 @@ -/* Use funopen(3) to provide open_memstream(3) like functionality. */ - -#include -#include -#include -#include - -struct memstream { - char **cp; - size_t *lenp; - size_t offset; -}; - -static void -memstream_grow(struct memstream *ms, size_t newsize) -{ - char *buf; - - if (newsize > *ms->lenp) { - buf = realloc(*ms->cp, newsize + 1); - if (buf != NULL) { -#ifdef DEBUG - fprintf(stderr, "MS: %p growing from %zd to %zd\n", - ms, *ms->lenp, newsize); -#endif - memset(buf + *ms->lenp + 1, 0, newsize - *ms->lenp); - *ms->cp = buf; - *ms->lenp = newsize; - } - } -} - -static int -memstream_read(void *cookie, char *buf, int len) -{ - struct memstream *ms; - int tocopy; - - ms = cookie; - memstream_grow(ms, ms->offset + len); - tocopy = *ms->lenp - ms->offset; - if (len < tocopy) - tocopy = len; - memcpy(buf, *ms->cp + ms->offset, tocopy); - ms->offset += tocopy; -#ifdef DEBUG - fprintf(stderr, "MS: read(%p, %d) = %d\n", ms, len, tocopy); -#endif - return (tocopy); -} - -static int -memstream_write(void *cookie, const char *buf, int len) -{ - struct memstream *ms; - int tocopy; - - ms = cookie; - memstream_grow(ms, ms->offset + len); - tocopy = *ms->lenp - ms->offset; - if (len < tocopy) - tocopy = len; - memcpy(*ms->cp + ms->offset, buf, tocopy); - ms->offset += tocopy; -#ifdef DEBUG - fprintf(stderr, "MS: write(%p, %d) = %d\n", ms, len, tocopy); -#endif - return (tocopy); -} - -static fpos_t -memstream_seek(void *cookie, fpos_t pos, int whence) -{ - struct memstream *ms; -#ifdef DEBUG - size_t old; -#endif - - ms = cookie; -#ifdef DEBUG - old = ms->offset; -#endif - switch (whence) { - case SEEK_SET: - ms->offset = pos; - break; - case SEEK_CUR: - ms->offset += pos; - break; - case SEEK_END: - ms->offset = *ms->lenp + pos; - break; - } -#ifdef DEBUG - fprintf(stderr, "MS: seek(%p, %lld, %d) %zd -> %zd\n", ms, pos, whence, - old, ms->offset); -#endif - return (ms->offset); -} - -static int -memstream_close(void *cookie) -{ - - free(cookie); - return (0); -} - -FILE * -open_memstream(char **cp, size_t *lenp) -{ - struct memstream *ms; - int save_errno; - FILE *fp; - - *cp = NULL; - *lenp = 0; - ms = malloc(sizeof(*ms)); - ms->cp = cp; - ms->lenp = lenp; - ms->offset = 0; - fp = funopen(ms, memstream_read, memstream_write, memstream_seek, - memstream_close); - if (fp == NULL) { - save_errno = errno; - free(ms); - errno = save_errno; - } - return (fp); -} \ No newline at end of file From ccbd7b09465446bd8f3f41a9afc63aa76e771a01 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 14 Dec 2023 22:20:10 +0800 Subject: [PATCH 04/62] Add error injection for dyn_interface. --- libs/dfi/CMakeLists.txt | 7 ++ libs/dfi/gtest/CMakeLists.txt | 12 ++++ libs/dfi/gtest/src/dyn_interface_ei_tests.cc | 68 ++++++++++++++++++ libs/dfi/gtest/src/dyn_interface_tests.cpp | 2 - libs/dfi/src/dyn_interface.c | 72 ++++++++++---------- 5 files changed, 122 insertions(+), 39 deletions(-) create mode 100644 libs/dfi/gtest/src/dyn_interface_ei_tests.cc diff --git a/libs/dfi/CMakeLists.txt b/libs/dfi/CMakeLists.txt index f82aca3a2..b5eb65e9c 100644 --- a/libs/dfi/CMakeLists.txt +++ b/libs/dfi/CMakeLists.txt @@ -60,6 +60,13 @@ if (CELIX_DFI) add_subdirectory(error_injector) endif () if (ENABLE_TESTING) + add_library(dfi_cut STATIC ${SOURCES}) + target_compile_definitions(dfi_cut PUBLIC CELIX_DFI_STATIC_DEFINE) + target_include_directories(dfi_cut PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_BINARY_DIR}/celix/gen/includes/dfi + ) + target_link_libraries(dfi_cut PUBLIC libffi::libffi jansson::jansson Celix::utils) add_subdirectory(gtest) endif(ENABLE_TESTING) endif (CELIX_DFI) diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index e0206df13..07df1dbbf 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -37,3 +37,15 @@ file(COPY ${CMAKE_CURRENT_LIST_DIR}/descriptors DESTINATION ${CMAKE_CURRENT_BINA add_test(NAME run_test_dfi COMMAND test_dfi) setup_target_for_coverage(test_dfi SCAN_DIR ..) +if (EI_TESTS) + add_executable(test_dfi_with_ei + src/dyn_interface_ei_tests.cc + ) + target_link_libraries(test_dfi_with_ei PRIVATE + dfi_cut + Celix::malloc_ei + GTest::gtest GTest::gtest_main + ) + add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) + setup_target_for_coverage(test_dfi_with_ei SCAN_DIR ..) +endif () diff --git a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc new file mode 100644 index 000000000..dce4a70cd --- /dev/null +++ b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_interface.h" +#include "malloc_ei.h" +#include "celix_err.h" +#include "celix_stdio_cleanup.h" + +#include + +class DynInterfaceErrorInjectionTestSuite : public ::testing::Test { +public: + DynInterfaceErrorInjectionTestSuite() = default; + + ~DynInterfaceErrorInjectionTestSuite() override { + celix_ei_expect_calloc(nullptr, 0, nullptr); + } +}; + +TEST_F(DynInterfaceErrorInjectionTestSuite, ParseError) { + int status = 0; + dyn_interface_type *dynIntf = NULL; + celix_autoptr(FILE) desc = fopen("descriptors/example1.descriptor", "r"); + ASSERT_NE(nullptr, desc); + + // not enough memory for dyn_interface_type + celix_ei_expect_calloc((void*) dynInterface_parse, 0, nullptr); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for dynamic interface", celix_err_popLastError()); + + rewind(desc); + // not enough memory for namval_entry when parsing header section + celix_ei_expect_calloc((void*) dynInterface_parse, 3, nullptr, 1); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for namval entry", celix_err_popLastError()); + + rewind(desc); + // not enough memory for type_entry when parsing types section + celix_ei_expect_calloc((void*) dynInterface_parse, 2, nullptr, 1); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for type entry", celix_err_popLastError()); + + rewind(desc); + // not enough memory for method_entry when parsing methods section + celix_ei_expect_calloc((void*) dynInterface_parse, 2, nullptr, 2); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for method entry", celix_err_popLastError()); +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index ab9688fab..eef8e3fe4 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -19,8 +19,6 @@ #include "gtest/gtest.h" -#include - extern "C" { diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index aab77bf8e..554cd06a4 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -44,52 +44,50 @@ static int dynInterface_getEntryForHead(struct namvals_head *head, const char *n int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { int status = OK; - dyn_interface_type *intf = calloc(1, sizeof(*intf)); - if (intf != NULL) { - TAILQ_INIT(&intf->header); - TAILQ_INIT(&intf->annotations); - TAILQ_INIT(&intf->types); - TAILQ_INIT(&intf->methods); - - char peek = (char)fgetc(descriptor); - while (peek == ':') { - ungetc(peek, descriptor); - status = dynInterface_parseSection(intf, descriptor); - if (status == OK) { - peek = (char)fgetc(descriptor); - } else { - break; - } - } + celix_autoptr(dyn_interface_type) intf = calloc(1, sizeof(*intf)); + if (intf == NULL) { + celix_err_pushf("Error allocating memory for dynamic interface"); + return ERROR; + } - if (status == OK) { - status = dynCommon_eatChar(descriptor, EOF); - } + TAILQ_INIT(&intf->header); + TAILQ_INIT(&intf->annotations); + TAILQ_INIT(&intf->types); + TAILQ_INIT(&intf->methods); + char peek = (char)fgetc(descriptor); + while (peek == ':') { + ungetc(peek, descriptor); + status = dynInterface_parseSection(intf, descriptor); if (status == OK) { - status = dynInterface_checkInterface(intf); + peek = (char)fgetc(descriptor); + } else { + break; } + } - if (status == OK) { /* We are sure that version field is present in the header */ - char* version = NULL; - dynInterface_getVersionString(intf,&version); - if (version != NULL){ - intf->version = celix_version_createVersionFromString(version); - status = intf->version != NULL ? OK : ERROR; - } - if (status == ERROR) { - celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); - } + if (status == OK) { + status = dynCommon_eatChar(descriptor, EOF); + } + + if (status == OK) { + status = dynInterface_checkInterface(intf); + } + + if (status == OK) { /* We are sure that version field is present in the header */ + char* version = NULL; + dynInterface_getVersionString(intf,&version); + if (version != NULL){ + intf->version = celix_version_createVersionFromString(version); + status = intf->version != NULL ? OK : ERROR; + } + if (status == ERROR) { + celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); } - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for dynamic interface\n"); } if (status == OK) { - *out = intf; - } else if (intf != NULL) { - dynInterface_destroy(intf); + *out = celix_steal_ptr(intf); } return status; } From ad1fa88e373359d0b322943496752cf6693121d9 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 14 Dec 2023 22:35:09 +0800 Subject: [PATCH 05/62] Remove unnecessary null check (guaranteed by class invariant). --- libs/dfi/src/dyn_interface.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 554cd06a4..7da0403c8 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -383,11 +383,9 @@ int dynInterface_getName(dyn_interface_type *intf, char **out) { } int dynInterface_getVersion(dyn_interface_type* intf , celix_version_t** version){ - *version = intf->version; - if(*version==NULL){ - return ERROR; - } - return OK; + // aseert(intf->version != NULL); + *version = intf->version; + return OK; } int dynInterface_getVersionString(dyn_interface_type *intf, char **version) { From cced65cfd1775c9488cb0fe873b0e32a69aba52f Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 15 Dec 2023 18:20:21 +0800 Subject: [PATCH 06/62] Code deduplication and coverage improvements for dyn_common. Also remove dync_common from public interface of dfi. --- .../src/export_registration_dfi.c | 2 + .../src/rsa_json_rpc_endpoint_impl.c | 1 + .../rsa_rpc_json/src/rsa_json_rpc_impl.c | 1 + .../src/rsa_json_rpc_proxy_impl.c | 3 +- libs/dfi/CMakeLists.txt | 4 +- libs/dfi/gtest/CMakeLists.txt | 3 +- libs/dfi/gtest/src/dyn_interface_ei_tests.cc | 17 +++- libs/dfi/include/dyn_interface.h | 1 - libs/dfi/include/dyn_message.h | 1 - libs/dfi/src/dyn_common.c | 98 +++++++++++-------- libs/dfi/{include => src}/dyn_common.h | 29 ++++-- libs/dfi/src/dyn_interface.c | 49 +--------- libs/dfi/src/dyn_message.c | 49 +--------- 13 files changed, 109 insertions(+), 149 deletions(-) rename libs/dfi/{include => src}/dyn_common.h (72%) diff --git a/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c index ff45b0bea..9713e4477 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c @@ -29,6 +29,8 @@ #include "dfi_utils.h" #include "remote_interceptors_handler.h" +#include + struct export_reference { endpoint_description_t *endpoint; //owner service_reference_pt reference; diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c index ee1dc5ef2..f457e8153 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c @@ -29,6 +29,7 @@ #include #include #include +#include struct rsa_json_rpc_endpoint { celix_bundle_context_t* ctx; diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_impl.c index de86537f5..9bb19386c 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_impl.c @@ -36,6 +36,7 @@ #include #include #include +#include struct rsa_json_rpc { celix_bundle_context_t *ctx; diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c index 83d505bcd..7d2a4b0e6 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c @@ -29,8 +29,9 @@ #include "celix_build_assert.h" #include "celix_long_hash_map.h" #include -#include #include +#include +#include struct rsa_json_rpc_proxy_factory { celix_bundle_context_t* ctx; diff --git a/libs/dfi/CMakeLists.txt b/libs/dfi/CMakeLists.txt index b5eb65e9c..2a9154956 100644 --- a/libs/dfi/CMakeLists.txt +++ b/libs/dfi/CMakeLists.txt @@ -34,7 +34,8 @@ if (CELIX_DFI) add_library(dfi SHARED ${SOURCES}) target_include_directories(dfi PUBLIC $ - $) + $ + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src) target_link_libraries(dfi PRIVATE libffi::libffi) target_link_libraries(dfi PUBLIC jansson::jansson Celix::utils)##The public header file(dyn_interface.h) Celix::utils(celix_version.h) set_target_properties(dfi PROPERTIES @@ -63,6 +64,7 @@ if (CELIX_DFI) add_library(dfi_cut STATIC ${SOURCES}) target_compile_definitions(dfi_cut PUBLIC CELIX_DFI_STATIC_DEFINE) target_include_directories(dfi_cut PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_BINARY_DIR}/celix/gen/includes/dfi ) diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 07df1dbbf..5090bf3d5 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -29,7 +29,7 @@ add_executable(test_dfi target_include_directories(test_dfi PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../src) -target_link_libraries(test_dfi PRIVATE Celix::dfi Celix::utils libffi::libffi jansson::jansson GTest::gtest GTest::gtest_main) +target_link_libraries(test_dfi PRIVATE dfi_cut Celix::utils libffi::libffi jansson::jansson GTest::gtest GTest::gtest_main) celix_deprecated_utils_headers(test_dfi) file(COPY ${CMAKE_CURRENT_LIST_DIR}/descriptors DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) @@ -44,6 +44,7 @@ if (EI_TESTS) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut Celix::malloc_ei + Celix::stdio_ei GTest::gtest GTest::gtest_main ) add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) diff --git a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc index dce4a70cd..76426ecdd 100644 --- a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc @@ -18,10 +18,15 @@ */ #include "dyn_interface.h" -#include "malloc_ei.h" +#include "dyn_common.h" #include "celix_err.h" #include "celix_stdio_cleanup.h" +#include "malloc_ei.h" +#include "stdio_ei.h" +#include +#include +#include #include class DynInterfaceErrorInjectionTestSuite : public ::testing::Test { @@ -30,6 +35,7 @@ class DynInterfaceErrorInjectionTestSuite : public ::testing::Test { ~DynInterfaceErrorInjectionTestSuite() override { celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_ei_expect_open_memstream(nullptr, 0, nullptr); } }; @@ -65,4 +71,13 @@ TEST_F(DynInterfaceErrorInjectionTestSuite, ParseError) { status = dynInterface_parse(desc, &dynIntf); ASSERT_NE(0, status); ASSERT_STREQ("Error allocating memory for method entry", celix_err_popLastError()); + + rewind(desc); + // not enough memory for open_memstream + celix_ei_expect_open_memstream((void*) dynCommon_parseNameAlsoAccept, 0, nullptr); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + std::string msg = "Error creating mem stream for name. "; + msg += strerror(ENOMEM); + ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); } \ No newline at end of file diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index 32c4d21dc..9a27d0801 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -20,7 +20,6 @@ #ifndef __DYN_INTERFACE_H_ #define __DYN_INTERFACE_H_ -#include "dyn_common.h" #include "dyn_type.h" #include "dyn_function.h" #include "celix_cleanup.h" diff --git a/libs/dfi/include/dyn_message.h b/libs/dfi/include/dyn_message.h index 1a29c187f..33d5c2c22 100644 --- a/libs/dfi/include/dyn_message.h +++ b/libs/dfi/include/dyn_message.h @@ -20,7 +20,6 @@ #ifndef __DYN_MESSAGE_H_ #define __DYN_MESSAGE_H_ -#include "dyn_common.h" #include "dyn_type.h" #include "celix_version.h" #include "celix_dfi_export.h" diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index eb1638290..212e771ca 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -19,6 +19,7 @@ #include "dyn_common.h" #include "celix_err.h" +#include "celix_stdlib_cleanup.h" #include #include @@ -28,7 +29,6 @@ static const int OK = 0; static const int ERROR = 1; -static bool dynCommon_charIn(int c, const char *acceptedChars); int dynCommon_parseName(FILE *stream, char **result) { return dynCommon_parseNameAlsoAccept(stream, NULL, result); @@ -41,21 +41,20 @@ int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char size_t size = 0; int strLen = 0; FILE *name = open_memstream(&buf, &size); - - if (name != NULL) { - int c = getc(stream); - while (isalnum(c) || c == '_' || dynCommon_charIn(c, acceptedChars)) { - fputc(c, name); - c = getc(stream); - strLen += 1; - } - fflush(name); - fclose(name); - ungetc(c, stream); - } else { - status = ERROR; + if (name == NULL) { celix_err_pushf("Error creating mem stream for name. %s", strerror(errno)); + return ERROR; + } + + int c = getc(stream); + while (isalnum(c) || c == '_' || (acceptedChars != NULL && strchr(acceptedChars, c) != NULL)) { + fputc(c, name); + c = getc(stream); + strLen += 1; } + fflush(name); + fclose(name); + ungetc(c, stream); if (status == OK) { if (strLen == 0) { @@ -75,8 +74,8 @@ int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char int dynCommon_parseNameValue(FILE *stream, char **outName, char **outValue) { int status; - char *name = NULL; - char *value = NULL; + celix_autofree char *name = NULL; + celix_autofree char *value = NULL; status = dynCommon_parseName(stream, &name); if (status == OK) { @@ -89,45 +88,22 @@ int dynCommon_parseNameValue(FILE *stream, char **outName, char **outValue) { } if (status == OK) { - *outName = name; - *outValue = value; - } else { - if (name != NULL) { - free(name); - } - if (value != NULL) { - free(value); - } + *outName = celix_steal_ptr(name); + *outValue = celix_steal_ptr(value); } return status; } int dynCommon_eatChar(FILE *stream, int expected) { int status = OK; - long loc = ftell(stream); int c = fgetc(stream); if (c != expected) { status = ERROR; - celix_err_pushf("Error parsing, expected token '%c' got '%c' at position %li", expected, c, loc); + celix_err_pushf("Error parsing, expected token '%c' got '%c' at position %li", expected, c, ftell(stream)); } return status; } -static bool dynCommon_charIn(int c, const char *acceptedChars) { - bool status = false; - if (acceptedChars != NULL) { - int i; - for (i = 0; acceptedChars[i] != '\0'; i += 1) { - if (c == acceptedChars[i]) { - status = true; - break; - } - } - } - - return status; -} - void dynCommon_clearNamValHead(struct namvals_head *head) { struct namval_entry *entry = TAILQ_FIRST(head); while (entry != NULL) { @@ -143,3 +119,41 @@ void dynCommon_clearNamValHead(struct namvals_head *head) { free(tmp); } } + +int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { + int status = OK; + + int peek = fgetc(stream); + while (peek != ':' && peek != EOF) { + ungetc(peek, stream); + + celix_autofree char *name = NULL; + celix_autofree char *value = NULL; + status = dynCommon_parseNameValue(stream, &name, &value); + + if (status == OK) { + status = dynCommon_eatChar(stream, '\n'); + } + + struct namval_entry *entry = NULL; + if (status == OK) { + entry = calloc(1, sizeof(*entry)); + if (entry != NULL) { + entry->name = celix_steal_ptr(name); + entry->value = celix_steal_ptr(value); + TAILQ_INSERT_TAIL(head, entry, entries); + } else { + status = ERROR; + celix_err_pushf("Error allocating memory for namval entry"); + } + } + + if (status != OK) { + break; + } + peek = fgetc(stream); + } + ungetc(peek, stream); + + return status; +} diff --git a/libs/dfi/include/dyn_common.h b/libs/dfi/src/dyn_common.h similarity index 72% rename from libs/dfi/include/dyn_common.h rename to libs/dfi/src/dyn_common.h index ad17bed0d..73e2fd933 100644 --- a/libs/dfi/include/dyn_common.h +++ b/libs/dfi/src/dyn_common.h @@ -25,8 +25,6 @@ #include #include -#include "celix_dfi_export.h" - #ifdef __cplusplus extern "C" { #endif @@ -51,7 +49,7 @@ struct namval_entry { * @return 0 if successful, otherwise 1. * @alsoseee dynCommon_parseNameAlsoAccept */ -CELIX_DFI_EXPORT int dynCommon_parseName(FILE *stream, char **result); +int dynCommon_parseName(FILE *stream, char **result); /** * @brief Parse the name of dynamic type from the given stream. @@ -66,7 +64,7 @@ CELIX_DFI_EXPORT int dynCommon_parseName(FILE *stream, char **result); * @return 0 if successful, otherwise 1. * @alsoseee dynCommon_parseName */ -CELIX_DFI_EXPORT int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char **result); +int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char **result); /** * @brief Parse the name and value of a name-value pair from the given stream. The name is only allowed to contain [a-zA-Z0-9_]. @@ -80,7 +78,24 @@ CELIX_DFI_EXPORT int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acc * @param[out] value The value of the name-value pair. * @return 0 if successful, otherwise 1. */ -CELIX_DFI_EXPORT int dynCommon_parseNameValue(FILE *stream, char **name, char **value); +int dynCommon_parseNameValue(FILE *stream, char **name, char **value); + +/** + * @brief Parses a section of name-value pairs from the given stream. The name is only allowed to contain [a-zA-Z0-9_]. + * + * This function reads from the provided stream until it encounters a new section or EOF. + * Each name-value pair is added to the provided namvals_head structure. + * + * The caller is responsible for managing the memory of the namvals_head structure. + * Use `dynCommon_clearNamValHead` to clear the name-value pairs when they are no longer needed. + * + * In case of an error, an error message is added to celix_err. + * + * @param[in] stream The input stream. + * @param[out] head The namvals_head structure where the parsed name-value pairs will be stored. + * @return 0 if successful, otherwise 1. + */ +int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head); /** * @brief Eat the given character from the given stream. @@ -91,14 +106,14 @@ CELIX_DFI_EXPORT int dynCommon_parseNameValue(FILE *stream, char **name, char ** * @param[in] c The character to be eaten. * @return 0 if successful, otherwise 1. */ -CELIX_DFI_EXPORT int dynCommon_eatChar(FILE *stream, int c); +int dynCommon_eatChar(FILE *stream, int c); /** * @brief Clear the given name-value pairs. * * @param[in] head The name-value pairs to be cleared. */ -CELIX_DFI_EXPORT void dynCommon_clearNamValHead(struct namvals_head *head); +void dynCommon_clearNamValHead(struct namvals_head *head); #ifdef __cplusplus } diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 7da0403c8..b0ab0fa64 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -38,7 +38,6 @@ static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream) static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseHeader(dyn_interface_type *intf, FILE *stream); -static int dynInterface_parseNameValueSection(dyn_interface_type *intf, FILE *stream, struct namvals_head *head); static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, char **value); int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { @@ -168,55 +167,11 @@ static int dynInterface_parseSection(dyn_interface_type *intf, FILE *stream) { } static int dynInterface_parseHeader(dyn_interface_type *intf, FILE *stream) { - return dynInterface_parseNameValueSection(intf, stream, &intf->header); + return dynCommon_parseNameValueSection(stream, &intf->header); } static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream) { - return dynInterface_parseNameValueSection(intf, stream, &intf->annotations); -} - -static int dynInterface_parseNameValueSection(dyn_interface_type *intf, FILE *stream, struct namvals_head *head) { - int status = OK; - - int peek = fgetc(stream); - while (peek != ':' && peek != EOF) { - ungetc(peek, stream); - - char *name = NULL; - char *value = NULL; - status = dynCommon_parseNameValue(stream, &name, &value); - - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); - } - - struct namval_entry *entry = NULL; - if (status == OK) { - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->name = name; - entry->value = value; - TAILQ_INSERT_TAIL(head, entry, entries); - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for namval entry"); - } - } - - if (status != OK) { - if (name != NULL) { - free(name); - } - if (value != NULL) { - free(value); - } - break; - } - peek = fgetc(stream); - } - ungetc(peek, stream); - - return status; + return dynCommon_parseNameValueSection(stream, &intf->annotations); } static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream) { diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c index e020a8122..886428bdc 100644 --- a/libs/dfi/src/dyn_message.c +++ b/libs/dfi/src/dyn_message.c @@ -42,7 +42,6 @@ static int dynMessage_parseAnnotations(dyn_message_type *msg, FILE *stream); static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream); static int dynMessage_parseMessage(dyn_message_type *msg, FILE *stream); static int dynMessage_parseHeader(dyn_message_type *msg, FILE *stream); -static int dynMessage_parseNameValueSection(dyn_message_type *msg, FILE *stream, struct namvals_head *head); static int dynMessage_checkMessage(dyn_message_type *msg); static int dynMessage_getEntryForHead(struct namvals_head *head, const char *name, char **value); @@ -165,55 +164,11 @@ static int dynMessage_parseSection(dyn_message_type *msg, FILE *stream) { } static int dynMessage_parseHeader(dyn_message_type *msg, FILE *stream) { - return dynMessage_parseNameValueSection(msg, stream, &msg->header); + return dynCommon_parseNameValueSection(stream, &msg->header); } static int dynMessage_parseAnnotations(dyn_message_type *msg, FILE *stream) { - return dynMessage_parseNameValueSection(msg, stream, &msg->annotations); -} - -static int dynMessage_parseNameValueSection(dyn_message_type *msg, FILE *stream, struct namvals_head *head) { - int status = OK; - - int peek = fgetc(stream); - while (peek != ':' && peek != EOF) { - ungetc(peek, stream); - - char *name = NULL; - char *value = NULL; - status = dynCommon_parseNameValue(stream, &name, &value); - - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); - } - - struct namval_entry *entry = NULL; - if (status == OK) { - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->name = name; - entry->value = value; - TAILQ_INSERT_TAIL(head, entry, entries); - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for namval entry"); - } - } - - if (status != OK) { - if (name != NULL) { - free(name); - } - if (value != NULL) { - free(value); - } - break; - } - peek = fgetc(stream); - } - ungetc(peek, stream); - - return status; + return dynCommon_parseNameValueSection(stream, &msg->annotations); } static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { From 45bc7d2fffe28061ce0996ef80bb70ffc3f9b258 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 18 Dec 2023 20:26:56 +0800 Subject: [PATCH 07/62] Add more tests for dyn_common.c --- libs/dfi/gtest/CMakeLists.txt | 2 + libs/dfi/gtest/src/dyn_common_ei_tests.cc | 84 +++++++++++ libs/dfi/gtest/src/dyn_common_tests.cc | 149 +++++++++++++++++++ libs/dfi/src/dyn_common.c | 102 +++++++------ libs/dfi/src/dyn_common.h | 16 +- libs/error_injector/stdio/CMakeLists.txt | 1 + libs/error_injector/stdio/include/stdio_ei.h | 2 + libs/error_injector/stdio/src/stdio_ei.cc | 10 ++ 8 files changed, 299 insertions(+), 67 deletions(-) create mode 100644 libs/dfi/gtest/src/dyn_common_ei_tests.cc create mode 100644 libs/dfi/gtest/src/dyn_common_tests.cc diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 5090bf3d5..9d3def82c 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(test_dfi src/dyn_message_tests.cpp src/json_serializer_tests.cpp src/json_rpc_tests.cpp + src/dyn_common_tests.cc ) @@ -40,6 +41,7 @@ setup_target_for_coverage(test_dfi SCAN_DIR ..) if (EI_TESTS) add_executable(test_dfi_with_ei src/dyn_interface_ei_tests.cc + src/dyn_common_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut diff --git a/libs/dfi/gtest/src/dyn_common_ei_tests.cc b/libs/dfi/gtest/src/dyn_common_ei_tests.cc new file mode 100644 index 000000000..4efdf5d53 --- /dev/null +++ b/libs/dfi/gtest/src/dyn_common_ei_tests.cc @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_common.h" +#include "celix_err.h" +#include "malloc_ei.h" +#include "stdio_ei.h" + +#include + +class DynCommonErrorInjectionTestSuite : public ::testing::Test { +protected: + char* result{nullptr}; + FILE* stream{nullptr}; + + DynCommonErrorInjectionTestSuite() = default; + + ~DynCommonErrorInjectionTestSuite() override { + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_ei_expect_fclose(nullptr, 0, 0); + celix_ei_expect_fputc(nullptr, 0, 0); + celix_ei_expect_open_memstream(nullptr, 0, nullptr); + if (stream != nullptr) { + fclose(stream); + } + if (result != nullptr) { + free(result); + } + } + // delete other constructors and assign operators + DynCommonErrorInjectionTestSuite(DynCommonErrorInjectionTestSuite const&) = delete; + DynCommonErrorInjectionTestSuite(DynCommonErrorInjectionTestSuite&&) = delete; + DynCommonErrorInjectionTestSuite& operator=(DynCommonErrorInjectionTestSuite const&) = delete; + DynCommonErrorInjectionTestSuite& operator=(DynCommonErrorInjectionTestSuite&&) = delete; +}; + +TEST_F(DynCommonErrorInjectionTestSuite, ParseNameErrors) { + stream = fmemopen((void *) "valid_name", 10, "r"); + + // not enough memory for name + celix_ei_expect_open_memstream((void *) dynCommon_parseName, 1, nullptr); + ASSERT_EQ(dynCommon_parseName(stream, &result), 1); + std::string msg = "Error creating mem stream for name. "; + msg += strerror(ENOMEM); + ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); + + // fail to put character into name + celix_ei_expect_fputc((void *) dynCommon_parseName, 1, EOF); + ASSERT_EQ(dynCommon_parseName(stream, &result), 1); + ASSERT_STREQ("Error writing to mem stream for name.", celix_err_popLastError()); + + // fail to close name stream + celix_ei_expect_fclose((void *) dynCommon_parseName, 1, EOF); + ASSERT_EQ(dynCommon_parseName(stream, &result), 1); + msg = "Error creating mem stream for name. "; + msg += strerror(ENOSPC); + ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); +} + +TEST_F(DynCommonErrorInjectionTestSuite, ParseNameValueSectionErrors) { + stream = fmemopen((void*)"name1=value1\nname2=value2\n", 26, "r"); + struct namvals_head head; + TAILQ_INIT(&head); + // not enough memory for namval_entry when parsing name value section + celix_ei_expect_calloc((void *) dynCommon_parseNameValueSection, 0, nullptr, 1); + ASSERT_EQ(dynCommon_parseNameValueSection(stream, &head), 1); + ASSERT_STREQ("Error allocating memory for namval entry", celix_err_popLastError()); +} diff --git a/libs/dfi/gtest/src/dyn_common_tests.cc b/libs/dfi/gtest/src/dyn_common_tests.cc new file mode 100644 index 000000000..2848ab19a --- /dev/null +++ b/libs/dfi/gtest/src/dyn_common_tests.cc @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_common.h" +#include "celix_err.h" +#include +#include +#include + +class DynCommonTests : public ::testing::Test { +protected: + char* result{nullptr}; + FILE* stream{nullptr}; + DynCommonTests() { + } + ~DynCommonTests() override { + if (stream != nullptr) { + fclose(stream); + } + if (result != nullptr) { + free(result); + } + } + // delete other constructors and assign operators + DynCommonTests(DynCommonTests const&) = delete; + DynCommonTests(DynCommonTests&&) = delete; + DynCommonTests& operator=(DynCommonTests const&) = delete; + DynCommonTests& operator=(DynCommonTests&&) = delete; +}; + +TEST_F(DynCommonTests, ParseValidName) { + stream = fmemopen((void*)"valid_name", 10, "r"); + ASSERT_EQ(dynCommon_parseName(stream, &result), 0); + ASSERT_STREQ(result, "valid_name"); +} + +TEST_F(DynCommonTests, ParseNameTillInvalidCharacter) { + stream = fmemopen((void*)"invalid-name", 12, "r"); + ASSERT_EQ(dynCommon_parseName(stream, &result), 0); + ASSERT_STREQ("invalid", result); + ASSERT_EQ('-', fgetc(stream)); +} + +TEST_F(DynCommonTests, ParseNameWithExtraAllowableCharacters) { + stream = fmemopen((void*)"invalid-@name", 13, "r"); + ASSERT_EQ(dynCommon_parseNameAlsoAccept(stream, "@-", &result), 0); + ASSERT_STREQ("invalid-@name", result); + ASSERT_EQ(EOF, fgetc(stream)); + +} + +TEST_F(DynCommonTests, ParseEmptyName) { + stream = fmemopen((void*)"", 1, "r"); + ASSERT_EQ(dynCommon_parseName(stream, &result), 1); + ASSERT_EQ('\0', fgetc(stream)); +} + +TEST_F(DynCommonTests, EatCharValid) { + stream = fmemopen((void*)"valid", 5, "r"); + ASSERT_EQ(dynCommon_eatChar(stream, 'v'), 0); + ASSERT_EQ(dynCommon_eatChar(stream, 'a'), 0); + ASSERT_EQ(dynCommon_eatChar(stream, 'l'), 0); + ASSERT_EQ(dynCommon_eatChar(stream, 'i'), 0); + ASSERT_EQ(dynCommon_eatChar(stream, 'd'), 0); + ASSERT_FALSE(feof(stream)); + ASSERT_EQ(dynCommon_eatChar(stream, EOF), 0); + ASSERT_TRUE(feof(stream)); +} + +TEST_F(DynCommonTests, EatCharFromEmptyString) { + stream = fmemopen((void*)"", 1, "r"); + ASSERT_EQ(dynCommon_eatChar(stream, '\0'), 0); + ASSERT_FALSE(feof(stream)); + ASSERT_EQ(dynCommon_eatChar(stream, EOF), 0); + ASSERT_TRUE(feof(stream)); +} + +TEST_F(DynCommonTests, ParseNameValueSection) { + stream = fmemopen((void*)"name1=value1\nname2=value2\n", 26, "r"); + struct namvals_head head; + TAILQ_INIT(&head); + ASSERT_EQ(dynCommon_parseNameValueSection(stream, &head), 0); + + struct namval_entry *entry = TAILQ_FIRST(&head); + ASSERT_STREQ("name1", entry->name); + ASSERT_STREQ("value1", entry->value); + + entry = TAILQ_NEXT(entry, entries); + ASSERT_STREQ("name2", entry->name); + ASSERT_STREQ("value2", entry->value); + + dynCommon_clearNamValHead(&head); +} + +TEST_F(DynCommonTests, ParseNameValueSectionWithPairWithEmptyName) { + stream = fmemopen((void*)"=value1\nname2=value2\n", 22, "r"); + struct namvals_head head; + TAILQ_INIT(&head); + ASSERT_EQ(dynCommon_parseNameValueSection(stream, &head), 1); + ASSERT_STREQ("Parsed empty name", celix_err_popLastError()); + + dynCommon_clearNamValHead(&head); +} + +TEST_F(DynCommonTests, ParseNameValueSectionWithPairWithEmptyValue) { + stream = fmemopen((void*)"name1=\nname2=value2\n", 22, "r"); + struct namvals_head head; + TAILQ_INIT(&head); + ASSERT_EQ(dynCommon_parseNameValueSection(stream, &head), 1); + ASSERT_STREQ("Parsed empty name", celix_err_popLastError()); + + dynCommon_clearNamValHead(&head); +} + +TEST_F(DynCommonTests, ParseNameValueSectionWithPairMissingEquality) { + stream = fmemopen((void*)"name1 value1\nname2=value2\n", 22, "r"); + struct namvals_head head; + TAILQ_INIT(&head); + ASSERT_EQ(dynCommon_parseNameValueSection(stream, &head), 1); + ASSERT_STREQ("Error parsing, expected token '=' got ' ' at position 6", celix_err_popLastError()); + + dynCommon_clearNamValHead(&head); +} + +TEST_F(DynCommonTests, ParseNameValueSectionWithPairMissingNewline) { + stream = fmemopen((void*)"name1=value1 name2=value2\n", 26, "r"); + struct namvals_head head; + TAILQ_INIT(&head); + ASSERT_EQ(dynCommon_parseNameValueSection(stream, &head), 1); + ASSERT_STREQ("Error parsing, expected token '\n' got ' ' at position 13", celix_err_popLastError()); + + dynCommon_clearNamValHead(&head); +} \ No newline at end of file diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 212e771ca..61029a5ce 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -19,6 +19,7 @@ #include "dyn_common.h" #include "celix_err.h" +#include "celix_stdio_cleanup.h" #include "celix_stdlib_cleanup.h" #include @@ -35,62 +36,59 @@ int dynCommon_parseName(FILE *stream, char **result) { } int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char **result) { - int status = OK; - - char *buf = NULL; + celix_autofree char *buf = NULL; size_t size = 0; - int strLen = 0; - FILE *name = open_memstream(&buf, &size); + celix_autoptr(FILE) name = open_memstream(&buf, &size); if (name == NULL) { celix_err_pushf("Error creating mem stream for name. %s", strerror(errno)); return ERROR; } int c = getc(stream); - while (isalnum(c) || c == '_' || (acceptedChars != NULL && strchr(acceptedChars, c) != NULL)) { - fputc(c, name); + while (c != EOF && (isalnum(c) || c == '_' || (acceptedChars != NULL && strchr(acceptedChars, c) != NULL))) { + if(fputc(c, name) == EOF) { + celix_err_push("Error writing to mem stream for name."); + return ERROR; + } c = getc(stream); - strLen += 1; } - fflush(name); - fclose(name); - ungetc(c, stream); - - if (status == OK) { - if (strLen == 0) { - status = ERROR; - celix_err_pushf("Parsed empty name"); - } + if (c != EOF) { + ungetc(c, stream); + } + if(fclose(celix_steal_ptr(name)) != 0) { + celix_err_pushf("Error creating mem stream for name. %s", strerror(errno)); + return ERROR; } - if (status == OK) { - *result = buf; - } else if (buf != NULL) { - free(buf); + if (size == 0) { + celix_err_pushf("Parsed empty name"); + return ERROR; } - return status; + *result = celix_steal_ptr(buf); + + return OK; } -int dynCommon_parseNameValue(FILE *stream, char **outName, char **outValue) { +static int dynCommon_parseNameValue(FILE *stream, char **outName, char **outValue) { int status; celix_autofree char *name = NULL; celix_autofree char *value = NULL; - - status = dynCommon_parseName(stream, &name); - if (status == OK) { - status = dynCommon_eatChar(stream, '='); - } - if (status == OK) { + do { + if ((status = dynCommon_parseName(stream, &name)) != OK) { + break; + } + if ((status = dynCommon_eatChar(stream, '=')) != OK) { + break; + } const char *valueAcceptedChars = ".<>{}[]?;:~!@#$%^&*()_+-=,./\\'\""; - - status = dynCommon_parseNameAlsoAccept(stream, valueAcceptedChars, &value); //NOTE use different more lenient function e.g. only stop at '\n' ? - } - - if (status == OK) { + //NOTE use different more lenient function e.g. only stop at '\n' ? + if ((status = dynCommon_parseNameAlsoAccept(stream, valueAcceptedChars, &value)) != OK) { + break; + } *outName = celix_steal_ptr(name); *outValue = celix_steal_ptr(value); - } + } while(false); return status; } @@ -129,31 +127,31 @@ int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { celix_autofree char *name = NULL; celix_autofree char *value = NULL; - status = dynCommon_parseNameValue(stream, &name, &value); - - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); + if ((status = dynCommon_parseNameValue(stream, &name, &value)) != OK) { + break; } - struct namval_entry *entry = NULL; - if (status == OK) { - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->name = celix_steal_ptr(name); - entry->value = celix_steal_ptr(value); - TAILQ_INSERT_TAIL(head, entry, entries); - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for namval entry"); - } + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + break; } - if (status != OK) { + struct namval_entry *entry = NULL; + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + status = ERROR; + celix_err_pushf("Error allocating memory for namval entry"); break; } + + entry->name = celix_steal_ptr(name); + entry->value = celix_steal_ptr(value); + TAILQ_INSERT_TAIL(head, entry, entries); + peek = fgetc(stream); } - ungetc(peek, stream); + if (peek != EOF) { + ungetc(peek, stream); + } return status; } diff --git a/libs/dfi/src/dyn_common.h b/libs/dfi/src/dyn_common.h index 73e2fd933..ab74af5f9 100644 --- a/libs/dfi/src/dyn_common.h +++ b/libs/dfi/src/dyn_common.h @@ -40,7 +40,7 @@ struct namval_entry { /** * @brief Parse the name of dynamic type from the given stream. The name is only allowed to contain [a-zA-Z0-9_]. * - * The caller is the owner of the dynamic type name and use `free` deallocate the memory. + * The caller is the owner of the dynamic type name and use `free` to deallocate the memory. * * In case of an error, an error message is added to celix_err. * @@ -66,20 +66,6 @@ int dynCommon_parseName(FILE *stream, char **result); */ int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char **result); -/** - * @brief Parse the name and value of a name-value pair from the given stream. The name is only allowed to contain [a-zA-Z0-9_]. - * - * The caller is the owner of the name and value and use `free` deallocate the memory. - * - * In case of an error, an error message is added to celix_err. - * - * @param[in] stream The input stream. - * @param[out] name The name of the name-value pair. - * @param[out] value The value of the name-value pair. - * @return 0 if successful, otherwise 1. - */ -int dynCommon_parseNameValue(FILE *stream, char **name, char **value); - /** * @brief Parses a section of name-value pairs from the given stream. The name is only allowed to contain [a-zA-Z0-9_]. * diff --git a/libs/error_injector/stdio/CMakeLists.txt b/libs/error_injector/stdio/CMakeLists.txt index 8a4675b2e..f4824197e 100644 --- a/libs/error_injector/stdio/CMakeLists.txt +++ b/libs/error_injector/stdio/CMakeLists.txt @@ -30,5 +30,6 @@ target_link_options(stdio_ei INTERFACE LINKER:--wrap,fread LINKER:--wrap,fputc LINKER:--wrap,fputs + LINKER:--wrap,fclose ) add_library(Celix::stdio_ei ALIAS stdio_ei) diff --git a/libs/error_injector/stdio/include/stdio_ei.h b/libs/error_injector/stdio/include/stdio_ei.h index 210e546b9..321a39d9b 100644 --- a/libs/error_injector/stdio/include/stdio_ei.h +++ b/libs/error_injector/stdio/include/stdio_ei.h @@ -44,6 +44,8 @@ CELIX_EI_DECLARE(fputc, int); CELIX_EI_DECLARE(fputs, int); +CELIX_EI_DECLARE(fclose, int); + #ifdef __cplusplus } #endif diff --git a/libs/error_injector/stdio/src/stdio_ei.cc b/libs/error_injector/stdio/src/stdio_ei.cc index a3d3ee779..d78f7bbc2 100644 --- a/libs/error_injector/stdio/src/stdio_ei.cc +++ b/libs/error_injector/stdio/src/stdio_ei.cc @@ -98,4 +98,14 @@ int __wrap_fputs(const char* __s, FILE* __stream) { return __real_fputs(__s, __stream); } +int __real_fclose(FILE* __stream); +CELIX_EI_DEFINE(fclose, int) +int __wrap_fclose(FILE* __stream) { + int rc = __real_fclose(__stream); //note always call real fclose to ensure the stream is closed. + errno = ENOSPC; + CELIX_EI_IMPL(fclose); + errno = 0; + return rc; +} + } From 2a0314d7faef244bfec81281bf5f42dde9a4c552 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 19 Dec 2023 13:53:05 +0800 Subject: [PATCH 08/62] Add const qualifier to dyn_interface.h and apply early return to dynInterface_parse. --- .../src/export_registration_dfi.c | 4 +- .../src/import_registration_dfi.c | 37 ++++++------ .../src/rsa_json_rpc_endpoint_impl.c | 2 +- .../src/rsa_json_rpc_proxy_impl.c | 4 +- .../descriptors/invalids/garbage.descriptor | 1 + libs/dfi/gtest/src/dyn_interface_tests.cpp | 32 ++++++---- libs/dfi/gtest/src/json_rpc_tests.cpp | 18 +++--- libs/dfi/include/dyn_interface.h | 12 ++-- libs/dfi/src/dyn_interface.c | 60 +++++++++---------- libs/dfi/src/json_rpc.c | 2 +- 10 files changed, 89 insertions(+), 83 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/invalids/garbage.descriptor diff --git a/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c index 9713e4477..b93fefbee 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c @@ -111,7 +111,7 @@ celix_status_t exportRegistration_create(celix_log_helper_t *helper, service_ref if (status == CELIX_SUCCESS) { /* Add the interface version as a property in the properties_map */ - char* intfVersion = NULL; + const char* intfVersion = NULL; dynInterface_getVersionString(reg->intf, &intfVersion); const char *serviceVersion = celix_properties_get(endpoint->properties,(char*) CELIX_FRAMEWORK_SERVICE_VERSION, NULL); if (serviceVersion != NULL) { @@ -192,7 +192,7 @@ celix_status_t exportRegistration_call(export_registration_t *export, char *data //printf("calling for '%s'\n"); if (export->logFile != NULL) { static int callCount = 0; - char *name = NULL; + const char *name = NULL; dynInterface_getName(export->intf, &name); fprintf(export->logFile, "REMOTE CALL %i\n\tservice=%s\n\tservice_id=%s\n\trequest_payload=%s\n\trequest_response=%s\n\tstatus=%i\n", callCount, name, export->servId, data, response, status); fflush(export->logFile); diff --git a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c index 5cc0327e7..8c3deb46b 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c @@ -20,7 +20,8 @@ #include #include #include -#include "version.h" +#include "celix_version.h" +#include "celix_stdlib_cleanup.h" #include "dyn_interface.h" #include "import_registration.h" #include "import_registration_dfi.h" @@ -32,7 +33,7 @@ struct import_registration { celix_bundle_context_t *context; endpoint_description_t * endpoint; //TODO owner? -> free when destroyed const char *classObject; //NOTE owned by endpoint - version_pt version; + celix_version_t* version; send_func_type send; void *sendHandle; @@ -87,8 +88,11 @@ celix_status_t importRegistration_create( remoteInterceptorsHandler_create(context, ®->interceptorsHandler); celixThreadMutex_create(®->proxiesMutex, NULL); - status = version_createVersionFromString((char*)serviceVersion,&(reg->version)); - + // serviceVersion == NULL is allowed, check TEST_F(RsaDfiTests, ImportService) + reg->version = celix_version_createVersionFromString(serviceVersion); + if (serviceVersion != NULL && reg->version == NULL) { + status = CELIX_ILLEGAL_ARGUMENT; + } reg->factorySvcId = -1; reg->factory.handle = reg; reg->factory.getService = importRegistration_getService; @@ -135,7 +139,7 @@ static void importRegistration_destroyCallback(void* data) { pthread_mutex_destroy(&import->proxiesMutex); if (import->version != NULL) { - version_destroy(import->version); + celix_version_destroy(import->version); } free(import); } @@ -230,21 +234,20 @@ static celix_status_t importRegistration_createProxy(import_registration_t *impo } /* Check if the imported service version is compatible with the one in the consumer descriptor */ - version_pt consumerVersion = NULL; + const celix_version_t* consumerVersion = NULL; bool isCompatible = false; dynInterface_getVersion(intf,&consumerVersion); - version_isCompatible(consumerVersion,import->version,&isCompatible); + isCompatible = celix_version_isCompatible(consumerVersion,import->version); if(!isCompatible){ - char* cVerString = NULL; - char* pVerString = NULL; - version_toString(consumerVersion,&cVerString); - version_toString(import->version,&pVerString); - printf("Service version mismatch: consumer has %s, provider has %s. NOT creating proxy.\n",cVerString,pVerString); - dynInterface_destroy(intf); - free(cVerString); - free(pVerString); - status = CELIX_SERVICE_EXCEPTION; + celix_autofree char* cVerString = NULL; + celix_autofree char* pVerString = NULL; + cVerString = celix_version_toString(consumerVersion); + pVerString = import->version != NULL ? celix_version_toString(import->version) : NULL; + printf("Service version mismatch: consumer has %s, provider has %s. NOT creating proxy.\n", + cVerString,pVerString != NULL ? pVerString : "NA"); + dynInterface_destroy(intf); + status = CELIX_SERVICE_EXCEPTION; } struct service_proxy *proxy = NULL; @@ -268,7 +271,7 @@ static celix_status_t importRegistration_createProxy(import_registration_t *impo void **serv = proxy->service; serv[0] = import; - struct methods_head *list = NULL; + const struct methods_head *list = NULL; dynInterface_methods(proxy->intf, &list); struct method_entry *entry = NULL; void (*fn)(void) = NULL; diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c index f457e8153..bc062d6f3 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c @@ -173,7 +173,7 @@ static void rsaJsonRpcEndpoint_addSvcWithOwner(void *handle, void *service, } // Check version - char *intfVersion = NULL; + const char *intfVersion = NULL; int ret = dynInterface_getVersionString(intfType, &intfVersion); if (ret != 0) { celix_logHelper_logTssErrors(endpoint->logHelper, CELIX_LOG_LEVEL_ERROR); diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c index 7d2a4b0e6..77d390c1e 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c @@ -318,7 +318,7 @@ static celix_status_t rsaJsonRpcProxy_create(rsa_json_rpc_proxy_factory_t *proxy celix_logHelper_error(proxyFactory->logHelper, "Proxy: Error converting service version type. %d.", status); return status; } - celix_version_t *consumerVersion = NULL; + const celix_version_t *consumerVersion = NULL; bool isCompatible = false; dynInterface_getVersion(intfType,&consumerVersion); isCompatible = celix_version_isCompatible(consumerVersion, providerVersion); @@ -337,7 +337,7 @@ static celix_status_t rsaJsonRpcProxy_create(rsa_json_rpc_proxy_factory_t *proxy } celix_autofree void **service = (void **)proxy->service; service[0] = proxy; - struct methods_head *list = NULL; + const struct methods_head *list = NULL; dynInterface_methods(intfType, &list); struct method_entry *entry = NULL; void (*fn)(void) = NULL; diff --git a/libs/dfi/gtest/descriptors/invalids/garbage.descriptor b/libs/dfi/gtest/descriptors/invalids/garbage.descriptor new file mode 100644 index 000000000..88c65efb1 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/garbage.descriptor @@ -0,0 +1 @@ +this is garbage diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index eef8e3fe4..b51eedf72 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -31,23 +31,24 @@ extern "C" { #include "dyn_common.h" #include "dyn_interface.h" -#include "version.h" +#include "celix_version.h" static void checkInterfaceVersion(dyn_interface_type* dynIntf, const char* v) { int status; - char *version = NULL; + const char *version = NULL; status = dynInterface_getVersionString(dynIntf, &version); ASSERT_EQ(0, status); ASSERT_STREQ(v, version); - version_pt msgVersion = NULL, localMsgVersion = NULL; + const celix_version_t* msgVersion = NULL; + celix_version_t* localMsgVersion = NULL; int cmpVersion = -1; - version_createVersionFromString(version, &localMsgVersion); + localMsgVersion = celix_version_createVersionFromString(version); status = dynInterface_getVersion(dynIntf, &msgVersion); ASSERT_EQ(0, status); - version_compareTo(msgVersion, localMsgVersion, &cmpVersion); + cmpVersion = celix_version_compareTo(msgVersion, localMsgVersion); ASSERT_EQ(cmpVersion, 0); - version_destroy(localMsgVersion); + celix_version_destroy(localMsgVersion); } static void test1(void) { @@ -59,24 +60,24 @@ extern "C" { ASSERT_EQ(0, status); fclose(desc); - char *name = NULL; + const char *name = NULL; status = dynInterface_getName(dynIntf, &name); ASSERT_EQ(0, status); ASSERT_STREQ("calculator", name); - checkInterfaceVersion(dynIntf,"1.0.0"); + checkInterfaceVersion(dynIntf,"1.0.0"); - char *annVal = NULL; + const char *annVal = NULL; status = dynInterface_getAnnotationEntry(dynIntf, "classname", &annVal); ASSERT_EQ(0, status); ASSERT_STREQ("org.example.Calculator", annVal); - char *nonExist = NULL; + const char *nonExist = NULL; status = dynInterface_getHeaderEntry(dynIntf, "nonExisting", &nonExist); ASSERT_TRUE(status != 0); ASSERT_TRUE(nonExist == NULL); - struct methods_head *list = NULL; + const struct methods_head *list = NULL; status = dynInterface_methods(dynIntf, &list); ASSERT_TRUE(status == 0); ASSERT_TRUE(list != NULL); @@ -170,10 +171,15 @@ extern "C" { desc = fopen("descriptors/invalids/invalidVersion.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); - ASSERT_EQ(1, status); //Invalid meta type doesn't generate errors, just warnings + ASSERT_EQ(1, status); fclose(desc); desc=NULL; + /* garbage descriptor */ + desc = fopen("descriptors/invalids/garbage.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); + fclose(desc); desc=NULL; } } diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 32e45663f..4af3e29c1 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -355,7 +355,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -397,7 +397,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -436,7 +436,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -494,7 +494,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -547,7 +547,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -580,7 +580,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -623,7 +623,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -689,7 +689,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; @@ -897,7 +897,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - struct methods_head *head; + const struct methods_head *head; dynInterface_methods(intf, &head); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index 9a27d0801..49eb517dd 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -85,7 +85,7 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_interface_type, dynInterface_destroy); * @param[out] name The name of the dynamic interface type instance. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getName(dyn_interface_type *intf, char **name); +CELIX_DFI_EXPORT int dynInterface_getName(dyn_interface_type *intf, const char **name); /** * @brief Gets the version of the given dynamic interface type instance. @@ -96,7 +96,7 @@ CELIX_DFI_EXPORT int dynInterface_getName(dyn_interface_type *intf, char **name) * @param[out] version The version of the dynamic interface type instance. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getVersion(dyn_interface_type *intf, celix_version_t** version); +CELIX_DFI_EXPORT int dynInterface_getVersion(dyn_interface_type *intf, const celix_version_t** version); /** * @brief Gets the version string of the given dynamic interface type instance. @@ -109,7 +109,7 @@ CELIX_DFI_EXPORT int dynInterface_getVersion(dyn_interface_type *intf, celix_ver * @param[out] version The version string of the dynamic interface type instance. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getVersionString(dyn_interface_type *intf, char **version); +CELIX_DFI_EXPORT int dynInterface_getVersionString(dyn_interface_type *intf, const char **version); /** * @brief Gets the value corresponding to the specified name, which comes from the header section of the given dynamic interface type instance. @@ -123,7 +123,7 @@ CELIX_DFI_EXPORT int dynInterface_getVersionString(dyn_interface_type *intf, cha * @param[out] value The value corresponding to the specified name. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getHeaderEntry(dyn_interface_type *intf, const char *name, char **value); +CELIX_DFI_EXPORT int dynInterface_getHeaderEntry(dyn_interface_type *intf, const char *name, const char **value); /** * @brief Gets the value corresponding to the specified name, which comes from the annotation section of the given dynamic interface type instance. @@ -137,7 +137,7 @@ CELIX_DFI_EXPORT int dynInterface_getHeaderEntry(dyn_interface_type *intf, const * @param[out] value The value corresponding to the specified name. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(dyn_interface_type *intf, const char *name, char **value); +CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(dyn_interface_type *intf, const char *name, const char **value); /** * @brief Gets the methods of the given dynamic interface type instance. @@ -148,7 +148,7 @@ CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(dyn_interface_type *intf, c * @param[out] list The method list of the dynamic interface type instance. * @return 0. */ -CELIX_DFI_EXPORT int dynInterface_methods(dyn_interface_type *intf, struct methods_head **list); +CELIX_DFI_EXPORT int dynInterface_methods(dyn_interface_type *intf, const struct methods_head **list); /** * @brief Returns the number of methods for the given dynamic interface type instance. diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index b0ab0fa64..bbedfae93 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -20,6 +20,7 @@ #include "dyn_interface.h" #include "celix_err.h" +#include #include #include @@ -38,7 +39,7 @@ static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream) static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseHeader(dyn_interface_type *intf, FILE *stream); -static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, char **value); +static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, const char **value); int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { int status = OK; @@ -57,38 +58,33 @@ int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { char peek = (char)fgetc(descriptor); while (peek == ':') { ungetc(peek, descriptor); - status = dynInterface_parseSection(intf, descriptor); - if (status == OK) { - peek = (char)fgetc(descriptor); - } else { - break; + if ((status = dynInterface_parseSection(intf, descriptor)) != OK) { + return status; } + peek = (char)fgetc(descriptor); } - if (status == OK) { - status = dynCommon_eatChar(descriptor, EOF); - } - - if (status == OK) { - status = dynInterface_checkInterface(intf); + if (peek != EOF) { + celix_err_pushf("Descriptor does not start with ':'"); + return ERROR; } - if (status == OK) { /* We are sure that version field is present in the header */ - char* version = NULL; - dynInterface_getVersionString(intf,&version); - if (version != NULL){ - intf->version = celix_version_createVersionFromString(version); - status = intf->version != NULL ? OK : ERROR; - } - if (status == ERROR) { - celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); - } + if ((status = dynInterface_checkInterface(intf)) != OK) { + return status; } - if (status == OK) { - *out = celix_steal_ptr(intf); + // We are sure that version field is present in the header + const char* version = NULL; + status = dynInterface_getVersionString(intf,&version); + assert(status == OK); + assert(version != NULL); + intf->version = celix_version_createVersionFromString(version); + if (intf->version == NULL) { + celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); + return ERROR; } - return status; + *out = celix_steal_ptr(intf); + return OK; } int dynInterface_checkInterface(dyn_interface_type *intf) { @@ -333,29 +329,29 @@ void dynInterface_destroy(dyn_interface_type *intf) { } } -int dynInterface_getName(dyn_interface_type *intf, char **out) { +int dynInterface_getName(dyn_interface_type *intf, const char **out) { return dynInterface_getEntryForHead(&intf->header, "name", out); } -int dynInterface_getVersion(dyn_interface_type* intf , celix_version_t** version){ +int dynInterface_getVersion(dyn_interface_type* intf , const celix_version_t** version){ // aseert(intf->version != NULL); *version = intf->version; return OK; } -int dynInterface_getVersionString(dyn_interface_type *intf, char **version) { +int dynInterface_getVersionString(dyn_interface_type *intf, const char **version) { return dynInterface_getEntryForHead(&intf->header, "version", version); } -int dynInterface_getHeaderEntry(dyn_interface_type *intf, const char *name, char **value) { +int dynInterface_getHeaderEntry(dyn_interface_type *intf, const char *name, const char **value) { return dynInterface_getEntryForHead(&intf->header, name, value); } -int dynInterface_getAnnotationEntry(dyn_interface_type *intf, const char *name, char **value) { +int dynInterface_getAnnotationEntry(dyn_interface_type *intf, const char *name, const char **value) { return dynInterface_getEntryForHead(&intf->annotations, name, value); } -static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, char **out) { +static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, const char **out) { int status = OK; char *value = NULL; struct namval_entry *entry = NULL; @@ -374,7 +370,7 @@ static int dynInterface_getEntryForHead(struct namvals_head *head, const char *n return status; } -int dynInterface_methods(dyn_interface_type *intf, struct methods_head **list) { +int dynInterface_methods(dyn_interface_type *intf, const struct methods_head **list) { int status = OK; *list = &intf->methods; return status; diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 359ca0ef9..0d3525af9 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -63,7 +63,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c return ERROR; } - struct methods_head *methods = NULL; + const struct methods_head *methods = NULL; dynInterface_methods(intf, &methods); struct method_entry *entry = NULL; struct method_entry *method = NULL; From e27d73afceeec7ff92c6fedd771794c28e5f2b88 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 20 Dec 2023 22:11:25 +0800 Subject: [PATCH 09/62] Add const qualifier, apply early return error handling patterns, and improve interface ergonomic to dyn_interface. --- .../src/export_registration_dfi.c | 6 +- .../src/import_registration_dfi.c | 9 +- .../gtest/src/RsaJsonRpcUnitTestSuite.cc | 33 --- .../src/rsa_json_rpc_endpoint_impl.c | 8 +- .../src/rsa_json_rpc_proxy_impl.c | 19 +- libs/dfi/error_injector/dfi/CMakeLists.txt | 1 - libs/dfi/error_injector/dfi/include/dfi_ei.h | 2 - libs/dfi/error_injector/dfi/src/dfi_ei.cc | 7 - .../invalidInterfaceAnnotations.descriptor | 13 + .../invalidMethodMissingEquality.descriptor | 8 + ...nvalidMethodMissingFunctionName.descriptor | 8 + .../invalidMethodMissingId.descriptor | 8 + .../invalidTypeMissingEquality.descriptor | 10 + ...validTypeUnrecognizedSimpleType.descriptor | 10 + .../descriptors/invalids/noName.descriptor | 12 + .../descriptors/invalids/noType.descriptor | 12 + .../invalids/noTypeName.descriptor | 10 + libs/dfi/gtest/src/dyn_interface_ei_tests.cc | 17 ++ libs/dfi/gtest/src/dyn_interface_tests.cpp | 83 ++++-- libs/dfi/gtest/src/json_rpc_tests.cpp | 19 +- libs/dfi/include/dyn_function.h | 8 + libs/dfi/include/dyn_interface.h | 42 +-- libs/dfi/include/dyn_type.h | 3 + libs/dfi/src/dyn_common.c | 9 +- libs/dfi/src/dyn_function.c | 4 + libs/dfi/src/dyn_interface.c | 268 ++++++++---------- libs/error_injector/stdio/CMakeLists.txt | 1 + libs/error_injector/stdio/include/stdio_ei.h | 2 + libs/error_injector/stdio/src/stdio_ei.cc | 7 + 29 files changed, 357 insertions(+), 282 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidInterfaceAnnotations.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidMethodMissingEquality.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidMethodMissingFunctionName.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidMethodMissingId.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidTypeMissingEquality.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidTypeUnrecognizedSimpleType.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/noName.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/noType.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/noTypeName.descriptor diff --git a/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c index b93fefbee..54893b9d2 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/export_registration_dfi.c @@ -111,8 +111,7 @@ celix_status_t exportRegistration_create(celix_log_helper_t *helper, service_ref if (status == CELIX_SUCCESS) { /* Add the interface version as a property in the properties_map */ - const char* intfVersion = NULL; - dynInterface_getVersionString(reg->intf, &intfVersion); + const char* intfVersion = dynInterface_getVersionString(reg->intf); const char *serviceVersion = celix_properties_get(endpoint->properties,(char*) CELIX_FRAMEWORK_SERVICE_VERSION, NULL); if (serviceVersion != NULL) { if(strcmp(serviceVersion,intfVersion)!=0){ @@ -192,8 +191,7 @@ celix_status_t exportRegistration_call(export_registration_t *export, char *data //printf("calling for '%s'\n"); if (export->logFile != NULL) { static int callCount = 0; - const char *name = NULL; - dynInterface_getName(export->intf, &name); + const char *name = dynInterface_getName(export->intf); fprintf(export->logFile, "REMOTE CALL %i\n\tservice=%s\n\tservice_id=%s\n\trequest_payload=%s\n\trequest_response=%s\n\tstatus=%i\n", callCount, name, export->servId, data, response, status); fflush(export->logFile); callCount += 1; diff --git a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c index 8c3deb46b..bfd142a84 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c @@ -234,9 +234,8 @@ static celix_status_t importRegistration_createProxy(import_registration_t *impo } /* Check if the imported service version is compatible with the one in the consumer descriptor */ - const celix_version_t* consumerVersion = NULL; + const celix_version_t* consumerVersion = dynInterface_getVersion(intf); bool isCompatible = false; - dynInterface_getVersion(intf,&consumerVersion); isCompatible = celix_version_isCompatible(consumerVersion,import->version); if(!isCompatible){ @@ -327,7 +326,8 @@ static void importRegistration_proxyFunc(void *userData, void *args[], void *ret int rc = 0; //printf("sending request\n"); celix_properties_t *metadata = NULL; - bool cont = remoteInterceptorHandler_invokePreProxyCall(import->interceptorsHandler, import->endpoint->properties, entry->name, &metadata); + bool cont = remoteInterceptorHandler_invokePreProxyCall(import->interceptorsHandler, import->endpoint->properties, + dynFunction_getName(entry->dynFunc), &metadata); if (cont) { status = import->send(import->sendHandle, import->endpoint, invokeRequest, metadata, &reply, &rc); //printf("request sended. got reply '%s' with status %i\n", reply, rc); @@ -346,7 +346,8 @@ static void importRegistration_proxyFunc(void *userData, void *args[], void *ret *(int *) returnVal = rc; } - remoteInterceptorHandler_invokePostProxyCall(import->interceptorsHandler, import->endpoint->properties, entry->name, metadata); + remoteInterceptorHandler_invokePostProxyCall(import->interceptorsHandler, import->endpoint->properties, + dynFunction_getName(entry->dynFunc), metadata); } else { *(int *) returnVal = CELIX_INTERCEPTOR_EXCEPTION; } diff --git a/bundles/remote_services/rsa_rpc_json/gtest/src/RsaJsonRpcUnitTestSuite.cc b/bundles/remote_services/rsa_rpc_json/gtest/src/RsaJsonRpcUnitTestSuite.cc index 47f906fb6..cdd3e5278 100644 --- a/bundles/remote_services/rsa_rpc_json/gtest/src/RsaJsonRpcUnitTestSuite.cc +++ b/bundles/remote_services/rsa_rpc_json/gtest/src/RsaJsonRpcUnitTestSuite.cc @@ -790,39 +790,6 @@ TEST_F(RsaJsonRpcEndPointUnitTestSuite, FailedToFindInterfaceDescriptor) { unsetenv("CELIX_FRAMEWORK_EXTENDER_PATH"); } -TEST_F(RsaJsonRpcEndPointUnitTestSuite, FailedToGetServiceVersionFromInterfaceDescriptor) { - celix_ei_expect_dynInterface_getVersionString(CELIX_EI_UNKNOWN_CALLER, 0, 1); - - auto endpoint = CreateEndpointDescription(rpcTestSvcId); - long svcId = -1L; - auto status = rsaJsonRpc_createEndpoint(jsonRpc.get(), endpoint, &svcId); - EXPECT_EQ(CELIX_SUCCESS, status); - - celix_bundleContext_waitForEvents(ctx.get());//wait for async endpoint creation - - unsigned int serialProtoId = GenerateSerialProtoId(); - celix_properties_t *metadata = celix_properties_create(); - celix_properties_setLong(metadata, "SerialProtocolId", serialProtoId); - - auto found = celix_bundleContext_useService(ctx.get(), RSA_REQUEST_HANDLER_SERVICE_NAME, metadata, [](void *handle, void *svc) { - celix_properties_t *metadata = static_cast< celix_properties_t *>(handle);//unused - auto reqHandler = static_cast(svc); - EXPECT_NE(nullptr, reqHandler); - struct iovec request{}; - request.iov_base = (char *)"{\n \"m\": \"test\",\n \"a\": []\n}"; - request.iov_len = strlen((char*)request.iov_base); - struct iovec reply{nullptr,0}; - EXPECT_EQ(CELIX_ILLEGAL_STATE, reqHandler->handleRequest(reqHandler->handle, metadata, &request, &reply)); - free(reply.iov_base); - }); - EXPECT_TRUE(found); - - celix_properties_destroy(metadata); - - rsaJsonRpc_destroyEndpoint(jsonRpc.get(), svcId); - endpointDescription_destroy(endpoint); -} - TEST_F(RsaJsonRpcEndPointUnitTestSuite, ServiceVersionMismatched) { auto endpoint = CreateEndpointDescription(rpcTestSvcId); celix_properties_set(endpoint->properties, CELIX_FRAMEWORK_SERVICE_VERSION, "2.0.0");//Its 1.0.0 in the interface descriptor diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c index bc062d6f3..fd3e96951 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_endpoint_impl.c @@ -173,13 +173,7 @@ static void rsaJsonRpcEndpoint_addSvcWithOwner(void *handle, void *service, } // Check version - const char *intfVersion = NULL; - int ret = dynInterface_getVersionString(intfType, &intfVersion); - if (ret != 0) { - celix_logHelper_logTssErrors(endpoint->logHelper, CELIX_LOG_LEVEL_ERROR); - celix_logHelper_error(endpoint->logHelper, "Endpoint: Error getting interface version from the descriptor for %s.", serviceName); - return; - } + const char* intfVersion = dynInterface_getVersionString(intfType); const char *serviceVersion = celix_properties_get(endpoint->endpointDesc->properties,CELIX_FRAMEWORK_SERVICE_VERSION, NULL); if (serviceVersion == NULL) { celix_logHelper_error(endpoint->logHelper, "Endpoint: Error getting service version for %s.", serviceName); diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c index 77d390c1e..ed4293575 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c @@ -213,7 +213,8 @@ static void rsaJsonRpcProxy_serviceFunc(void *userData, void *args[], void *retu int rc = jsonRpc_prepareInvokeRequest(entry->dynFunc, entry->id, args, &invokeRequest); if (rc != 0) { celix_logHelper_logTssErrors(proxyFactory->logHelper, CELIX_LOG_LEVEL_ERROR); - celix_logHelper_error(proxyFactory->logHelper, "Error preparing invoke request for %s", entry->name); + celix_logHelper_error(proxyFactory->logHelper, "Error preparing invoke request for %s", + dynFunction_getName(entry->dynFunc)); *(celix_status_t *)returnVal = CELIX_SERVICE_EXCEPTION; return; } @@ -221,14 +222,15 @@ static void rsaJsonRpcProxy_serviceFunc(void *userData, void *args[], void *retu struct iovec replyIovec = {NULL,0}; celix_properties_t *metadata = celix_properties_create(); if (metadata == NULL) { - celix_logHelper_error(proxyFactory->logHelper,"Error creating metadata for %s", entry->name); + celix_logHelper_error(proxyFactory->logHelper,"Error creating metadata for %s", + dynFunction_getName(entry->dynFunc)); free(invokeRequest); *(celix_status_t *)returnVal = CELIX_ENOMEM; return; } celix_properties_setLong(metadata, "SerialProtocolId", proxyFactory->serialProtoId); bool cont = remoteInterceptorHandler_invokePreProxyCall(proxyFactory->interceptorsHandler, - proxyFactory->endpointDesc->properties, entry->name, &metadata); + proxyFactory->endpointDesc->properties, dynFunction_getName(entry->dynFunc), &metadata); if (cont) { struct iovec requestIovec = {invokeRequest,strlen(invokeRequest) + 1}; struct rsa_request_sender_callback_data data= { @@ -247,7 +249,8 @@ static void rsaJsonRpcProxy_serviceFunc(void *userData, void *args[], void *retu if(retVal != 0) { status = CELIX_SERVICE_EXCEPTION; celix_logHelper_logTssErrors(proxyFactory->logHelper, CELIX_LOG_LEVEL_ERROR); - celix_logHelper_error(proxyFactory->logHelper, "Error handling reply for %s", entry->name); + celix_logHelper_error(proxyFactory->logHelper, "Error handling reply for %s", + dynFunction_getName(entry->dynFunc)); } else if (rsErrno != CELIX_SUCCESS) { //return the invocation error of remote service function status = rsErrno; @@ -260,7 +263,7 @@ static void rsaJsonRpcProxy_serviceFunc(void *userData, void *args[], void *retu celix_logHelper_error(proxyFactory->logHelper,"Service proxy send request failed. %d", status); } remoteInterceptorHandler_invokePostProxyCall(proxyFactory->interceptorsHandler, - proxyFactory->endpointDesc->properties, entry->name, metadata); + proxyFactory->endpointDesc->properties, dynFunction_getName(entry->dynFunc), metadata); } else { celix_logHelper_error(proxyFactory->logHelper, "%s has been intercepted.", proxyFactory->endpointDesc->serviceName); status = CELIX_INTERCEPTOR_EXCEPTION; @@ -318,9 +321,8 @@ static celix_status_t rsaJsonRpcProxy_create(rsa_json_rpc_proxy_factory_t *proxy celix_logHelper_error(proxyFactory->logHelper, "Proxy: Error converting service version type. %d.", status); return status; } - const celix_version_t *consumerVersion = NULL; + const celix_version_t *consumerVersion = dynInterface_getVersion(intfType); bool isCompatible = false; - dynInterface_getVersion(intfType,&consumerVersion); isCompatible = celix_version_isCompatible(consumerVersion, providerVersion); if(!isCompatible){ celix_logHelper_error(proxyFactory->logHelper, "Proxy: Service version mismatch, consumer has %d.%d.%d, provider has %s.", @@ -345,7 +347,8 @@ static celix_status_t rsaJsonRpcProxy_create(rsa_json_rpc_proxy_factory_t *proxy TAILQ_FOREACH(entry, list, entries) { int rc = dynFunction_createClosure(entry->dynFunc, rsaJsonRpcProxy_serviceFunc, entry, &fn); if (rc != 0) { - celix_logHelper_error(proxyFactory->logHelper, "Proxy: Failed to create closure for service function %s.", entry->name); + celix_logHelper_error(proxyFactory->logHelper, "Proxy: Failed to create closure for service function %s.", + dynFunction_getName(entry->dynFunc)); return CELIX_SERVICE_EXCEPTION; } service[++index] = fn; diff --git a/libs/dfi/error_injector/dfi/CMakeLists.txt b/libs/dfi/error_injector/dfi/CMakeLists.txt index 23608c884..34fd1713a 100644 --- a/libs/dfi/error_injector/dfi/CMakeLists.txt +++ b/libs/dfi/error_injector/dfi/CMakeLists.txt @@ -23,6 +23,5 @@ target_link_libraries(dfi_ei PUBLIC Celix::error_injector Celix::dfi) target_link_options(dfi_ei INTERFACE LINKER:--wrap,dynFunction_createClosure LINKER:--wrap,jsonRpc_prepareInvokeRequest - LINKER:--wrap,dynInterface_getVersionString ) add_library(Celix::dfi_ei ALIAS dfi_ei) diff --git a/libs/dfi/error_injector/dfi/include/dfi_ei.h b/libs/dfi/error_injector/dfi/include/dfi_ei.h index 140d922d1..cdab04f90 100644 --- a/libs/dfi/error_injector/dfi/include/dfi_ei.h +++ b/libs/dfi/error_injector/dfi/include/dfi_ei.h @@ -28,8 +28,6 @@ CELIX_EI_DECLARE(dynFunction_createClosure, int); CELIX_EI_DECLARE(jsonRpc_prepareInvokeRequest, int); -CELIX_EI_DECLARE(dynInterface_getVersionString, int); - #ifdef __cplusplus } #endif diff --git a/libs/dfi/error_injector/dfi/src/dfi_ei.cc b/libs/dfi/error_injector/dfi/src/dfi_ei.cc index b37f6060b..4950d7f60 100644 --- a/libs/dfi/error_injector/dfi/src/dfi_ei.cc +++ b/libs/dfi/error_injector/dfi/src/dfi_ei.cc @@ -35,11 +35,4 @@ int __wrap_jsonRpc_prepareInvokeRequest(dyn_function_type *func, const char *id, return __real_jsonRpc_prepareInvokeRequest(func, id, args, out); } -int __real_dynInterface_getVersionString(dyn_interface_type *dynInterface, char **out); -CELIX_EI_DEFINE(dynInterface_getVersionString, int) -int __wrap_dynInterface_getVersionString(dyn_interface_type *dynInterface, char **out) { - CELIX_EI_IMPL(dynInterface_getVersionString); - return __real_dynInterface_getVersionString(dynInterface, out); -} - } \ No newline at end of file diff --git a/libs/dfi/gtest/descriptors/invalids/invalidInterfaceAnnotations.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidInterfaceAnnotations.descriptor new file mode 100644 index 000000000..a5ecd55e4 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidInterfaceAnnotations.descriptor @@ -0,0 +1,13 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(#am=handle;PDD#am=pre;*D)N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingEquality.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingEquality.descriptor new file mode 100644 index 000000000..c7b369e2f --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingEquality.descriptor @@ -0,0 +1,8 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:methods +add(DD)D add(#am=handle;PDD#am=pre;*D)N diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingFunctionName.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingFunctionName.descriptor new file mode 100644 index 000000000..092e5781f --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingFunctionName.descriptor @@ -0,0 +1,8 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:methods +add(DD)D=(#am=handle;PDD#am=pre;*D)N diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingId.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingId.descriptor new file mode 100644 index 000000000..62bb20b26 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidMethodMissingId.descriptor @@ -0,0 +1,8 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:methods +=add(#am=handle;PDD#am=pre;*D)N diff --git a/libs/dfi/gtest/descriptors/invalids/invalidTypeMissingEquality.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidTypeMissingEquality.descriptor new file mode 100644 index 000000000..ac567a8d9 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidTypeMissingEquality.descriptor @@ -0,0 +1,10 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult {DDD[D average min max input} +:methods +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/invalidTypeUnrecognizedSimpleType.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidTypeUnrecognizedSimpleType.descriptor new file mode 100644 index 000000000..4bdce8011 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidTypeUnrecognizedSimpleType.descriptor @@ -0,0 +1,10 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={X x} +:methods +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/noName.descriptor b/libs/dfi/gtest/descriptors/invalids/noName.descriptor new file mode 100644 index 000000000..819e0ede3 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/noName.descriptor @@ -0,0 +1,12 @@ +:header +type=interface +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(#am=handle;PDD#am=pre;*D)N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/noType.descriptor b/libs/dfi/gtest/descriptors/invalids/noType.descriptor new file mode 100644 index 000000000..60933df40 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/noType.descriptor @@ -0,0 +1,12 @@ +:header +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(#am=handle;PDD#am=pre;*D)N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/noTypeName.descriptor b/libs/dfi/gtest/descriptors/invalids/noTypeName.descriptor new file mode 100644 index 000000000..9d6825d59 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/noTypeName.descriptor @@ -0,0 +1,10 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +={DDD[D average min max input} +:methods +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc index 76426ecdd..4145d9e20 100644 --- a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc @@ -34,6 +34,7 @@ class DynInterfaceErrorInjectionTestSuite : public ::testing::Test { DynInterfaceErrorInjectionTestSuite() = default; ~DynInterfaceErrorInjectionTestSuite() override { + celix_ei_expect_fgetc(nullptr, 0, EOF); celix_ei_expect_calloc(nullptr, 0, nullptr); celix_ei_expect_open_memstream(nullptr, 0, nullptr); } @@ -80,4 +81,20 @@ TEST_F(DynInterfaceErrorInjectionTestSuite, ParseError) { std::string msg = "Error creating mem stream for name. "; msg += strerror(ENOMEM); ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); + + rewind(desc); + // encounter EOF when parsing header section + celix_ei_expect_fgetc((void*) dynCommon_eatChar, 0, EOF); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + msg = "Error parsing, expected token"; + ASSERT_TRUE(msg.compare(0, msg.length(), celix_err_popLastError())); + + rewind(desc); + // encounter EOF when expecting newline ending a header name + celix_ei_expect_fgetc((void*) dynCommon_eatChar, 0, EOF, 2); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + msg = "Error parsing, expected token"; + ASSERT_TRUE(msg.compare(0, msg.length(), celix_err_popLastError())); } \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index b51eedf72..29a286bed 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -34,18 +34,12 @@ extern "C" { #include "celix_version.h" static void checkInterfaceVersion(dyn_interface_type* dynIntf, const char* v) { - int status; - - const char *version = NULL; - status = dynInterface_getVersionString(dynIntf, &version); - ASSERT_EQ(0, status); + const char* version = dynInterface_getVersionString(dynIntf); ASSERT_STREQ(v, version); - const celix_version_t* msgVersion = NULL; + const celix_version_t* msgVersion = dynInterface_getVersion(dynIntf); celix_version_t* localMsgVersion = NULL; int cmpVersion = -1; localMsgVersion = celix_version_createVersionFromString(version); - status = dynInterface_getVersion(dynIntf, &msgVersion); - ASSERT_EQ(0, status); cmpVersion = celix_version_compareTo(msgVersion, localMsgVersion); ASSERT_EQ(cmpVersion, 0); celix_version_destroy(localMsgVersion); @@ -60,9 +54,7 @@ extern "C" { ASSERT_EQ(0, status); fclose(desc); - const char *name = NULL; - status = dynInterface_getName(dynIntf, &name); - ASSERT_EQ(0, status); + const char *name = dynInterface_getName(dynIntf); ASSERT_STREQ("calculator", name); checkInterfaceVersion(dynIntf,"1.0.0"); @@ -108,7 +100,6 @@ extern "C" { FILE *desc = fopen("descriptors/invalids/invalid.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); ASSERT_EQ(1, status); //Test fails because of a space at the end of the name fclose(desc); desc=NULL; @@ -123,15 +114,34 @@ extern "C" { desc = fopen("descriptors/invalids/noVersion.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); ASSERT_EQ(1, status); //Test fails because of missing version field in header section fclose(desc); desc=NULL; + /* Header without Type */ + desc = fopen("descriptors/invalids/noType.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); //Test fails because of missing type field in header section + fclose(desc); desc=NULL; + + /* Header without Name */ + desc = fopen("descriptors/invalids/noName.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); //Test fails because of missing name field in header section + fclose(desc); desc=NULL; + + /* Invalid Annotations Section */ + desc = fopen("descriptors/invalids/invalidInterfaceAnnotations.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); //Test fails because of missing name field in annotations section + fclose(desc); desc=NULL; + /* Invalid section */ desc = fopen("descriptors/invalids/invalidSection.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); ASSERT_EQ(1, status); //Test fails because of unknown section type fclose(desc); desc=NULL; @@ -139,7 +149,6 @@ extern "C" { desc = fopen("descriptors/invalids/invalidMethodReturnType.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); ASSERT_EQ(1, status); //Test fails because of invalid return type (D instead of N) fclose(desc); desc=NULL; @@ -147,18 +156,58 @@ extern "C" { desc = fopen("descriptors/invalids/invalidMethod.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); ASSERT_EQ(1, status); //Test fails because of space at the end of the method fclose(desc); desc=NULL; + /* Invalid method section missing method id */ + desc = fopen("descriptors/invalids/invalidMethodMissingId.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); + fclose(desc); desc=NULL; + + /* Invalid method section missing equality */ + desc = fopen("descriptors/invalids/invalidMethodMissingEquality.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); + fclose(desc); desc=NULL; + + /* Invalid method section missing function name */ + desc = fopen("descriptors/invalids/invalidMethodMissingFunctionName.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); + fclose(desc); desc=NULL; + /* Invalid type */ desc = fopen("descriptors/invalids/invalidType.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); - //dynInterface_destroy(dynIntf); ASSERT_EQ(1, status); //Test fails because of space at the end of the type fclose(desc); desc=NULL; + /* Invalid types section missing type name */ + desc = fopen("descriptors/invalids/noTypeName.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); //Test fails because of missing type name + fclose(desc); desc=NULL; + + /* Invalid types section missing equality */ + desc = fopen("descriptors/invalids/invalidTypeMissingEquality.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); //Test fails because of missing equality + fclose(desc); desc=NULL; + + /* Invalid types section with unrecognized simple type */ + desc = fopen("descriptors/invalids/invalidTypeUnrecognizedSimpleType.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + fclose(desc); desc=NULL; + /* Invalid metatype in method description */ desc = fopen("descriptors/invalids/invalidMetaType.descriptor", "r"); assert(desc != NULL); diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 4af3e29c1..680e0b600 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -34,6 +34,7 @@ extern "C" { #include "dyn_common.h" #include "dyn_type.h" +#include "dyn_function.h" #include "json_serializer.h" #include "json_rpc.h" #include "celix_errno.h" @@ -360,7 +361,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "stats") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) { func = entry->dynFunc; break; } @@ -402,7 +403,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "stats") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) { func = entry->dynFunc; break; } @@ -441,7 +442,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "add") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "add") == 0) { func = entry->dynFunc; break; } @@ -499,7 +500,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "example1") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "items") == 0) { func = entry->dynFunc; break; } @@ -552,7 +553,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "action") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "action") == 0) { func = entry->dynFunc; break; } @@ -585,7 +586,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "multiPreOut") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "multiPreOut") == 0) { func = entry->dynFunc; break; } @@ -628,7 +629,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "multiOut") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "multiOut") == 0) { func = entry->dynFunc; break; } @@ -694,7 +695,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "getName") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "getName") == 0) { func = entry->dynFunc; break; } @@ -902,7 +903,7 @@ extern "C" { dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { - if (strcmp(entry->name, "compatibility") == 0) { + if (strcmp(dynFunction_getName(entry->dynFunc), "cpt") == 0) { func = entry->dynFunc; break; } diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index c93f3ab6f..7d506945c 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -21,6 +21,7 @@ #define __DYN_FUNCTION_H_ #include "dyn_type.h" +#include "celix_cleanup.h" #include "celix_dfi_export.h" #ifdef __cplusplus @@ -119,6 +120,8 @@ CELIX_DFI_EXPORT dyn_type * dynFunction_returnType(dyn_function_type *dynFunctio */ CELIX_DFI_EXPORT void dynFunction_destroy(dyn_function_type *dynFunc); +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_function_type, dynFunction_destroy); + /** * @brief Calls the given dynamic type function. * @param[in] dynFunc The dynamic type instance for function. @@ -155,6 +158,11 @@ CELIX_DFI_EXPORT int dynFunction_getFnPointer(dyn_function_type *func, void (**f */ CELIX_DFI_EXPORT bool dynFunction_hasReturn(dyn_function_type *dynFunction); +/** + * @brief Returns the name of the given dynamic function type instance. + */ +CELIX_DFI_EXPORT const char* dynFunction_getName(const dyn_function_type *func); + #ifdef __cplusplus } #endif diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index 49eb517dd..fcbc936c1 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -46,9 +46,8 @@ typedef struct _dyn_interface_type dyn_interface_type; TAILQ_HEAD(methods_head, method_entry); struct method_entry { int index; - char *id; - char *name; - dyn_function_type *dynFunc; + char* id; + dyn_function_type* dynFunc; TAILQ_ENTRY(method_entry) entries; }; @@ -64,52 +63,33 @@ struct method_entry { * @param[out] out The created dynamic interface type instance. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_parse(FILE *descriptor, dyn_interface_type **out); +CELIX_DFI_EXPORT int dynInterface_parse(FILE* descriptor, dyn_interface_type** out); /** * @brief Destroys the given dynamic interface type instance. * @param[in] intf The dynamic interface type instance to destroy. */ -CELIX_DFI_EXPORT void dynInterface_destroy(dyn_interface_type *intf); +CELIX_DFI_EXPORT void dynInterface_destroy(dyn_interface_type* intf); CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_interface_type, dynInterface_destroy); /** * @brief Gets the name of the given dynamic interface type instance. - * * The dynamic interface type instance is the owner of the returned string and the string should not be freed. - * - * In case of an error, an error message is added to celix_err. - * - * @param[in] intf The dynamic interface type instance. - * @param[out] name The name of the dynamic interface type instance. - * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getName(dyn_interface_type *intf, const char **name); +CELIX_DFI_EXPORT const char* dynInterface_getName(const dyn_interface_type* intf); /** * @brief Gets the version of the given dynamic interface type instance. - * * The dynamic interface type instance is the owner of the version and the version should not be freed. - * - * @param[in] intf The dynamic interface type instance. - * @param[out] version The version of the dynamic interface type instance. - * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getVersion(dyn_interface_type *intf, const celix_version_t** version); +CELIX_DFI_EXPORT const celix_version_t* dynInterface_getVersion(const dyn_interface_type* intf); /** * @brief Gets the version string of the given dynamic interface type instance. - * * The dynamic interface type instance is the owner of the version string and the version string should not be freed. - * - * In case of an error, an error message is added to celix_err. - * - * @param[in] intf The dynamic interface type instance. - * @param[out] version The version string of the dynamic interface type instance. - * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getVersionString(dyn_interface_type *intf, const char **version); +CELIX_DFI_EXPORT const char* dynInterface_getVersionString(const dyn_interface_type* intf); /** * @brief Gets the value corresponding to the specified name, which comes from the header section of the given dynamic interface type instance. @@ -123,7 +103,7 @@ CELIX_DFI_EXPORT int dynInterface_getVersionString(dyn_interface_type *intf, con * @param[out] value The value corresponding to the specified name. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getHeaderEntry(dyn_interface_type *intf, const char *name, const char **value); +CELIX_DFI_EXPORT int dynInterface_getHeaderEntry(const dyn_interface_type* intf, const char* name, const char** value); /** * @brief Gets the value corresponding to the specified name, which comes from the annotation section of the given dynamic interface type instance. @@ -137,7 +117,7 @@ CELIX_DFI_EXPORT int dynInterface_getHeaderEntry(dyn_interface_type *intf, const * @param[out] value The value corresponding to the specified name. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(dyn_interface_type *intf, const char *name, const char **value); +CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(const dyn_interface_type* intf, const char* name, const char** value); /** * @brief Gets the methods of the given dynamic interface type instance. @@ -148,14 +128,14 @@ CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(dyn_interface_type *intf, c * @param[out] list The method list of the dynamic interface type instance. * @return 0. */ -CELIX_DFI_EXPORT int dynInterface_methods(dyn_interface_type *intf, const struct methods_head **list); +CELIX_DFI_EXPORT int dynInterface_methods(const dyn_interface_type* intf, const struct methods_head** list); /** * @brief Returns the number of methods for the given dynamic interface type instance. * @param[in] intf The dynamic interface type instance. * @return The number of methods for the given dynamic interface type instance. */ -CELIX_DFI_EXPORT int dynInterface_nrOfMethods(dyn_interface_type *intf); +CELIX_DFI_EXPORT int dynInterface_nrOfMethods(const dyn_interface_type* intf); #ifdef __cplusplus diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 4276eaf92..f56b08e9a 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -26,6 +26,7 @@ #include #include "celix_dfi_export.h" +#include "celix_cleanup.h" #ifdef __cplusplus extern "C" { @@ -165,6 +166,8 @@ CELIX_DFI_EXPORT int dynType_parseWithStr(const char *descriptor, const char *na */ CELIX_DFI_EXPORT void dynType_destroy(dyn_type *type); +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_type, dynType_destroy); + /** * Allocates memory for a type instance described by a dyn type. The memory will be 0 allocated (calloc). * diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 61029a5ce..17783fd7b 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -128,19 +128,18 @@ int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { celix_autofree char *name = NULL; celix_autofree char *value = NULL; if ((status = dynCommon_parseNameValue(stream, &name, &value)) != OK) { - break; + return status; } if ((status = dynCommon_eatChar(stream, '\n')) != OK) { - break; + return status; } struct namval_entry *entry = NULL; entry = calloc(1, sizeof(*entry)); if (entry == NULL) { - status = ERROR; celix_err_pushf("Error allocating memory for namval entry"); - break; + return ERROR; } entry->name = celix_steal_ptr(name); @@ -153,5 +152,5 @@ int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { ungetc(peek, stream); } - return status; + return OK; } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index eb9903b13..65739ae37 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -307,4 +307,8 @@ dyn_type * dynFunction_returnType(dyn_function_type *dynFunction) { bool dynFunction_hasReturn(dyn_function_type *dynFunction) { dyn_type *t = dynFunction_returnType(dynFunction); return t->descriptor != 'V'; +} + +const char* dynFunction_getName(const dyn_function_type* func) { + return func->name; } \ No newline at end of file diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index bbedfae93..a9379e817 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -18,28 +18,28 @@ */ #include "dyn_interface.h" +#include "dyn_common.h" +#include "dyn_type.h" +#include "dyn_interface_common.h" #include "celix_err.h" +#include "celix_stdlib_cleanup.h" #include #include #include -#include "dyn_common.h" -#include "dyn_type.h" -#include "dyn_interface_common.h" static const int OK = 0; static const int ERROR = 1; -int dynInterface_checkInterface(dyn_interface_type *intf); - +static int dynInterface_checkInterface(dyn_interface_type *intf); static int dynInterface_parseSection(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream); static int dynInterface_parseHeader(dyn_interface_type *intf, FILE *stream); -static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, const char **value); +static int dynInterface_getEntryForHead(const struct namvals_head *head, const char *name, const char **value); int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { int status = OK; @@ -74,9 +74,7 @@ int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { } // We are sure that version field is present in the header - const char* version = NULL; - status = dynInterface_getVersionString(intf,&version); - assert(status == OK); + const char* version = dynInterface_getVersionString(intf); assert(version != NULL); intf->version = celix_version_createVersionFromString(version); if (intf->version == NULL) { @@ -87,76 +85,67 @@ int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { return OK; } -int dynInterface_checkInterface(dyn_interface_type *intf) { - int status = OK; - +static int dynInterface_checkInterface(dyn_interface_type *intf) { //check header section - if (status == OK) { - bool foundType = false; - bool foundVersion = false; - bool foundName = false; - struct namval_entry *entry = NULL; - TAILQ_FOREACH(entry, &intf->header, entries) { - if (strcmp(entry->name, "type") == 0) { - foundType = true; - } else if (strcmp(entry->name, "version") == 0) { - foundVersion = true; - } else if (strcmp(entry->name, "name") == 0) { - foundName = true; - } + bool foundType = false; + bool foundVersion = false; + bool foundName = false; + struct namval_entry *entry = NULL; + TAILQ_FOREACH(entry, &intf->header, entries) { + if (strcmp(entry->name, "type") == 0) { + foundType = true; + } else if (strcmp(entry->name, "version") == 0) { + foundVersion = true; + } else if (strcmp(entry->name, "name") == 0) { + foundName = true; } + } - if (!foundType || !foundVersion || !foundName) { - status = ERROR; - celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); - } + if (!foundType || !foundVersion || !foundName) { + celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); + return ERROR; + } - struct method_entry *mEntry = NULL; - TAILQ_FOREACH(mEntry, &intf->methods, entries) { - dyn_type *type = dynFunction_returnType(mEntry->dynFunc); - int descriptor = dynType_descriptorType(type); - if (descriptor != 'N') { - status = ERROR; - celix_err_pushf("Parse Error. Only method with a return type 'N' (native int) are supported. Got return type '%c'\n", descriptor); - break; - } + struct method_entry *mEntry = NULL; + TAILQ_FOREACH(mEntry, &intf->methods, entries) { + dyn_type *type = dynFunction_returnType(mEntry->dynFunc); + int descriptor = dynType_descriptorType(type); + if (descriptor != 'N') { + celix_err_pushf("Parse Error. Only method with a return type 'N' (native int) are supported. Got return type '%c'\n", descriptor); + return ERROR; } } - return status; + return OK; } static int dynInterface_parseSection(dyn_interface_type *intf, FILE *stream) { int status = OK; - char *sectionName = NULL; + celix_autofree char *sectionName = NULL; - status = dynCommon_eatChar(stream, ':'); - - if (status == OK) { - status = dynCommon_parseName(stream, §ionName); + if ((status = dynCommon_eatChar(stream, ':')) != OK) { + return status; } - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); + if ((status = dynCommon_parseName(stream, §ionName)) != OK) { + return status; } - if (status == OK) { - if (strcmp("header", sectionName) == 0) { - status = dynInterface_parseHeader(intf, stream); - } else if (strcmp("annotations", sectionName) == 0) { - status = dynInterface_parseAnnotations(intf, stream); - } else if (strcmp("types", sectionName) == 0) { - status = dynInterface_parseTypes(intf, stream); - } else if (strcmp("methods", sectionName) == 0) { - status = dynInterface_parseMethods(intf, stream); - } else { - status = ERROR; - celix_err_pushf("unsupported section '%s'", sectionName); - } + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + return status; } - if (sectionName != NULL) { - free(sectionName); + if (strcmp("header", sectionName) == 0) { + status = dynInterface_parseHeader(intf, stream); + } else if (strcmp("annotations", sectionName) == 0) { + status = dynInterface_parseAnnotations(intf, stream); + } else if (strcmp("types", sectionName) == 0) { + status = dynInterface_parseTypes(intf, stream); + } else if (strcmp("methods", sectionName) == 0) { + status = dynInterface_parseMethods(intf, stream); + } else { + status = ERROR; + celix_err_pushf("unsupported section '%s'", sectionName); } return status; @@ -171,55 +160,46 @@ static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream) } static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream) { - int status = OK; + int status; //expected input (Name)=\n int peek = fgetc(stream); while (peek != ':' && peek != EOF) { ungetc(peek, stream); - char *name = NULL; - status = dynCommon_parseName(stream, &name); - - if (status == OK) { - status = dynCommon_eatChar(stream, '='); + celix_autofree char* name = NULL; + if ((status = dynCommon_parseName(stream, &name)) != OK) { + return status; } - dyn_type *type = NULL; - if (status == OK) { - dynType_parse(stream, name, &intf->types, &type); - } - if (name != NULL) { - free(name); + if ((status = dynCommon_eatChar(stream, '=')) != OK) { + return status; } - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); + celix_autoptr(dyn_type) type = NULL; + if ((status = dynType_parse(stream, name, &intf->types, &type)) != OK) { + return status; + } + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + return status; } struct type_entry *entry = NULL; - if (status == OK) { - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->type = type; - TAILQ_INSERT_TAIL(&intf->types, entry, entries); - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for type entry"); - } + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + celix_err_pushf("Error allocating memory for type entry"); + return ERROR; } + entry->type = celix_steal_ptr(type); + TAILQ_INSERT_TAIL(&intf->types, entry, entries); - if (status != OK) { - if (type != NULL) { - dynType_destroy(type); - } - break; - } peek = fgetc(stream); } - ungetc(peek, stream); + if (peek != EOF) { + ungetc(peek, stream); + } - return status; + return OK; } static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream) { @@ -231,67 +211,45 @@ static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream) { while (peek != ':' && peek != EOF) { ungetc(peek, stream); - char *id = NULL; - status = dynCommon_parseNameAlsoAccept(stream, ".();[{}/", &id); - - if (status == OK) { - status = dynCommon_eatChar(stream, '='); + celix_autofree char* id = NULL; + if ((status = dynCommon_parseNameAlsoAccept(stream, ".();[{}/", &id)) != OK) { + return status; } + if ((status = dynCommon_eatChar(stream, '=')) != OK) { + return status; + } - dyn_function_type *func = NULL; - if (status == OK) { - status = dynFunction_parse(stream, &intf->types, &func); + celix_autoptr(dyn_function_type) func = NULL; + if ((status = dynFunction_parse(stream, &intf->types, &func)) != OK) { + return status; } - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + return status; } struct method_entry *entry = NULL; - if (status == OK) { - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->index = index++; - entry->id = id; - entry->dynFunc = func; - entry->name = strndup(id, 1024); - if (entry->name != NULL) { - int i; - for (i = 0; i < 1024; i += 1) { - if (entry->name[i] == '\0') { - break; - } else if (entry->name[i] == '(') { - entry->name[i] = '\0'; - break; - } - } - } - TAILQ_INSERT_TAIL(&intf->methods, entry, entries); - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for method entry"); - } + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + celix_err_pushf("Error allocating memory for method entry"); + return ERROR; } + entry->index = index++; + entry->id = celix_steal_ptr(id); + entry->dynFunc = celix_steal_ptr(func); + TAILQ_INSERT_TAIL(&intf->methods, entry, entries); - if (status != OK) { - if (id != NULL) { - free(id); - } - if (func != NULL) { - dynFunction_destroy(func); - //TODO free strIdentifier, name - } - break; - } peek = fgetc(stream); } - ungetc(peek, stream); + if (peek != EOF) { + ungetc(peek, stream); + } return status; } -void dynInterface_destroy(dyn_interface_type *intf) { +void dynInterface_destroy(dyn_interface_type* intf) { if (intf != NULL) { dynCommon_clearNamValHead(&intf->header); dynCommon_clearNamValHead(&intf->annotations); @@ -304,9 +262,6 @@ void dynInterface_destroy(dyn_interface_type *intf) { if (mTmp->id != NULL) { free(mTmp->id); } - if (mTmp->name != NULL) { - free(mTmp->name); - } if (mTmp->dynFunc != NULL) { dynFunction_destroy(mTmp->dynFunc); } @@ -322,36 +277,41 @@ void dynInterface_destroy(dyn_interface_type *intf) { } if(intf->version!=NULL){ - celix_version_destroy(intf->version); + celix_version_destroy(intf->version); } free(intf); } } -int dynInterface_getName(dyn_interface_type *intf, const char **out) { - return dynInterface_getEntryForHead(&intf->header, "name", out); +const char* dynInterface_getName(const dyn_interface_type* intf) { + const char* name = NULL; + // dynInterface_checkInterface ensures that the name is present + (void)dynInterface_getEntryForHead(&intf->header, "name", &name); + return name; } -int dynInterface_getVersion(dyn_interface_type* intf , const celix_version_t** version){ - // aseert(intf->version != NULL); - *version = intf->version; - return OK; +const celix_version_t* dynInterface_getVersion(const dyn_interface_type* intf){ + // dynInterface_checkInterface ensures that version is present + return intf->version; } -int dynInterface_getVersionString(dyn_interface_type *intf, const char **version) { - return dynInterface_getEntryForHead(&intf->header, "version", version); +const char* dynInterface_getVersionString(const dyn_interface_type* intf) { + const char* version = NULL; + // dynInterface_checkInterface ensures that the version is present + (void)dynInterface_getEntryForHead(&intf->header, "version", &version); + return version; } -int dynInterface_getHeaderEntry(dyn_interface_type *intf, const char *name, const char **value) { +int dynInterface_getHeaderEntry(const dyn_interface_type* intf, const char* name, const char** value) { return dynInterface_getEntryForHead(&intf->header, name, value); } -int dynInterface_getAnnotationEntry(dyn_interface_type *intf, const char *name, const char **value) { +int dynInterface_getAnnotationEntry(const dyn_interface_type *intf, const char *name, const char **value) { return dynInterface_getEntryForHead(&intf->annotations, name, value); } -static int dynInterface_getEntryForHead(struct namvals_head *head, const char *name, const char **out) { +static int dynInterface_getEntryForHead(const struct namvals_head *head, const char *name, const char **out) { int status = OK; char *value = NULL; struct namval_entry *entry = NULL; @@ -370,13 +330,13 @@ static int dynInterface_getEntryForHead(struct namvals_head *head, const char *n return status; } -int dynInterface_methods(dyn_interface_type *intf, const struct methods_head **list) { +int dynInterface_methods(const dyn_interface_type* intf, const struct methods_head** list) { int status = OK; *list = &intf->methods; return status; } -int dynInterface_nrOfMethods(dyn_interface_type *intf) { +int dynInterface_nrOfMethods(const dyn_interface_type* intf) { int count = 0; struct method_entry *entry = NULL; TAILQ_FOREACH(entry, &intf->methods, entries) { diff --git a/libs/error_injector/stdio/CMakeLists.txt b/libs/error_injector/stdio/CMakeLists.txt index f4824197e..667969093 100644 --- a/libs/error_injector/stdio/CMakeLists.txt +++ b/libs/error_injector/stdio/CMakeLists.txt @@ -31,5 +31,6 @@ target_link_options(stdio_ei INTERFACE LINKER:--wrap,fputc LINKER:--wrap,fputs LINKER:--wrap,fclose + LINKER:--wrap,fgetc ) add_library(Celix::stdio_ei ALIAS stdio_ei) diff --git a/libs/error_injector/stdio/include/stdio_ei.h b/libs/error_injector/stdio/include/stdio_ei.h index 321a39d9b..4ea80e0cc 100644 --- a/libs/error_injector/stdio/include/stdio_ei.h +++ b/libs/error_injector/stdio/include/stdio_ei.h @@ -46,6 +46,8 @@ CELIX_EI_DECLARE(fputs, int); CELIX_EI_DECLARE(fclose, int); +CELIX_EI_DECLARE(fgetc, int); + #ifdef __cplusplus } #endif diff --git a/libs/error_injector/stdio/src/stdio_ei.cc b/libs/error_injector/stdio/src/stdio_ei.cc index d78f7bbc2..4a0c5d936 100644 --- a/libs/error_injector/stdio/src/stdio_ei.cc +++ b/libs/error_injector/stdio/src/stdio_ei.cc @@ -108,4 +108,11 @@ int __wrap_fclose(FILE* __stream) { return rc; } +int __real_fgetc(FILE* __stream); +CELIX_EI_DEFINE(fgetc, int) +int __wrap_fgetc(FILE* __stream) { + CELIX_EI_IMPL(fgetc); + return __real_fgetc(__stream); +} + } From 9e9b14f50e19a69756059136e71377d18e561af8 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 20 Dec 2023 22:33:36 +0800 Subject: [PATCH 10/62] Improve ergonomics of dynInterface_methods. --- .../src/import_registration_dfi.c | 3 +-- .../src/rsa_json_rpc_proxy_impl.c | 3 +-- libs/dfi/gtest/src/dyn_interface_tests.cpp | 4 +-- libs/dfi/gtest/src/json_rpc_tests.cpp | 27 +++++++------------ libs/dfi/include/dyn_interface.h | 11 ++++---- libs/dfi/src/dyn_interface.c | 6 ++--- libs/dfi/src/json_rpc.c | 3 +-- 7 files changed, 20 insertions(+), 37 deletions(-) diff --git a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c index bfd142a84..a0c55724a 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c @@ -270,8 +270,7 @@ static celix_status_t importRegistration_createProxy(import_registration_t *impo void **serv = proxy->service; serv[0] = import; - const struct methods_head *list = NULL; - dynInterface_methods(proxy->intf, &list); + const struct methods_head* list = dynInterface_methods(proxy->intf); struct method_entry *entry = NULL; void (*fn)(void) = NULL; int index = 0; diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c index ed4293575..55914fc87 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c @@ -339,8 +339,7 @@ static celix_status_t rsaJsonRpcProxy_create(rsa_json_rpc_proxy_factory_t *proxy } celix_autofree void **service = (void **)proxy->service; service[0] = proxy; - const struct methods_head *list = NULL; - dynInterface_methods(intfType, &list); + const struct methods_head* list = dynInterface_methods(intfType); struct method_entry *entry = NULL; void (*fn)(void) = NULL; int index = 0; diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index 29a286bed..820ad83f9 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -69,9 +69,7 @@ extern "C" { ASSERT_TRUE(status != 0); ASSERT_TRUE(nonExist == NULL); - const struct methods_head *list = NULL; - status = dynInterface_methods(dynIntf, &list); - ASSERT_TRUE(status == 0); + const struct methods_head* list = dynInterface_methods(dynIntf); ASSERT_TRUE(list != NULL); int count = dynInterface_nrOfMethods(dynIntf); diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 680e0b600..35db8c53b 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -356,8 +356,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -398,8 +397,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -437,8 +435,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -495,8 +492,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -548,8 +544,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -581,8 +576,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -624,8 +618,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -690,8 +683,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { @@ -898,8 +890,7 @@ extern "C" { ASSERT_EQ(0, rc); fclose(desc); - const struct methods_head *head; - dynInterface_methods(intf, &head); + const struct methods_head* head = dynInterface_methods(intf); dyn_function_type *func = nullptr; struct method_entry *entry = nullptr; TAILQ_FOREACH(entry, head, entries) { diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index fcbc936c1..fe0adf8bd 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -76,18 +76,21 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_interface_type, dynInterface_destroy); /** * @brief Gets the name of the given dynamic interface type instance. * The dynamic interface type instance is the owner of the returned string and the string should not be freed. + * @note It always returns valid string. */ CELIX_DFI_EXPORT const char* dynInterface_getName(const dyn_interface_type* intf); /** * @brief Gets the version of the given dynamic interface type instance. * The dynamic interface type instance is the owner of the version and the version should not be freed. + * @note It always returns valid version. */ CELIX_DFI_EXPORT const celix_version_t* dynInterface_getVersion(const dyn_interface_type* intf); /** * @brief Gets the version string of the given dynamic interface type instance. * The dynamic interface type instance is the owner of the version string and the version string should not be freed. + * @note It always returns valid string. */ CELIX_DFI_EXPORT const char* dynInterface_getVersionString(const dyn_interface_type* intf); @@ -121,14 +124,10 @@ CELIX_DFI_EXPORT int dynInterface_getAnnotationEntry(const dyn_interface_type* i /** * @brief Gets the methods of the given dynamic interface type instance. - * * The dynamic interface type instance is the owner of the list and the list should not be freed. - * - * @param[in] intf The dynamic interface type instance. - * @param[out] list The method list of the dynamic interface type instance. - * @return 0. + * @note It always returns valid methods. */ -CELIX_DFI_EXPORT int dynInterface_methods(const dyn_interface_type* intf, const struct methods_head** list); +CELIX_DFI_EXPORT const struct methods_head* dynInterface_methods(const dyn_interface_type* intf); /** * @brief Returns the number of methods for the given dynamic interface type instance. diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index a9379e817..8d5d5b4f5 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -330,10 +330,8 @@ static int dynInterface_getEntryForHead(const struct namvals_head *head, const c return status; } -int dynInterface_methods(const dyn_interface_type* intf, const struct methods_head** list) { - int status = OK; - *list = &intf->methods; - return status; +const struct methods_head* dynInterface_methods(const dyn_interface_type* intf) { + return &intf->methods; } int dynInterface_nrOfMethods(const dyn_interface_type* intf) { diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 0d3525af9..ef90f5275 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -63,8 +63,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c return ERROR; } - const struct methods_head *methods = NULL; - dynInterface_methods(intf, &methods); + const struct methods_head* methods = dynInterface_methods(intf); struct method_entry *entry = NULL; struct method_entry *method = NULL; TAILQ_FOREACH(entry, methods, entries) { From b6d5e395030ec05476ce4a03004d416d489a0e4f Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 21 Dec 2023 21:38:22 +0800 Subject: [PATCH 11/62] Apply const qualifier to dyn_type and improve ergonomics for functions that cannot fail. --- libs/dfi/gtest/src/dyn_function_tests.cpp | 6 +- libs/dfi/gtest/src/dyn_type_tests.cpp | 6 +- libs/dfi/include/dyn_function.h | 4 +- libs/dfi/include/dyn_type.h | 95 +++--- libs/dfi/include/json_serializer.h | 4 +- libs/dfi/src/dyn_function.c | 12 +- libs/dfi/src/dyn_interface.c | 2 +- libs/dfi/src/dyn_type.c | 362 +++++++++++----------- libs/dfi/src/dyn_type_common.h | 22 +- libs/dfi/src/json_rpc.c | 35 +-- libs/dfi/src/json_serializer.c | 135 ++++---- 11 files changed, 324 insertions(+), 359 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 6164cbcc7..0af571e42 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -114,15 +114,15 @@ static bool func_acc() { int nrOfArgs = 0; bool isStruct = false; bool isVoid = false; - dyn_type *nonExist = nullptr; + const dyn_type *nonExist = nullptr; if (rc == 0) { nrOfArgs = dynFunction_nrOfArguments(dynFunc); - dyn_type *arg1 = dynFunction_argumentTypeForIndex(dynFunc, 1); + const dyn_type *arg1 = dynFunction_argumentTypeForIndex(dynFunc, 1); if (arg1 != nullptr) { isStruct = '{' == dynType_descriptorType(arg1); } nonExist = dynFunction_argumentTypeForIndex(dynFunc, 10); - dyn_type *returnType = dynFunction_returnType(dynFunc); + const dyn_type* returnType = dynFunction_returnType(dynFunc); if (returnType != nullptr) { isVoid = 'V' == dynType_descriptorType(returnType); } diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index cfa01ee22..ed4bbef49 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -188,10 +188,8 @@ TEST_F(DynTypeTests, AssignTest2) { dynType_complex_setValueAt(type, 0, &inst, &a); ASSERT_EQ(2, inst.a); - void *loc = NULL; - dyn_type *subType = NULL; - dynType_complex_valLocAt(type, 1, (void *)&inst, &loc); - dynType_complex_dynTypeAt(type, 1, &subType); + (void)dynType_complex_valLocAt(type, 1, (void *)&inst); + const dyn_type* subType = dynType_complex_dynTypeAt(type, 1); dynType_complex_setValueAt(subType, 0, &inst.b, &b_a); ASSERT_EQ(1.1, inst.b.a); diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index 7d506945c..c9f34d5d4 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -97,7 +97,7 @@ CELIX_DFI_EXPORT int dynFunction_nrOfArguments(dyn_function_type *dynFunc); * @param[in] argumentNr The argument index. * @return The argument type. */ -CELIX_DFI_EXPORT dyn_type *dynFunction_argumentTypeForIndex(dyn_function_type *dynFunc, int argumentNr); +CELIX_DFI_EXPORT const dyn_type* dynFunction_argumentTypeForIndex(dyn_function_type* dynFunc, int argumentNr); /** * @brief Returns the argument meta for the given argument index. @@ -112,7 +112,7 @@ CELIX_DFI_EXPORT enum dyn_function_argument_meta dynFunction_argumentMetaForInde * @param[in] dynFunc The dynamic type instance for function. * @return The return value type. */ -CELIX_DFI_EXPORT dyn_type * dynFunction_returnType(dyn_function_type *dynFunction); +CELIX_DFI_EXPORT const dyn_type* dynFunction_returnType(dyn_function_type *dynFunction); /** * @brief Destroys the given dynamic function type instance. diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index f56b08e9a..1ab2da416 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -59,8 +59,8 @@ extern "C" { * //Extended * b unsigned char * i uint32_t - * j uint62_t - * s uint64_t + * j uint64_t + * s uint16_t * P untyped pointer (void *) * t char* string * N native int @@ -112,64 +112,64 @@ typedef struct _dyn_type dyn_type; TAILQ_HEAD(types_head, type_entry); struct type_entry { - dyn_type *type; + dyn_type* type; TAILQ_ENTRY(type_entry) entries; }; TAILQ_HEAD(complex_type_entries_head, complex_type_entry); struct complex_type_entry { - dyn_type *type; - char *name; + dyn_type* type; + char* name; TAILQ_ENTRY(complex_type_entry) entries; }; TAILQ_HEAD(meta_properties_head, meta_entry); struct meta_entry { - char *name; - char *value; + char* name; + char* value; TAILQ_ENTRY(meta_entry) entries; }; /** * Parses a descriptor stream and creates a dyn_type (dynamic type). * If successful the type output argument points to the newly created dyn type. - * The caller is the owner of the dyn type and use dynType_destroy deallocate the memory. + * The caller is the owner of the dyn type and use dynType_destroy to deallocate the memory. * * In case of an error, an error message is added to celix_err. * * @param descriptorStream Stream to the descriptor. * @param name name for the dyn_type. This can be used in references to dyn types. - * @param refTypes A list if reference-able dyn types. + * @param refTypes A list of referable dyn types. * @param type Output argument for the parsed dyn type. * @return 0 if successful. */ -CELIX_DFI_EXPORT int dynType_parse(FILE *descriptorStream, const char *name, struct types_head *refTypes, dyn_type **type); +CELIX_DFI_EXPORT int dynType_parse(FILE* descriptorStream, const char* name, const struct types_head* refTypes, dyn_type** type); /** * Parses a descriptor string and creates a dyn_type (dynamic type). * If successful the type output argument points to the newly created dyn type. - * The caller is the owner of the dyn type and use dynType_destroy deallocate the memory. + * The caller is the owner of the dyn type and use dynType_destroy to deallocate the memory. * * In case of an error, an error message is added to celix_err. * * @param descriptor The descriptor. * @param name name for the dyn_type. This can be used in references to dyn types. - * @param refTypes A list if reference-able dyn types. + * @param refTypes A list if referable dyn types. * @param type Output argument for the parsed dyn type. * @return 0 if successful. */ -CELIX_DFI_EXPORT int dynType_parseWithStr(const char *descriptor, const char *name, struct types_head *refTypes, dyn_type **type); +CELIX_DFI_EXPORT int dynType_parseWithStr(const char* descriptor, const char* name, const struct types_head* refTypes, dyn_type** type); /** * Destroy a dyn type and de-allocates the memory. * @param type The dyn type to destroy. */ -CELIX_DFI_EXPORT void dynType_destroy(dyn_type *type); +CELIX_DFI_EXPORT void dynType_destroy(dyn_type* type); CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_type, dynType_destroy); /** - * Allocates memory for a type instance described by a dyn type. The memory will be 0 allocated (calloc). + * Allocates memory for a type instance described by a dyn type. The memory will be set to zero (calloc). * * In case of an error, an error message is added to celix_err. * @@ -177,7 +177,7 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_type, dynType_destroy); * @param instance The output argument for the allocated memory. * @return 0 on success. */ -CELIX_DFI_EXPORT int dynType_alloc(dyn_type *type, void **instance); +CELIX_DFI_EXPORT int dynType_alloc(const dyn_type* type, void** instance); /** * free the memory for a type instance described by a dyn type. @@ -186,23 +186,23 @@ CELIX_DFI_EXPORT int dynType_alloc(dyn_type *type, void **instance); * @param type The dyn type for which structure to allocate. * @param instance The memory location of the type instance. */ -CELIX_DFI_EXPORT void dynType_free(dyn_type *type, void *instance); +CELIX_DFI_EXPORT void dynType_free(const dyn_type* type, void* instance); /** * Prints the dyn type information to the provided output stream. * @param type The dyn type to print. * @param stream The output stream (e.g. stdout). */ -CELIX_DFI_EXPORT void dynType_print(dyn_type *type, FILE *stream); +CELIX_DFI_EXPORT void dynType_print(const dyn_type* type, FILE* stream); /** - * Return the size of the structure descripbed by the dyn type. - * This this _not_ includes sizes of data where is only pointed to (i.e. content of sequences, strings, etc). + * Return the size of the structure described by the dyn type. + * This does _not_ includes sizes of data which is only pointed to (i.e. content of sequences, strings, etc). * * @param type The dyn type. * @return The size of the type instance described by dyn type. */ -CELIX_DFI_EXPORT size_t dynType_size(dyn_type *type); +CELIX_DFI_EXPORT size_t dynType_size(const dyn_type* type); /** * The type of the dyn type @@ -210,7 +210,7 @@ CELIX_DFI_EXPORT size_t dynType_size(dyn_type *type); * @param type The dyn type * @return The type of the dyn type. */ -CELIX_DFI_EXPORT int dynType_type(dyn_type *type); +CELIX_DFI_EXPORT int dynType_type(const dyn_type* type); /** * Returns the char identifier of the dyn type type. @@ -218,7 +218,7 @@ CELIX_DFI_EXPORT int dynType_type(dyn_type *type); * @param type The dyn type * @return The descriptor of the dyn type. */ -CELIX_DFI_EXPORT char dynType_descriptorType(dyn_type *type); +CELIX_DFI_EXPORT char dynType_descriptorType(const dyn_type* type); /** * Get the dyn type meta information for the provided name. @@ -226,21 +226,20 @@ CELIX_DFI_EXPORT char dynType_descriptorType(dyn_type *type); * @param name The name of the requested meta information. * @return The meta information or NULL if the meta information is not present. */ -CELIX_DFI_EXPORT const char * dynType_getMetaInfo(dyn_type *type, const char *name); +CELIX_DFI_EXPORT const char* dynType_getMetaInfo(const dyn_type* type, const char* name); /** - * Returns a list of meta entries. + * Returns the list of meta entries. * Note that dyn type is owner of the entries. Traversing the entries is not thread safe. * @param type The dyn type. - * @param entries The output arguments for the meta entries. - * @return 0 on success. + * @note It always returns valid list. */ -CELIX_DFI_EXPORT int dynType_metaEntries(dyn_type *type, struct meta_properties_head **entries); +CELIX_DFI_EXPORT const struct meta_properties_head* dynType_metaEntries(const dyn_type* type); /** * Returns the name of the dyn type. Name can be NULL. */ -CELIX_DFI_EXPORT const char * dynType_getName(dyn_type *type); +CELIX_DFI_EXPORT const char* dynType_getName(const dyn_type* type); /** @@ -251,30 +250,27 @@ CELIX_DFI_EXPORT const char * dynType_getName(dyn_type *type); * @param name The field name. * @return The field index or -1 if no field with the name was found. */ -CELIX_DFI_EXPORT int dynType_complex_indexForName(dyn_type *type, const char *name); +CELIX_DFI_EXPORT int dynType_complex_indexForName(const dyn_type* type, const char* name); /** * Returns the field dyn type for a given complex type and a field index. * * @param type The dyn type. Must be a complex type. * @param index The field index for which field the dyn type should be returned. The field index must be correct. - * @param subType The field dyn type as output. - * exists. - * @return 0 if successful. */ -CELIX_DFI_EXPORT int dynType_complex_dynTypeAt(dyn_type *type, int index, dyn_type **subType); +CELIX_DFI_EXPORT const dyn_type* dynType_complex_dynTypeAt(const dyn_type* type, int index); -CELIX_DFI_EXPORT int dynType_complex_setValueAt(dyn_type *type, int index, void *inst, void *in); -CELIX_DFI_EXPORT int dynType_complex_valLocAt(dyn_type *type, int index, void *inst, void **valLoc); -CELIX_DFI_EXPORT int dynType_complex_entries(dyn_type *type, struct complex_type_entries_head **entries); -CELIX_DFI_EXPORT size_t dynType_complex_nrOfEntries(dyn_type *type); +CELIX_DFI_EXPORT int dynType_complex_setValueAt(const dyn_type* type, int index, void* inst, const void* in); +CELIX_DFI_EXPORT void* dynType_complex_valLocAt(const dyn_type* type, int index, void* inst); +CELIX_DFI_EXPORT const struct complex_type_entries_head* dynType_complex_entries(const dyn_type* type); +CELIX_DFI_EXPORT size_t dynType_complex_nrOfEntries(const dyn_type* type); //sequence /** * Initialize a sequence struct with a cap & len of 0 and the buf to NULL. */ -CELIX_DFI_EXPORT void dynType_sequence_init(dyn_type *type, void *inst); +CELIX_DFI_EXPORT void dynType_sequence_init(const dyn_type* type, void* inst); /** * Allocates memory for a sequence with capacity cap. @@ -284,7 +280,7 @@ CELIX_DFI_EXPORT void dynType_sequence_init(dyn_type *type, void *inst); * In case of an error, an error message is added to celix_err. * */ -CELIX_DFI_EXPORT int dynType_sequence_alloc(dyn_type *type, void *inst, uint32_t cap); +CELIX_DFI_EXPORT int dynType_sequence_alloc(const dyn_type* type, void* inst, uint32_t cap); /** * Reserve a sequence capacity of cap @@ -295,7 +291,7 @@ CELIX_DFI_EXPORT int dynType_sequence_alloc(dyn_type *type, void *inst, uint32_t * In case of an error, an error message is added to celix_err. * */ -CELIX_DFI_EXPORT int dynType_sequence_reserve(dyn_type *type, void *inst, uint32_t cap); +CELIX_DFI_EXPORT int dynType_sequence_reserve(const dyn_type* type, void* inst, uint32_t cap); /** * @brief Gets the value location for a specific sequence index from a sequence instance. @@ -309,7 +305,7 @@ CELIX_DFI_EXPORT int dynType_sequence_reserve(dyn_type *type, void *inst, uint32 * @return 0 if successful. * @retval 1 if the index is out of bounds. */ -CELIX_DFI_EXPORT int dynType_sequence_locForIndex(dyn_type *type, void *seqLoc, int index, void **valLoc); +CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, int index, void** valLoc); /** * @brief Increase the length of the sequence by one and return the value location for the last element. @@ -322,29 +318,26 @@ CELIX_DFI_EXPORT int dynType_sequence_locForIndex(dyn_type *type, void *seqLoc, * @return 0 if successful. * @retval 1 if the sequence length is already at the max. */ -CELIX_DFI_EXPORT int dynType_sequence_increaseLengthAndReturnLastLoc(dyn_type *type, void *seqLoc, void **valLoc); +CELIX_DFI_EXPORT int dynType_sequence_increaseLengthAndReturnLastLoc(const dyn_type* type, void* seqLoc, void** valLoc); /** * @brief Get the item type of a sequence. * @param[in] type The dyn type. Must be a sequence type. * @return The item type of the sequence. */ -CELIX_DFI_EXPORT dyn_type * dynType_sequence_itemType(dyn_type *type); +CELIX_DFI_EXPORT const dyn_type* dynType_sequence_itemType(const dyn_type* type); /** * @brief Get the length of a sequence. * @param[in] seqLoc The sequence instance. * @return The length of the sequence. */ -CELIX_DFI_EXPORT uint32_t dynType_sequence_length(void *seqLoc); +CELIX_DFI_EXPORT uint32_t dynType_sequence_length(const void* seqLoc); /** * @brief Gets the typedType of a typedPointer type. - * @param[in] type The dyn type. Must be a typedPointer type. - * @param[out] typedType The typedType of the typedPointer type. - * @return 0 */ -CELIX_DFI_EXPORT int dynType_typedPointer_getTypedType(dyn_type *type, dyn_type **typedType); +CELIX_DFI_EXPORT const dyn_type* dynType_typedPointer_getTypedType(const dyn_type* type); /** * @brief Allocates and initializes a string type. @@ -357,7 +350,7 @@ CELIX_DFI_EXPORT int dynType_typedPointer_getTypedType(dyn_type *type, dyn_type * @return 0 if successful. * @retval 1 if Cannot allocate memory. */ -CELIX_DFI_EXPORT int dynType_text_allocAndInit(dyn_type *type, void *textLoc, const char *value); +CELIX_DFI_EXPORT int dynType_text_allocAndInit(const dyn_type* type, void* textLoc, const char* value); /** * @brief Sets the value of a simple type. @@ -365,7 +358,7 @@ CELIX_DFI_EXPORT int dynType_text_allocAndInit(dyn_type *type, void *textLoc, co * @param[out] inst The instance of the simple type. * @param[in] in The value to set. */ -CELIX_DFI_EXPORT void dynType_simple_setValue(dyn_type *type, void *inst, void *in); +CELIX_DFI_EXPORT void dynType_simple_setValue(const dyn_type* type, void* inst, const void* in); #ifdef __cplusplus } diff --git a/libs/dfi/include/json_serializer.h b/libs/dfi/include/json_serializer.h index 2e5d2ce93..5d38b7ebc 100644 --- a/libs/dfi/include/json_serializer.h +++ b/libs/dfi/include/json_serializer.h @@ -58,7 +58,7 @@ CELIX_DFI_EXPORT int jsonSerializer_deserialize(dyn_type *type, const char *inpu * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonSerializer_deserializeJson(dyn_type *type, json_t *input, void **result); +CELIX_DFI_EXPORT int jsonSerializer_deserializeJson(const dyn_type *type, json_t *input, void **result); /** * @brief Serialize a given type to a JSON string. @@ -88,7 +88,7 @@ CELIX_DFI_EXPORT int jsonSerializer_serialize(dyn_type *type, const void* input, * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonSerializer_serializeJson(dyn_type *type, const void* input, json_t **out); +CELIX_DFI_EXPORT int jsonSerializer_serializeJson(const dyn_type *type, const void* input, json_t **out); #ifdef __cplusplus } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index 65739ae37..69a7dcf99 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -286,10 +286,10 @@ int dynFunction_nrOfArguments(dyn_function_type *dynFunc) { return count; } -dyn_type *dynFunction_argumentTypeForIndex(dyn_function_type *dynFunc, int argumentNr) { - dyn_type *result = NULL; +const dyn_type* dynFunction_argumentTypeForIndex(dyn_function_type* dynFunc, int argumentNr) { + dyn_type* result = NULL; int index = 0; - dyn_function_argument_type *entry = NULL; + dyn_function_argument_type* entry = NULL; TAILQ_FOREACH(entry, &dynFunc->arguments, entries) { if (index == argumentNr) { result = entry->type; @@ -300,12 +300,12 @@ dyn_type *dynFunction_argumentTypeForIndex(dyn_function_type *dynFunc, int argum return result; } -dyn_type * dynFunction_returnType(dyn_function_type *dynFunction) { +const dyn_type* dynFunction_returnType(dyn_function_type *dynFunction) { return dynFunction->funcReturn; } -bool dynFunction_hasReturn(dyn_function_type *dynFunction) { - dyn_type *t = dynFunction_returnType(dynFunction); +bool dynFunction_hasReturn(dyn_function_type* dynFunction) { + const dyn_type* t = dynFunction_returnType(dynFunction); return t->descriptor != 'V'; } diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 8d5d5b4f5..6ac50a8a7 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -108,7 +108,7 @@ static int dynInterface_checkInterface(dyn_interface_type *intf) { struct method_entry *mEntry = NULL; TAILQ_FOREACH(mEntry, &intf->methods, entries) { - dyn_type *type = dynFunction_returnType(mEntry->dynFunc); + const dyn_type* type = dynFunction_returnType(mEntry->dynFunc); int descriptor = dynType_descriptorType(type); if (descriptor != 'N') { celix_err_pushf("Parse Error. Only method with a return type 'N' (native int) are supported. Got return type '%c'\n", descriptor); diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 507f6c4a1..c533b8f39 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -34,59 +34,59 @@ static const int ERROR = 1; static const int MEM_ERROR = 2; static const int PARSE_ERROR = 3; -static int dynType_parseWithStream(FILE *stream, const char *name, dyn_type *parent, struct types_head *refTypes, dyn_type **result); -static void dynType_clear(dyn_type *type); -static void dynType_clearComplex(dyn_type *type); -static void dynType_clearSequence(dyn_type *type); -static void dynType_clearTypedPointer(dyn_type *type); -ffi_type * dynType_ffiType(dyn_type *type); - -static struct type_entry *dynType_allocTypeEntry(void); - -static ffi_type * dynType_ffiTypeFor(int c); -static int dynType_parseAny(FILE *stream, dyn_type *type); -static int dynType_parseComplex(FILE *stream, dyn_type *type); -static int dynType_parseNestedType(FILE *stream, dyn_type *type); -static int dynType_parseReference(FILE *stream, dyn_type *type); -static int dynType_parseRefByValue(FILE *stream, dyn_type *type); -static int dynType_parseSequence(FILE *stream, dyn_type *type); -static int dynType_parseSimple(int c, dyn_type *type); -static int dynType_parseTypedPointer(FILE *stream, dyn_type *type); -static unsigned short dynType_getOffset(dyn_type *type, int index); - -static void dynType_printAny(char *name, dyn_type *type, int depth, FILE *stream); -static void dynType_printComplex(char *name, dyn_type *type, int depth, FILE *stream); -static void dynType_printSequence(char *name, dyn_type *type, int depth, FILE *stream); -static void dynType_printSimple(char *name, dyn_type *type, int depth, FILE *stream); -static void dynType_printEnum(char *name, dyn_type *type, int depth, FILE *stream); -static void dynType_printTypedPointer(char *name, dyn_type *type, int depth, FILE *stream); -static void dynType_printDepth(int depth, FILE *stream); - -static void dynType_printTypes(dyn_type *type, FILE *stream); -static void dynType_printComplexType(dyn_type *type, FILE *stream); -static void dynType_printSimpleType(dyn_type *type, FILE *stream); - -static int dynType_parseText(FILE *stream, dyn_type *type); -static int dynType_parseEnum(FILE *stream, dyn_type *type); -void dynType_freeComplexType(dyn_type *type, void *loc); -void dynType_deepFree(dyn_type *type, void *loc, bool alsoDeleteSelf); -void dynType_freeSequenceType(dyn_type *type, void *seqLoc); - -static int dynType_parseMetaInfo(FILE *stream, dyn_type *type); +static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); +static void dynType_clear(dyn_type* type); +static void dynType_clearComplex(dyn_type* type); +static void dynType_clearSequence(dyn_type* type); +static void dynType_clearTypedPointer(dyn_type* type); +ffi_type* dynType_ffiType(dyn_type* type); + +static struct type_entry* dynType_allocTypeEntry(void); + +static ffi_type* dynType_ffiTypeFor(int c); +static int dynType_parseAny(FILE* stream, dyn_type* type); +static int dynType_parseComplex(FILE* stream, dyn_type* type); +static int dynType_parseNestedType(FILE* stream, dyn_type* type); +static int dynType_parseReference(FILE* stream, dyn_type* type); +static int dynType_parseRefByValue(FILE* stream, dyn_type* type); +static int dynType_parseSequence(FILE* stream, dyn_type* type); +static int dynType_parseSimple(int c, dyn_type* type); +static int dynType_parseTypedPointer(FILE* stream, dyn_type* type); +static unsigned short dynType_getOffset(const dyn_type* type, int index); + +static void dynType_printAny(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printComplex(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printSequence(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printSimple(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printEnum(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printTypedPointer(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printDepth(int depth, FILE* stream); + +static void dynType_printTypes(const dyn_type* type, FILE* stream); +static void dynType_printComplexType(dyn_type* type, FILE* stream); +static void dynType_printSimpleType(dyn_type* type, FILE* stream); + +static int dynType_parseText(FILE* stream, dyn_type* type); +static int dynType_parseEnum(FILE* stream, dyn_type* type); +static void dynType_freeComplexType(const dyn_type* type, void* loc); +static void dynType_deepFree(const dyn_type* type, void* loc, bool alsoDeleteSelf); +static void dynType_freeSequenceType(const dyn_type* type, void* seqLoc); + +static int dynType_parseMetaInfo(FILE* stream, dyn_type* type); struct generic_sequence { uint32_t cap; uint32_t len; - void *buf; + void* buf; }; -int dynType_parse(FILE *descriptorStream, const char *name, struct types_head *refTypes, dyn_type **type) { +int dynType_parse(FILE* descriptorStream, const char* name, const struct types_head* refTypes, dyn_type** type) { return dynType_parseWithStream(descriptorStream, name, NULL, refTypes, type); } -int dynType_parseWithStr(const char *descriptor, const char *name, struct types_head *refTypes, dyn_type **type) { +int dynType_parseWithStr(const char* descriptor, const char* name, const struct types_head* refTypes, dyn_type** type) { int status = OK; - FILE *stream = fmemopen((char *)descriptor, strlen(descriptor) + 1, "r"); + FILE* stream = fmemopen((char *)descriptor, strlen(descriptor) + 1, "r"); if (stream != NULL) { status = dynType_parseWithStream(stream, name, NULL, refTypes, type); if (status == OK) { @@ -106,9 +106,9 @@ int dynType_parseWithStr(const char *descriptor, const char *name, struct types_ return status; } -static int dynType_parseWithStream(FILE *stream, const char *name, dyn_type *parent, struct types_head *refTypes, dyn_type **result) { +static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { int status = OK; - dyn_type *type = calloc(1, sizeof(*type)); + dyn_type* type = calloc(1, sizeof(*type)); if (type != NULL) { type->parent = parent; type->type = DYN_TYPE_INVALID; @@ -137,7 +137,7 @@ static int dynType_parseWithStream(FILE *stream, const char *name, dyn_type *par return status; } -static int dynType_parseAny(FILE *stream, dyn_type *type) { +static int dynType_parseAny(FILE* stream, dyn_type* type) { int status = OK; int c = fgetc(stream); @@ -183,10 +183,10 @@ static int dynType_parseAny(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseMetaInfo(FILE *stream, dyn_type *type) { +static int dynType_parseMetaInfo(FILE* stream, dyn_type* type) { int status = OK; - char *name = NULL; - char *value = NULL; + char* name = NULL; + char* value = NULL; struct meta_entry *entry = calloc(1, sizeof(*entry)); if (entry == NULL) { @@ -223,7 +223,7 @@ static int dynType_parseMetaInfo(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseText(FILE *stream, dyn_type *type) { +static int dynType_parseText(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_TEXT; type->descriptor = 't'; @@ -231,7 +231,7 @@ static int dynType_parseText(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseEnum(FILE *stream, dyn_type *type) { +static int dynType_parseEnum(FILE* stream, dyn_type* type) { int status = OK; type->ffiType = &ffi_type_sint32; type->descriptor = 'E'; @@ -239,7 +239,7 @@ static int dynType_parseEnum(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseComplex(FILE *stream, dyn_type *type) { +static int dynType_parseComplex(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_COMPLEX; type->descriptor = '{'; @@ -247,7 +247,7 @@ static int dynType_parseComplex(FILE *stream, dyn_type *type) { TAILQ_INIT(&type->complex.entriesHead); int c = fgetc(stream); - struct complex_type_entry *entry = NULL; + struct complex_type_entry* entry = NULL; while (c != ' ' && c != '}') { ungetc(c,stream); entry = calloc(1, sizeof(*entry)); @@ -277,7 +277,7 @@ static int dynType_parseComplex(FILE *stream, dyn_type *type) { // loop over names if (status == OK) { entry = TAILQ_FIRST(&type->complex.entriesHead); - char *name = NULL; + char* name = NULL; while (c == ' ' && entry != NULL) { status = dynCommon_parseName(stream, &name); if (status == OK) { @@ -333,10 +333,10 @@ static int dynType_parseComplex(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseNestedType(FILE *stream, dyn_type *type) { +static int dynType_parseNestedType(FILE* stream, dyn_type* type) { int status = OK; - char *name = NULL; - struct type_entry *entry = NULL; + char* name = NULL; + struct type_entry* entry = NULL; entry = dynType_allocTypeEntry(); if (entry != NULL) { @@ -372,7 +372,7 @@ static int dynType_parseNestedType(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseReference(FILE *stream, dyn_type *type) { +static int dynType_parseReference(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_TYPED_POINTER; type->descriptor = '*'; @@ -380,7 +380,7 @@ static int dynType_parseReference(FILE *stream, dyn_type *type) { type->ffiType = &ffi_type_pointer; type->typedPointer.typedType = NULL; - dyn_type *subType = calloc(1, sizeof(*subType)); + dyn_type* subType = calloc(1, sizeof(*subType)); if (subType != NULL) { type->typedPointer.typedType = subType; @@ -397,15 +397,15 @@ static int dynType_parseReference(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseRefByValue(FILE *stream, dyn_type *type) { +static int dynType_parseRefByValue(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_REF; type->descriptor = 'l'; - char *name = NULL; + char* name = NULL; status = dynCommon_parseName(stream, &name); if (status == OK) { - dyn_type *ref = dynType_findType(type, name); + dyn_type* ref = dynType_findType(type, name); if (ref != NULL) { type->ref.ref = ref; } else { @@ -426,8 +426,8 @@ static int dynType_parseRefByValue(FILE *stream, dyn_type *type) { return status; } -static struct type_entry *dynType_allocTypeEntry(void) { - struct type_entry *entry = calloc(1, sizeof(*entry)); +static struct type_entry* dynType_allocTypeEntry(void) { + struct type_entry* entry = calloc(1, sizeof(*entry)); if (entry != NULL) { entry->type = calloc(1, sizeof(*entry->type)); if (entry->type == NULL) { @@ -438,9 +438,9 @@ static struct type_entry *dynType_allocTypeEntry(void) { return entry; } -static ffi_type *seq_types[] = {&ffi_type_uint32, &ffi_type_uint32, &ffi_type_pointer, NULL}; +static ffi_type* seq_types[] = {&ffi_type_uint32, &ffi_type_uint32, &ffi_type_pointer, NULL}; -static int dynType_parseSequence(FILE *stream, dyn_type *type) { +static int dynType_parseSequence(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_SEQUENCE; type->descriptor = '['; @@ -460,9 +460,9 @@ static int dynType_parseSequence(FILE *stream, dyn_type *type) { return status; } -static int dynType_parseSimple(int c, dyn_type *type) { +static int dynType_parseSimple(int c, dyn_type* type) { int status = OK; - ffi_type *ffiType = dynType_ffiTypeFor(c); + ffi_type* ffiType = dynType_ffiTypeFor(c); if (ffiType != NULL) { type->type = DYN_TYPE_SIMPLE; type->descriptor = c; @@ -475,7 +475,7 @@ static int dynType_parseSimple(int c, dyn_type *type) { return status; } -static int dynType_parseTypedPointer(FILE *stream, dyn_type *type) { +static int dynType_parseTypedPointer(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_TYPED_POINTER; type->descriptor = '*'; @@ -486,16 +486,16 @@ static int dynType_parseTypedPointer(FILE *stream, dyn_type *type) { return status; } -void dynType_destroy(dyn_type *type) { +void dynType_destroy(dyn_type* type) { if (type != NULL) { dynType_clear(type); free(type); } } -static void dynType_clear(dyn_type *type) { - struct type_entry *entry = TAILQ_FIRST(&type->nestedTypesHead); - struct type_entry *tmp = NULL; +static void dynType_clear(dyn_type* type) { + struct type_entry* entry = TAILQ_FIRST(&type->nestedTypesHead); + struct type_entry* tmp = NULL; while (entry != NULL) { tmp = entry; entry = TAILQ_NEXT(entry, entries); @@ -506,8 +506,8 @@ static void dynType_clear(dyn_type *type) { free(tmp); } - struct meta_entry *mEntry = TAILQ_FIRST(&type->metaProperties);; - struct meta_entry *next = NULL; + struct meta_entry* mEntry = TAILQ_FIRST(&type->metaProperties);; + struct meta_entry* next = NULL; while (mEntry != NULL) { next = TAILQ_NEXT(mEntry, entries); if (mEntry != NULL) { @@ -535,10 +535,10 @@ static void dynType_clear(dyn_type *type) { } } -static void dynType_clearComplex(dyn_type *type) { +static void dynType_clearComplex(dyn_type* type) { assert(type->type == DYN_TYPE_COMPLEX); - struct complex_type_entry *entry = TAILQ_FIRST(&type->complex.entriesHead); - struct complex_type_entry *tmp = NULL; + struct complex_type_entry* entry = TAILQ_FIRST(&type->complex.entriesHead); + struct complex_type_entry* tmp = NULL; while (entry != NULL) { dynType_destroy(entry->type); if (entry->name != NULL) { @@ -556,27 +556,27 @@ static void dynType_clearComplex(dyn_type *type) { } } -static void dynType_clearSequence(dyn_type *type) { +static void dynType_clearSequence(dyn_type* type) { assert(type->type == DYN_TYPE_SEQUENCE); if (type->sequence.itemType != NULL) { dynType_destroy(type->sequence.itemType); } } -static void dynType_clearTypedPointer(dyn_type *type) { +static void dynType_clearTypedPointer(dyn_type* type) { assert(type->type == DYN_TYPE_TYPED_POINTER); if (type->typedPointer.typedType != NULL) { dynType_destroy(type->typedPointer.typedType); } } -int dynType_alloc(dyn_type *type, void **bufLoc) { +int dynType_alloc(const dyn_type* type, void** bufLoc) { int status = OK; if (type->type == DYN_TYPE_REF) { status = dynType_alloc(type->ref.ref, bufLoc); } else { - void *inst = calloc(1, type->ffiType->size); + void* inst = calloc(1, type->ffiType->size); if (inst != NULL) { *bufLoc = inst; } else { @@ -589,14 +589,14 @@ int dynType_alloc(dyn_type *type, void **bufLoc) { } -int dynType_complex_indexForName(dyn_type *type, const char *name) { +int dynType_complex_indexForName(const dyn_type* type, const char* name) { assert(type->type == DYN_TYPE_COMPLEX); if (name == NULL) { return -1; } int i = 0; int index = -1; - struct complex_type_entry *entry = NULL; + struct complex_type_entry* entry = NULL; TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { if (entry->name != NULL && strcmp(name, entry->name) == 0) { index = i; @@ -607,63 +607,58 @@ int dynType_complex_indexForName(dyn_type *type, const char *name) { return index; } -int dynType_complex_dynTypeAt(dyn_type *type, int index, dyn_type **result) { +const dyn_type* dynType_complex_dynTypeAt(const dyn_type* type, int index) { assert(type->type == DYN_TYPE_COMPLEX); assert(index >= 0); - dyn_type *sub = type->complex.types[index]; + dyn_type* sub = type->complex.types[index]; if (sub->type == DYN_TYPE_REF) { sub = sub->ref.ref; } - *result = sub; - return 0; + return sub; } -int dynType_complex_setValueAt(dyn_type *type, int index, void *start, void *in) { +int dynType_complex_setValueAt(const dyn_type* type, int index, void* start, const void* in) { assert(type->type == DYN_TYPE_COMPLEX); - char *loc = ((char *)start) + dynType_getOffset(type, index); + char* loc = ((char*)start) + dynType_getOffset(type, index); size_t size = type->complex.structType.elements[index]->size; memcpy(loc, in, size); return 0; } -int dynType_complex_valLocAt(dyn_type *type, int index, void *inst, void **result) { +void* dynType_complex_valLocAt(const dyn_type* type, int index, void* inst) { assert(type->type == DYN_TYPE_COMPLEX); - char *l = (char *)inst; - void *loc = (void *)(l + dynType_getOffset(type, index)); - *result = loc; - return OK; + char* l = (char*)inst; + return (void* )(l + dynType_getOffset(type, index)); } -size_t dynType_complex_nrOfEntries(dyn_type *type) { +size_t dynType_complex_nrOfEntries(const dyn_type* type) { size_t count = 0; - struct complex_type_entry *entry = NULL; + struct complex_type_entry* entry = NULL; TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { ++count; } return count; } -int dynType_complex_entries(dyn_type *type, struct complex_type_entries_head **entries) { +const struct complex_type_entries_head* dynType_complex_entries(const dyn_type* type) { assert(type->type == DYN_TYPE_COMPLEX); - int status = OK; - *entries = &type->complex.entriesHead; - return status; + return &type->complex.entriesHead; } //sequence -void dynType_sequence_init(dyn_type *type, void *inst) { +void dynType_sequence_init(const dyn_type* type, void* inst) { assert(type->type == DYN_TYPE_SEQUENCE); - struct generic_sequence *seq = inst; + struct generic_sequence* seq = inst; seq->buf = NULL; seq->cap = 0; seq->len = 0; } -int dynType_sequence_alloc(dyn_type *type, void *inst, uint32_t cap) { +int dynType_sequence_alloc(const dyn_type* type, void* inst, uint32_t cap) { assert(type->type == DYN_TYPE_SEQUENCE); int status = OK; - struct generic_sequence *seq = inst; + struct generic_sequence* seq = inst; if (seq != NULL) { size_t size = dynType_size(type->sequence.itemType); seq->buf = calloc(cap, size); @@ -682,10 +677,10 @@ int dynType_sequence_alloc(dyn_type *type, void *inst, uint32_t cap) { return status; } -int dynType_sequence_reserve(dyn_type *type, void *inst, uint32_t cap) { +int dynType_sequence_reserve(const dyn_type* type, void* inst, uint32_t cap) { assert(type->type == DYN_TYPE_SEQUENCE); int status = OK; - struct generic_sequence *seq = inst; + struct generic_sequence* seq = inst; if (seq != NULL && seq->cap < cap) { size_t size = dynType_size(type->sequence.itemType); seq->buf = realloc(seq->buf, (size_t)(cap * size)); @@ -703,14 +698,14 @@ int dynType_sequence_reserve(dyn_type *type, void *inst, uint32_t cap) { return status; } -void dynType_free(dyn_type *type, void *loc) { +void dynType_free(const dyn_type* type, void* loc) { dynType_deepFree(type, loc, true); } -void dynType_deepFree(dyn_type *type, void *loc, bool alsoDeleteSelf) { +static void dynType_deepFree(const dyn_type* type, void* loc, bool alsoDeleteSelf) { if (loc != NULL) { - dyn_type *subType = NULL; - char *text = NULL; + const dyn_type* subType = NULL; + char* text = NULL; switch (type->type) { case DYN_TYPE_REF: //NOTE: do not recursively forward asloDeleteSelf, because this is already handled in this function) @@ -723,12 +718,12 @@ void dynType_deepFree(dyn_type *type, void *loc, bool alsoDeleteSelf) { dynType_freeSequenceType(type, loc); break; case DYN_TYPE_TYPED_POINTER: - dynType_typedPointer_getTypedType(type, &subType); + subType = dynType_typedPointer_getTypedType(type); void *ptrToType = *(void**)loc; dynType_deepFree(subType, ptrToType, true); break; case DYN_TYPE_TEXT : - text = *(char **)loc; + text = *(char**)loc; free(text); break; case DYN_TYPE_SIMPLE: @@ -745,10 +740,10 @@ void dynType_deepFree(dyn_type *type, void *loc, bool alsoDeleteSelf) { } } -void dynType_freeSequenceType(dyn_type *type, void *seqLoc) { - struct generic_sequence *seq = seqLoc; - dyn_type *itemType = dynType_sequence_itemType(type); - void *itemLoc = NULL; +static void dynType_freeSequenceType(const dyn_type* type, void* seqLoc) { + struct generic_sequence* seq = seqLoc; + const dyn_type* itemType = dynType_sequence_itemType(type); + void* itemLoc = NULL; int i; for (i = 0; i < seq->len; ++i) { dynType_sequence_locForIndex(type, seqLoc, i, &itemLoc); @@ -757,27 +752,25 @@ void dynType_freeSequenceType(dyn_type *type, void *seqLoc) { free(seq->buf); } -void dynType_freeComplexType(dyn_type *type, void *loc) { - struct complex_type_entry *entry = NULL; +static void dynType_freeComplexType(const dyn_type* type, void* loc) { + struct complex_type_entry* entry = NULL; int index = 0; - void *entryLoc = NULL; TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { - dynType_complex_valLocAt(type, index++, loc, &entryLoc); - dynType_deepFree(entry->type, entryLoc, false); + dynType_deepFree(entry->type, dynType_complex_valLocAt(type, index++, loc), false); } } -uint32_t dynType_sequence_length(void *seqLoc) { - struct generic_sequence *seq = seqLoc; +uint32_t dynType_sequence_length(const void *seqLoc) { + const struct generic_sequence* seq = seqLoc; return seq->len; } -int dynType_sequence_locForIndex(dyn_type *type, void *seqLoc, int index, void **out) { +int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, int index, void** out) { assert(type->type == DYN_TYPE_SEQUENCE); int status = OK; - struct generic_sequence *seq = seqLoc; + struct generic_sequence* seq = seqLoc; size_t itemSize = dynType_size(type->sequence.itemType); @@ -792,17 +785,17 @@ int dynType_sequence_locForIndex(dyn_type *type, void *seqLoc, int index, void * } if (status == OK) { - char *valLoc = seq->buf + (index * itemSize); + char* valLoc = seq->buf + (index * itemSize); (*out) = valLoc; } return status; } -int dynType_sequence_increaseLengthAndReturnLastLoc(dyn_type *type, void *seqLoc, void **valLoc) { +int dynType_sequence_increaseLengthAndReturnLastLoc(const dyn_type* type, void* seqLoc, void** valLoc) { assert(type->type == DYN_TYPE_SEQUENCE); int status = OK; - struct generic_sequence *seq = seqLoc; + struct generic_sequence* seq = seqLoc; int lastIndex = seq->len; if (seq->len < seq->cap) { @@ -819,7 +812,7 @@ int dynType_sequence_increaseLengthAndReturnLastLoc(dyn_type *type, void *seqLoc return status; } -dyn_type * dynType_sequence_itemType(dyn_type *type) { +const dyn_type* dynType_sequence_itemType(const dyn_type* type) { assert(type->type == DYN_TYPE_SEQUENCE); dyn_type *itemType = type->sequence.itemType; if (itemType->type == DYN_TYPE_REF) { @@ -828,18 +821,18 @@ dyn_type * dynType_sequence_itemType(dyn_type *type) { return itemType; } -void dynType_simple_setValue(dyn_type *type, void *inst, void *in) { +void dynType_simple_setValue(const dyn_type *type, void *inst, const void *in) { size_t size = dynType_size(type); memcpy(inst, in, size); } -char dynType_descriptorType(dyn_type *type) { +char dynType_descriptorType(const dyn_type* type) { return type->descriptor; } -const char * dynType_getMetaInfo(dyn_type *type, const char *name) { - const char *result = NULL; - struct meta_entry *entry = NULL; +const char* dynType_getMetaInfo(const dyn_type* type, const char* name) { + const char* result = NULL; + struct meta_entry* entry = NULL; TAILQ_FOREACH(entry, &type->metaProperties, entries) { if (strcmp(entry->name, name) == 0) { result = entry->value; @@ -849,17 +842,16 @@ const char * dynType_getMetaInfo(dyn_type *type, const char *name) { return result; } -int dynType_metaEntries(dyn_type *type, struct meta_properties_head **entries) { - *entries = &type->metaProperties; - return OK; +const struct meta_properties_head* dynType_metaEntries(const dyn_type* type) { + return &type->metaProperties; } -const char * dynType_getName(dyn_type *type) { +const char* dynType_getName(const dyn_type* type) { return type->name; } -static ffi_type * dynType_ffiTypeFor(int c) { - ffi_type *type = NULL; +static ffi_type* dynType_ffiTypeFor(int c) { + ffi_type* type = NULL; switch (c) { case 'Z' : type = &ffi_type_uint8; @@ -907,11 +899,11 @@ static ffi_type * dynType_ffiTypeFor(int c) { return type; } -static unsigned short dynType_getOffset(dyn_type *type, int index) { +static unsigned short dynType_getOffset(const dyn_type* type, int index) { assert(type->type == DYN_TYPE_COMPLEX); unsigned short offset = 0; - ffi_type *ffiType = &type->complex.structType; + const ffi_type* ffiType = &type->complex.structType; int i; for (i = 0; i <= index && ffiType->elements[i] != NULL; i += 1) { size_t size = ffiType->elements[i]->size; @@ -928,38 +920,33 @@ static unsigned short dynType_getOffset(dyn_type *type, int index) { return offset; } -size_t dynType_size(dyn_type *type) { - dyn_type *rType = type; +size_t dynType_size(const dyn_type* type) { + const dyn_type* rType = type; if (type->type == DYN_TYPE_REF) { rType = type->ref.ref; } return rType->ffiType->size; } -int dynType_type(dyn_type *type) { +int dynType_type(const dyn_type* type) { return type->type; } - -int dynType_typedPointer_getTypedType(dyn_type *type, dyn_type **out) { +const dyn_type* dynType_typedPointer_getTypedType(const dyn_type* type) { assert(type->type == DYN_TYPE_TYPED_POINTER); - int status = 0; - - dyn_type *typedType = type->typedPointer.typedType; + dyn_type* typedType = type->typedPointer.typedType; if (typedType->type == DYN_TYPE_REF) { typedType = typedType->ref.ref; } - - *out = typedType; - return status; + return typedType; } -int dynType_text_allocAndInit(dyn_type *type, void *textLoc, const char *value) { +int dynType_text_allocAndInit(const dyn_type* type, void* textLoc, const char* value) { assert(type->type == DYN_TYPE_TEXT); int status = 0; - const char *str = strdup(value); - char const **loc = textLoc; + const char* str = strdup(value); + char const** loc = textLoc; if (str != NULL) { *loc = str; } else { @@ -973,7 +960,7 @@ int dynType_text_allocAndInit(dyn_type *type, void *textLoc, const char *value) -void dynType_print(dyn_type *type, FILE *stream) { +void dynType_print(const dyn_type *type, FILE *stream) { if (type != NULL) { dynType_printTypes(type, stream); @@ -991,8 +978,8 @@ static void dynType_printDepth(int depth, FILE *stream) { } } -static void dynType_printAny(char *name, dyn_type *type, int depth, FILE *stream) { - dyn_type *toPrint = type; +static void dynType_printAny(const char* name, const dyn_type* type, int depth, FILE *stream) { + const dyn_type* toPrint = type; if (toPrint->type == DYN_TYPE_REF) { toPrint = toPrint->ref.ref; } @@ -1015,12 +1002,13 @@ static void dynType_printAny(char *name, dyn_type *type, int depth, FILE *stream } } -static void dynType_printComplex(char *name, dyn_type *type, int depth, FILE *stream) { +static void dynType_printComplex(const char* name, const dyn_type* type, int depth, FILE *stream) { if (type->name == NULL) { dynType_printDepth(depth, stream); - fprintf(stream, "%s: complex type (anon), size is %zu, alignment is %i, descriptor is '%c'. fields:\n", name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + fprintf(stream, "%s: complex type (anon), size is %zu, alignment is %i, descriptor is '%c'. fields:\n", + name, type->ffiType->size, type->ffiType->alignment, type->descriptor); - struct complex_type_entry *entry = NULL; + struct complex_type_entry* entry = NULL; TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { dynType_printAny(entry->name, entry->type, depth + 1, stream); } @@ -1029,49 +1017,57 @@ static void dynType_printComplex(char *name, dyn_type *type, int depth, FILE *st fprintf(stream, "}\n"); } else { dynType_printDepth(depth, stream); - fprintf(stream, "%s: complex type ('%s'), size is %zu, alignment is %i, descriptor is '%c'.\n", name, type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + fprintf(stream, "%s: complex type ('%s'), size is %zu, alignment is %i, descriptor is '%c'.\n", + name, type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); } } -static void dynType_printSequence(char *name, dyn_type *type, int depth, FILE *stream) { +static void dynType_printSequence(const char* name, const dyn_type* type, int depth, FILE* stream) { dynType_printDepth(depth, stream); - fprintf(stream, "sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", type->ffiType->size, type->ffiType->alignment, type->descriptor); + fprintf(stream, "sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", + type->ffiType->size, type->ffiType->alignment, type->descriptor); dynType_printDepth(depth + 1, stream); - fprintf(stream, "cap: simple type, size is %zu, alignment is %i.\n", type->sequence.seqType.elements[0]->size, type->sequence.seqType.elements[0]->alignment); + fprintf(stream, "cap: simple type, size is %zu, alignment is %i.\n", + type->sequence.seqType.elements[0]->size, type->sequence.seqType.elements[0]->alignment); dynType_printDepth(depth + 1, stream); - fprintf(stream, "len: simple type, size is %zu, alignment is %i.\n", type->sequence.seqType.elements[1]->size, type->sequence.seqType.elements[1]->alignment); + fprintf(stream, "len: simple type, size is %zu, alignment is %i.\n", + type->sequence.seqType.elements[1]->size, type->sequence.seqType.elements[1]->alignment); dynType_printDepth(depth + 1, stream); - fprintf(stream, "buf: array, size is %zu, alignment is %i. points to ->\n", type->sequence.seqType.elements[2]->size, type->sequence.seqType.elements[2]->alignment); + fprintf(stream, "buf: array, size is %zu, alignment is %i. points to ->\n", + type->sequence.seqType.elements[2]->size, type->sequence.seqType.elements[2]->alignment); dynType_printAny("element", type->sequence.itemType, depth + 1, stream); } -static void dynType_printSimple(char *name, dyn_type *type, int depth, FILE *stream) { +static void dynType_printSimple(const char* name, const dyn_type* type, int depth, FILE* stream) { if (type->descriptor != 'E') { dynType_printDepth(depth, stream); - fprintf(stream, "%s: simple type, size is %zu, alignment is %i, descriptor is '%c'.\n", name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + fprintf(stream, "%s: simple type, size is %zu, alignment is %i, descriptor is '%c'.\n", + name, type->ffiType->size, type->ffiType->alignment, type->descriptor); } else { dynType_printEnum(name, type, depth, stream); } } -static void dynType_printEnum(char *name, dyn_type *type, int depth, FILE *stream) { +static void dynType_printEnum(const char* name, const dyn_type* type, int depth, FILE* stream) { dynType_printDepth(depth, stream); - fprintf(stream, "%s: enum type, size is %zu, alignment is %i, descriptor is '%c'. values:", name, type->ffiType->size, type->ffiType->alignment, type->descriptor); - struct meta_entry * m_entry; + fprintf(stream, "%s: enum type, size is %zu, alignment is %i, descriptor is '%c'. values:", + name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + struct meta_entry* m_entry; TAILQ_FOREACH(m_entry, &type->metaProperties, entries) { fprintf(stream, " (\"%s\":\"%s\")", m_entry->name, m_entry->value); } fprintf(stream, "\n"); } -static void dynType_printTypedPointer(char *name, dyn_type *type, int depth, FILE *stream) { +static void dynType_printTypedPointer(const char* name, const dyn_type* type, int depth, FILE* stream) { dynType_printDepth(depth, stream); - fprintf(stream, "%s: typed pointer, size is %zu, alignment is %i, points to ->\n", name, type->ffiType->size, type->ffiType->alignment); - char *subName = NULL; + fprintf(stream, "%s: typed pointer, size is %zu, alignment is %i, points to ->\n", + name, type->ffiType->size, type->ffiType->alignment); + char* subName = NULL; char buf[128]; memset(buf,0,128); if (name != NULL) { @@ -1081,10 +1077,10 @@ static void dynType_printTypedPointer(char *name, dyn_type *type, int depth, FIL dynType_printAny(subName, type->typedPointer.typedType, depth + 1, stream); } -static void dynType_printTypes(dyn_type *type, FILE *stream) { +static void dynType_printTypes(const dyn_type* type, FILE* stream) { - dyn_type *parent = type->parent; - struct type_entry *pentry = NULL; + dyn_type* parent = type->parent; + struct type_entry* pentry = NULL; while (parent != NULL) { TAILQ_FOREACH(pentry, &parent->nestedTypesHead, entries) { if (pentry->type == type) { @@ -1094,9 +1090,9 @@ static void dynType_printTypes(dyn_type *type, FILE *stream) { parent = parent->parent; } - struct type_entry *entry = NULL; + struct type_entry* entry = NULL; TAILQ_FOREACH(entry, &type->nestedTypesHead, entries) { - dyn_type *toPrint = entry->type; + dyn_type* toPrint = entry->type; if (toPrint->type == DYN_TYPE_REF) { toPrint = toPrint->ref.ref; } @@ -1115,7 +1111,7 @@ static void dynType_printTypes(dyn_type *type, FILE *stream) { } - struct complex_type_entry *centry = NULL; + struct complex_type_entry* centry = NULL; switch(type->type) { case DYN_TYPE_COMPLEX : TAILQ_FOREACH(centry, &type->complex.entriesHead, entries) { diff --git a/libs/dfi/src/dyn_type_common.h b/libs/dfi/src/dyn_type_common.h index 45e8098ab..8638b63a7 100644 --- a/libs/dfi/src/dyn_type_common.h +++ b/libs/dfi/src/dyn_type_common.h @@ -31,36 +31,36 @@ extern "C" { #endif struct _dyn_type { - char *name; + char* name; char descriptor; int type; - ffi_type *ffiType; - dyn_type *parent; - struct types_head *referenceTypes; //NOTE: not owned + ffi_type* ffiType; + dyn_type* parent; + const struct types_head* referenceTypes; //NOTE: not owned struct types_head nestedTypesHead; struct meta_properties_head metaProperties; union { struct { struct complex_type_entries_head entriesHead; ffi_type structType; //dyn_type.ffiType points to this - dyn_type **types; //based on entriesHead for fast access + dyn_type** types; //based on entriesHead for fast access } complex; struct { ffi_type seqType; //dyn_type.ffiType points to this - dyn_type *itemType; + dyn_type* itemType; } sequence; struct { - dyn_type *typedType; + dyn_type* typedType; } typedPointer; struct { - dyn_type *ref; + dyn_type* ref; } ref; }; }; -dyn_type * dynType_findType(dyn_type *type, char *name); -ffi_type * dynType_ffiType(dyn_type * type); -void dynType_prepCif(ffi_type *type); +dyn_type * dynType_findType(dyn_type* type, char* name); +ffi_type * dynType_ffiType(dyn_type* type); +void dynType_prepCif(ffi_type* type); #ifdef __cplusplus } diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index ef90f5275..541cd9c3c 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -44,7 +44,7 @@ struct generic_service_layout { int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, char **out) { int status = OK; - dyn_type* returnType = NULL; + const dyn_type* returnType = NULL; json_error_t error; json_t *js_request = json_loads(request, 0, &error); @@ -108,7 +108,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c //setup and deserialize input for (i = 0; i < nrOfArgs; ++i) { - dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); + const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { value = json_array_get(arguments, index++); @@ -118,8 +118,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c } else if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { void **instPtr = calloc(1, sizeof(void*)); void *inst = NULL; - dyn_type *subType = NULL; - dynType_typedPointer_getTypedType(argType, &subType); + const dyn_type *subType = dynType_typedPointer_getTypedType(argType); dynType_alloc(subType, &inst); *instPtr = inst; args[i] = instPtr; @@ -154,7 +153,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c //free input args json_t *jsonResult = NULL; for(i = 0; i < nrOfArgs; ++i) { - dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); + const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { if (dynType_descriptorType(argType) == 't') { @@ -174,30 +173,29 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, c //serialize and free output for (i = 0; i < nrOfArgs; i += 1) { - dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); + const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { if (funcCallStatus == 0 && status == OK) { status = jsonSerializer_serializeJson(argType, args[i], &jsonResult); } - dyn_type *subType = NULL; - dynType_typedPointer_getTypedType(argType, &subType); + const dyn_type* subType = dynType_typedPointer_getTypedType(argType); void **ptrToInst = (void**)args[i]; dynType_free(subType, *ptrToInst); free(ptrToInst); } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { if (funcCallStatus == 0 && ptr != NULL) { - dyn_type *typedType = NULL; + const dyn_type *typedType = NULL; if (status == OK) { - status = dynType_typedPointer_getTypedType(argType, &typedType); + typedType = dynType_typedPointer_getTypedType(argType); } if (status == OK && dynType_descriptorType(typedType) == 't') { status = jsonSerializer_serializeJson(typedType, (void*) &ptr, &jsonResult); free(ptr); } else { - dyn_type *typedTypedType = NULL; + const dyn_type *typedTypedType = NULL; if (status == OK) { - status = dynType_typedPointer_getTypedType(typedType, &typedTypedType); + typedTypedType = dynType_typedPointer_getTypedType(typedType); } if(status == OK){ @@ -255,7 +253,7 @@ int jsonRpc_prepareInvokeRequest(dyn_function_type *func, const char *id, void * int i; int nrOfArgs = dynFunction_nrOfArguments(func); for (i = 0; i < nrOfArgs; i +=1) { - dyn_type *type = dynFunction_argumentTypeForIndex(func, i); + const dyn_type *type = dynFunction_argumentTypeForIndex(func, i); enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { json_t *val = NULL; @@ -345,7 +343,7 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] if (status == OK && !replyHasError) { int i; for (i = 0; i < nrOfArgs; i += 1) { - dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); + const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { void *tmp = NULL; @@ -359,7 +357,7 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] memcpy(*out, *(void**) tmp, size); } } else { - dynType_typedPointer_getTypedType(argType, &argType); + argType = dynType_typedPointer_getTypedType(argType); status = jsonSerializer_deserializeJson(argType, result, &tmp); if (tmp != NULL) { size = dynType_size(argType); @@ -369,9 +367,7 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] dynType_free(argType, tmp); } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - dyn_type *subType = NULL; - - dynType_typedPointer_getTypedType(argType, &subType); + const dyn_type *subType = dynType_typedPointer_getTypedType(argType); if (dynType_descriptorType(subType) == 't') { char ***out = (char ***) args[i]; @@ -381,8 +377,7 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] free(ptrToString); **out = (void*)s; } else { - dyn_type *subSubType = NULL; - dynType_typedPointer_getTypedType(subType, &subSubType); + const dyn_type *subSubType = dynType_typedPointer_getTypedType(subType); void ***out = (void ***) args[i]; status = jsonSerializer_deserializeJson(subSubType, result, *out); } diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index dbbc5b742..34d4f577b 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -28,17 +28,17 @@ #include #include -static int jsonSerializer_createType(dyn_type *type, json_t *object, void **result); -static int jsonSerializer_parseObject(dyn_type *type, json_t *object, void *inst); -static int jsonSerializer_parseObjectMember(dyn_type *type, const char *name, json_t *val, void *inst); -static int jsonSerializer_parseSequence(dyn_type *seq, json_t *array, void *seqLoc); -static int jsonSerializer_parseAny(dyn_type *type, void *input, json_t *val); -static int jsonSerializer_parseEnum(dyn_type *type, const char* enum_name, int32_t *out); +static int jsonSerializer_createType(const dyn_type *type, json_t *object, void **result); +static int jsonSerializer_parseObject(const dyn_type *type, json_t *object, void *inst); +static int jsonSerializer_parseObjectMember(const dyn_type *type, const char *name, json_t *val, void *inst); +static int jsonSerializer_parseSequence(const dyn_type *seq, json_t *array, void *seqLoc); +static int jsonSerializer_parseAny(const dyn_type *type, void *input, json_t *val); +static int jsonSerializer_parseEnum(const dyn_type *type, const char* enum_name, int32_t *out); -static int jsonSerializer_writeAny(dyn_type *type, void *input, json_t **val); -static int jsonSerializer_writeComplex(dyn_type *type, void *input, json_t **val); -static int jsonSerializer_writeSequence(dyn_type *type, void *input, json_t **out); -static int jsonSerializer_writeEnum(dyn_type *type, int32_t enum_value, json_t **out); +static int jsonSerializer_writeAny(const dyn_type *type, void *input, json_t **val); +static int jsonSerializer_writeComplex(const dyn_type *type, void *input, json_t **val); +static int jsonSerializer_writeSequence(const dyn_type *type, void *input, json_t **out); +static int jsonSerializer_writeEnum(const dyn_type *type, int32_t enum_value, json_t **out); static int OK = 0; @@ -65,11 +65,11 @@ int jsonSerializer_deserialize(dyn_type *type, const char *input, size_t length, return status; } -int jsonSerializer_deserializeJson(dyn_type *type, json_t *input, void **out) { +int jsonSerializer_deserializeJson(const dyn_type *type, json_t *input, void **out) { return jsonSerializer_createType(type, input, out); } -static int jsonSerializer_createType(dyn_type *type, json_t *val, void **result) { +static int jsonSerializer_createType(const dyn_type *type, json_t *val, void **result) { assert(val != NULL); int status = OK; void *inst = NULL; @@ -104,7 +104,7 @@ static int jsonSerializer_createType(dyn_type *type, json_t *val, void **result) return status; } -static int jsonSerializer_parseObject(dyn_type *type, json_t *object, void *inst) { +static int jsonSerializer_parseObject(const dyn_type *type, json_t *object, void *inst) { assert(object != NULL); int status = 0; json_t *value; @@ -120,35 +120,25 @@ static int jsonSerializer_parseObject(dyn_type *type, json_t *object, void *inst return status; } -static int jsonSerializer_parseObjectMember(dyn_type *type, const char *name, json_t *val, void *inst) { - int status = OK; +static int jsonSerializer_parseObjectMember(const dyn_type *type, const char *name, json_t *val, void *inst) { void *valp = NULL; - dyn_type *valType = NULL; + const dyn_type* valType = NULL; int index = dynType_complex_indexForName(type, name); if (index < 0) { return OK;//We should ignore unknown name in request or response. Satisfy forward compatibility for responses. } - if (status == OK) { - status = dynType_complex_valLocAt(type, index, inst, &valp); - } - - if (status == OK ) { - status = dynType_complex_dynTypeAt(type, index, &valType); - } + valp = dynType_complex_valLocAt(type, index, inst); + valType = dynType_complex_dynTypeAt(type, index); - if (status == OK) { - status = jsonSerializer_parseAny(valType, valp, val); - } - - return status; + return jsonSerializer_parseAny(valType, valp, val); } -static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { +static int jsonSerializer_parseAny(const dyn_type *type, void *loc, json_t *val) { int status = OK; - dyn_type *subType = NULL; + const dyn_type *subType = NULL; char c = dynType_descriptorType(type); /* @@ -255,10 +245,8 @@ static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { } break; case '*' : - status = dynType_typedPointer_getTypedType(type, &subType); - if (status == OK) { - status = jsonSerializer_createType(subType, val, (void **) loc); - } + subType = dynType_typedPointer_getTypedType(type); + status = jsonSerializer_createType(subType, val, (void **) loc); break; case 'P' : status = ERROR; @@ -276,7 +264,7 @@ static int jsonSerializer_parseAny(dyn_type *type, void *loc, json_t *val) { return status; } -static int jsonSerializer_parseSequence(dyn_type *seq, json_t *array, void *seqLoc) { +static int jsonSerializer_parseSequence(const dyn_type *seq, json_t *array, void *seqLoc) { assert(dynType_type(seq) == DYN_TYPE_SEQUENCE); int status = OK; @@ -284,7 +272,7 @@ static int jsonSerializer_parseSequence(dyn_type *seq, json_t *array, void *seqL status = dynType_sequence_alloc(seq, seqLoc, (int) size); if (status == OK) { - dyn_type *itemType = dynType_sequence_itemType(seq); + const dyn_type *itemType = dynType_sequence_itemType(seq); size_t index; json_t *val; json_array_foreach(array, index, val) { @@ -316,7 +304,7 @@ int jsonSerializer_serialize(dyn_type *type, const void* input, char **output) { return status; } -static int jsonSerializer_parseEnum(dyn_type *type, const char* enum_name, int32_t *out) { +static int jsonSerializer_parseEnum(const dyn_type *type, const char* enum_name, int32_t *out) { struct meta_entry * entry; TAILQ_FOREACH(entry, &type->metaProperties, entries) { @@ -330,16 +318,16 @@ static int jsonSerializer_parseEnum(dyn_type *type, const char* enum_name, int32 return ERROR; } -int jsonSerializer_serializeJson(dyn_type *type, const void* input, json_t **out) { +int jsonSerializer_serializeJson(const dyn_type *type, const void* input, json_t **out) { return jsonSerializer_writeAny(type, (void*)input /*TODO update static function to take const void**/, out); } -static int jsonSerializer_writeAny(dyn_type *type, void* input, json_t **out) { +static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **out) { int status = OK; int descriptor = dynType_descriptorType(type); json_t *val = NULL; - dyn_type *subType = NULL; + const dyn_type *subType = NULL; bool *z; //Z float *f; //F @@ -412,10 +400,8 @@ static int jsonSerializer_writeAny(dyn_type *type, void* input, json_t **out) { status = jsonSerializer_writeEnum(type, *e, &val); break; case '*' : - status = dynType_typedPointer_getTypedType(type, &subType); - if (status == OK) { - status = jsonSerializer_writeAny(subType, *(void **)input, &val); - } + subType = dynType_typedPointer_getTypedType(type); + status = jsonSerializer_writeAny(subType, *(void **)input, &val); break; case '{' : status = jsonSerializer_writeComplex(type, input, &val); @@ -443,12 +429,12 @@ static int jsonSerializer_writeAny(dyn_type *type, void* input, json_t **out) { return status; } -static int jsonSerializer_writeSequence(dyn_type *type, void *input, json_t **out) { +static int jsonSerializer_writeSequence(const dyn_type *type, void *input, json_t **out) { assert(dynType_type(type) == DYN_TYPE_SEQUENCE); int status = OK; json_t *array = json_array(); - dyn_type *itemType = dynType_sequence_itemType(type); + const dyn_type *itemType = dynType_sequence_itemType(type); uint32_t len = dynType_sequence_length(input); int i = 0; @@ -480,43 +466,40 @@ static int jsonSerializer_writeSequence(dyn_type *type, void *input, json_t **ou return status; } -static int jsonSerializer_writeComplex(dyn_type *type, void *input, json_t **out) { +static int jsonSerializer_writeComplex(const dyn_type *type, void *input, json_t **out) { assert(dynType_type(type) == DYN_TYPE_COMPLEX); int status = OK; json_t *val = json_object(); struct complex_type_entry *entry = NULL; - struct complex_type_entries_head *entries = NULL; + const struct complex_type_entries_head *entries = dynType_complex_entries(type); int index = -1; - status = dynType_complex_entries(type, &entries); - if (status == OK) { - TAILQ_FOREACH(entry, entries, entries) { - void *subLoc = NULL; - json_t *subVal = NULL; - dyn_type *subType = NULL; - index = dynType_complex_indexForName(type, entry->name); - if (index < 0) { - celix_err_pushf("Cannot find index for member '%s'", entry->name); - status = ERROR; - } - if(status == OK){ - status = dynType_complex_valLocAt(type, index, input, &subLoc); - } - if (status == OK) { - status = dynType_complex_dynTypeAt(type, index, &subType); - } - if (status == OK) { - status = jsonSerializer_writeAny(subType, subLoc, &subVal); - } - if (status == OK) { - json_object_set(val, entry->name, subVal); - json_decref(subVal); - } + TAILQ_FOREACH(entry, entries, entries) { + void *subLoc = NULL; + json_t *subVal = NULL; + const dyn_type* subType = NULL; + index = dynType_complex_indexForName(type, entry->name); + if (index < 0) { + celix_err_pushf("Cannot find index for member '%s'", entry->name); + status = ERROR; + } + if(status == OK){ + subLoc = dynType_complex_valLocAt(type, index, input); + } + if (status == OK) { + subType = dynType_complex_dynTypeAt(type, index); + } + if (status == OK) { + status = jsonSerializer_writeAny(subType, subLoc, &subVal); + } + if (status == OK) { + json_object_set(val, entry->name, subVal); + json_decref(subVal); + } - if (status != OK) { - break; - } + if (status != OK) { + break; } } @@ -530,7 +513,7 @@ static int jsonSerializer_writeComplex(dyn_type *type, void *input, json_t **out return status; } -static int jsonSerializer_writeEnum(dyn_type *type, int32_t enum_value, json_t **out) { +static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t **out) { struct meta_entry * entry; // Convert to string From 04692bce585d40a9f55fa708b74af2447c0f5534 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 26 Dec 2023 18:17:59 +0800 Subject: [PATCH 12/62] Add dynType_parseOfName to avoid unnecessary string duplication during parsing. --- libs/dfi/gtest/CMakeLists.txt | 2 + libs/dfi/gtest/src/dyn_type_ei_tests.cc | 70 +++++++++++++++ libs/dfi/include/dyn_type.h | 6 ++ libs/dfi/src/dyn_interface.c | 2 +- libs/dfi/src/dyn_message.c | 9 +- libs/dfi/src/dyn_type.c | 87 ++++++++++--------- libs/error_injector/stdio/CMakeLists.txt | 1 + libs/error_injector/stdio/include/stdio_ei.h | 2 + libs/error_injector/stdio/src/stdio_ei.cc | 53 ++++++----- libs/error_injector/string/CMakeLists.txt | 1 + .../error_injector/string/include/string_ei.h | 2 + libs/error_injector/string/src/string_ei.cc | 14 ++- 12 files changed, 177 insertions(+), 72 deletions(-) create mode 100644 libs/dfi/gtest/src/dyn_type_ei_tests.cc diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 9d3def82c..187cc4404 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -42,11 +42,13 @@ if (EI_TESTS) add_executable(test_dfi_with_ei src/dyn_interface_ei_tests.cc src/dyn_common_ei_tests.cc + src/dyn_type_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut Celix::malloc_ei Celix::stdio_ei + Celix::string_ei GTest::gtest GTest::gtest_main ) add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc new file mode 100644 index 000000000..7a2deb622 --- /dev/null +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_type.h" +#include "celix_err.h" +#include "malloc_ei.h" +#include "stdio_ei.h" +#include "string_ei.h" + +#include +#include +#include + +class DynTypeErrorInjectionTestSuite : public ::testing::Test { +public: + DynTypeErrorInjectionTestSuite() { + } + + ~DynTypeErrorInjectionTestSuite() override { + celix_ei_expect_strdup(nullptr, 0, nullptr); + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_ei_expect_fmemopen(nullptr, 0, nullptr); + } + // delete other constructors and assign operators + DynTypeErrorInjectionTestSuite(DynTypeErrorInjectionTestSuite const&) = delete; + DynTypeErrorInjectionTestSuite(DynTypeErrorInjectionTestSuite&&) = delete; + DynTypeErrorInjectionTestSuite& operator=(DynTypeErrorInjectionTestSuite const&) = delete; + DynTypeErrorInjectionTestSuite& operator=(DynTypeErrorInjectionTestSuite&&) = delete; +}; + +TEST_F(DynTypeErrorInjectionTestSuite, ParseTypeErrors) { + dyn_type *type = NULL; + const char* descriptor = "{D{DD b_1 b_2}I a b c}"; + + // fail to open memory as stream + celix_ei_expect_fmemopen((void*)dynType_parseWithStr, 0, nullptr); + int status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + std::string msg = "Error creating mem stream for descriptor string. "; + msg += strerror(ENOMEM); + ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); + + // fail to allocate dyn_type + celix_ei_expect_calloc((void*)dynType_parseWithStr, 2, nullptr); + status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for type", celix_err_popLastError()); + + // fail to duplicate type name + celix_ei_expect_strdup((void*)dynType_parseWithStr, 1, nullptr); + status = dynType_parseWithStr(descriptor, "hello", NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error strdup'ing name 'hello'", celix_err_popLastError()); +} diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 1ab2da416..635004b27 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -145,6 +145,12 @@ struct meta_entry { */ CELIX_DFI_EXPORT int dynType_parse(FILE* descriptorStream, const char* name, const struct types_head* refTypes, dyn_type** type); +/** + * @brief Parses a descriptor stream and creates a dyn_type (dynamic type) of a given name. + * Similar to dynType_parse except that the ownership of the given name is taken by the dyn type. + */ +CELIX_DFI_EXPORT int dynType_parseOfName(FILE* descriptorStream, char* name, const struct types_head* refTypes, dyn_type** type); + /** * Parses a descriptor string and creates a dyn_type (dynamic type). * If successful the type output argument points to the newly created dyn type. diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 6ac50a8a7..4d105f649 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -177,7 +177,7 @@ static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream) { } celix_autoptr(dyn_type) type = NULL; - if ((status = dynType_parse(stream, name, &intf->types, &type)) != OK) { + if ((status = dynType_parseOfName(stream, celix_steal_ptr(name), &intf->types, &type)) != OK) { return status; } if ((status = dynCommon_eatChar(stream, '\n')) != OK) { diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c index 886428bdc..a8b49bd39 100644 --- a/libs/dfi/src/dyn_message.c +++ b/libs/dfi/src/dyn_message.c @@ -19,6 +19,7 @@ #include "dyn_message.h" #include "celix_err.h" +#include "celix_stdlib_cleanup.h" #include #include @@ -179,7 +180,7 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { while (peek != ':' && peek != EOF) { ungetc(peek, stream); - char *name = NULL; + celix_autofree char* name = NULL; status = dynCommon_parseName(stream, &name); if (status == OK) { @@ -188,7 +189,7 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { dyn_type *type = NULL; if (status == OK) { - status = dynType_parse(stream, name, &msg->types, &type); + status = dynType_parseOfName(stream, celix_steal_ptr(name), &msg->types, &type); } if (status == OK) { @@ -207,10 +208,6 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { } } - if (name != NULL) { - free(name); - } - if (status != OK) { if (type != NULL) { dynType_destroy(type); diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index c533b8f39..9b995423e 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -19,9 +19,10 @@ #include "dyn_type.h" #include "dyn_type_common.h" -#include "dyn_type_common.h" #include "dyn_common.h" #include "celix_err.h" +#include "celix_stdio_cleanup.h" +#include "celix_stdlib_cleanup.h" #include #include @@ -35,6 +36,7 @@ static const int MEM_ERROR = 2; static const int PARSE_ERROR = 3; static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); +static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); static void dynType_clear(dyn_type* type); static void dynType_clearComplex(dyn_type* type); static void dynType_clearSequence(dyn_type* type); @@ -84,56 +86,59 @@ int dynType_parse(FILE* descriptorStream, const char* name, const struct types_h return dynType_parseWithStream(descriptorStream, name, NULL, refTypes, type); } + +int dynType_parseOfName(FILE* descriptorStream, char* name, const struct types_head* refTypes, dyn_type** type) { + return dynType_parseWithStreamOfName(descriptorStream, name, NULL, refTypes, type); +} + int dynType_parseWithStr(const char* descriptor, const char* name, const struct types_head* refTypes, dyn_type** type) { int status = OK; - FILE* stream = fmemopen((char *)descriptor, strlen(descriptor) + 1, "r"); - if (stream != NULL) { - status = dynType_parseWithStream(stream, name, NULL, refTypes, type); - if (status == OK) { - int c = fgetc(stream); - if (c != '\0' && c != EOF) { - status = PARSE_ERROR; - dynType_destroy(*type); - *type = NULL; - celix_err_pushf("Expected EOF got %c", c); - } - } - fclose(stream); - } else { - status = ERROR; + celix_autoptr(FILE) stream = fmemopen((char *)descriptor, strlen(descriptor), "r"); + if (stream == NULL) { celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); + return ERROR; + } + celix_autoptr(dyn_type) result = NULL; + if ((status = dynType_parseWithStream(stream, name, NULL, refTypes, &result)) != OK) { + return status; } + if (dynCommon_eatChar(stream, EOF) != 0) { + return PARSE_ERROR; + } + *type = celix_steal_ptr(result); return status; } static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { - int status = OK; - dyn_type* type = calloc(1, sizeof(*type)); - if (type != NULL) { - type->parent = parent; - type->type = DYN_TYPE_INVALID; - type->referenceTypes = refTypes; - TAILQ_INIT(&type->nestedTypesHead); - TAILQ_INIT(&type->metaProperties); - if (name != NULL) { - type->name = strdup(name); - if (type->name == NULL) { - status = MEM_ERROR; - celix_err_pushf("Error strdup'ing name '%s'\n", name); - } - } - if (status == OK) { - status = dynType_parseAny(stream, type); - } - if (status == OK) { - *result = type; - } else { - dynType_destroy(type); + char* typeName = NULL; + if (name != NULL) { + typeName = strdup(name); + if (typeName == NULL) { + celix_err_pushf("Error strdup'ing name '%s'", name); + return MEM_ERROR; } - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for type"); } + return dynType_parseWithStreamOfName(stream, typeName, parent, refTypes, result); +} + +static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { + int status = OK; + celix_autofree char* typeName = name; + celix_autoptr(dyn_type) type = calloc(1, sizeof(*type)); + if (type == NULL) { + celix_err_push("Error allocating memory for type"); + return MEM_ERROR; + } + type->parent = parent; + type->type = DYN_TYPE_INVALID; + type->referenceTypes = refTypes; + TAILQ_INIT(&type->nestedTypesHead); + TAILQ_INIT(&type->metaProperties); + type->name = celix_steal_ptr(typeName); + if ((status = dynType_parseAny(stream, type)) != OK) { + return status; + } + *result = celix_steal_ptr(type); return status; } diff --git a/libs/error_injector/stdio/CMakeLists.txt b/libs/error_injector/stdio/CMakeLists.txt index 667969093..2e53d8099 100644 --- a/libs/error_injector/stdio/CMakeLists.txt +++ b/libs/error_injector/stdio/CMakeLists.txt @@ -32,5 +32,6 @@ target_link_options(stdio_ei INTERFACE LINKER:--wrap,fputs LINKER:--wrap,fclose LINKER:--wrap,fgetc + LINKER:--wrap,fmemopen ) add_library(Celix::stdio_ei ALIAS stdio_ei) diff --git a/libs/error_injector/stdio/include/stdio_ei.h b/libs/error_injector/stdio/include/stdio_ei.h index 4ea80e0cc..f8ca53b49 100644 --- a/libs/error_injector/stdio/include/stdio_ei.h +++ b/libs/error_injector/stdio/include/stdio_ei.h @@ -48,6 +48,8 @@ CELIX_EI_DECLARE(fclose, int); CELIX_EI_DECLARE(fgetc, int); +CELIX_EI_DECLARE(fmemopen, FILE *); + #ifdef __cplusplus } #endif diff --git a/libs/error_injector/stdio/src/stdio_ei.cc b/libs/error_injector/stdio/src/stdio_ei.cc index 4a0c5d936..fca838384 100644 --- a/libs/error_injector/stdio/src/stdio_ei.cc +++ b/libs/error_injector/stdio/src/stdio_ei.cc @@ -21,54 +21,54 @@ #include "stdio_ei.h" extern "C" { -FILE* __real_fopen(const char* __filename, const char* __modes); +FILE *__real_fopen(const char *__filename, const char *__modes); CELIX_EI_DEFINE(fopen, FILE*) -FILE* __wrap_fopen(const char* __filename, const char* __modes) { +FILE *__wrap_fopen(const char *__filename, const char *__modes) { errno = EMFILE; CELIX_EI_IMPL(fopen); errno = 0; return __real_fopen(__filename, __modes); } -size_t __real_fwrite(const void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s); +size_t __real_fwrite(const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s); CELIX_EI_DEFINE(fwrite, size_t) -size_t __wrap_fwrite(const void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s) { +size_t __wrap_fwrite(const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s) { errno = ENOSPC; CELIX_EI_IMPL(fwrite); errno = 0; return __real_fwrite(__ptr, __size, __n, __s); } -int __real_remove(const char* __filename); +int __real_remove(const char *__filename); CELIX_EI_DEFINE(remove, int) -int __wrap_remove(const char* __filename) { +int __wrap_remove(const char *__filename) { errno = EACCES; CELIX_EI_IMPL(remove); errno = 0; return __real_remove(__filename); } -FILE* __real_open_memstream(char** __bufloc, size_t* __sizeloc); +FILE *__real_open_memstream(char **__bufloc, size_t *__sizeloc); CELIX_EI_DEFINE(open_memstream, FILE*) -FILE* __wrap_open_memstream(char** __bufloc, size_t* __sizeloc) { +FILE *__wrap_open_memstream(char **__bufloc, size_t *__sizeloc) { errno = ENOMEM; CELIX_EI_IMPL(open_memstream); errno = 0; return __real_open_memstream(__bufloc, __sizeloc); } -int __real_fseek(FILE* __stream, long int __off, int __whence); +int __real_fseek(FILE *__stream, long int __off, int __whence); CELIX_EI_DEFINE(fseek, int) -int __wrap_fseek(FILE* __stream, long int __off, int __whence) { +int __wrap_fseek(FILE *__stream, long int __off, int __whence) { errno = EACCES; CELIX_EI_IMPL(fseek); errno = 0; return __real_fseek(__stream, __off, __whence); } -long __real_ftell(FILE* __stream); +long __real_ftell(FILE *__stream); CELIX_EI_DEFINE(ftell, long) -long __wrap_ftell(FILE* __stream) { +long __wrap_ftell(FILE *__stream) { if (ftell_ret == -1) { errno = EACCES; } @@ -77,30 +77,30 @@ long __wrap_ftell(FILE* __stream) { return __real_ftell(__stream); } -size_t __real_fread(void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s); +size_t __real_fread(void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s); CELIX_EI_DEFINE(fread, size_t) -size_t __wrap_fread(void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s) { +size_t __wrap_fread(void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s) { CELIX_EI_IMPL(fread); return __real_fread(__ptr, __size, __n, __s); } -int __real_fputc(int __c, FILE* __stream); +int __real_fputc(int __c, FILE *__stream); CELIX_EI_DEFINE(fputc, int) -int __wrap_fputc(int __c, FILE* __stream) { +int __wrap_fputc(int __c, FILE *__stream) { CELIX_EI_IMPL(fputc); return __real_fputc(__c, __stream); } -int __real_fputs(const char* __s, FILE* __stream); +int __real_fputs(const char *__s, FILE *__stream); CELIX_EI_DEFINE(fputs, int) -int __wrap_fputs(const char* __s, FILE* __stream) { +int __wrap_fputs(const char *__s, FILE *__stream) { CELIX_EI_IMPL(fputs); return __real_fputs(__s, __stream); } -int __real_fclose(FILE* __stream); +int __real_fclose(FILE *__stream); CELIX_EI_DEFINE(fclose, int) -int __wrap_fclose(FILE* __stream) { +int __wrap_fclose(FILE *__stream) { int rc = __real_fclose(__stream); //note always call real fclose to ensure the stream is closed. errno = ENOSPC; CELIX_EI_IMPL(fclose); @@ -108,11 +108,20 @@ int __wrap_fclose(FILE* __stream) { return rc; } -int __real_fgetc(FILE* __stream); +int __real_fgetc(FILE *__stream); CELIX_EI_DEFINE(fgetc, int) -int __wrap_fgetc(FILE* __stream) { +int __wrap_fgetc(FILE *__stream) { CELIX_EI_IMPL(fgetc); return __real_fgetc(__stream); } +FILE* __real_fmemopen(void *__s, size_t __len, const char *__modes); +CELIX_EI_DEFINE(fmemopen, FILE*) +FILE* __wrap_fmemopen(void *__s, size_t __len, const char *__modes) { + errno = ENOMEM; + CELIX_EI_IMPL(fmemopen); + errno = 0; + return __real_fmemopen(__s, __len, __modes); } + +} \ No newline at end of file diff --git a/libs/error_injector/string/CMakeLists.txt b/libs/error_injector/string/CMakeLists.txt index ff53b52d3..190048570 100644 --- a/libs/error_injector/string/CMakeLists.txt +++ b/libs/error_injector/string/CMakeLists.txt @@ -22,5 +22,6 @@ target_link_libraries(string_ei PUBLIC Celix::error_injector) target_link_options(string_ei INTERFACE LINKER:--wrap,strndup + LINKER:--wrap,strdup ) add_library(Celix::string_ei ALIAS string_ei) diff --git a/libs/error_injector/string/include/string_ei.h b/libs/error_injector/string/include/string_ei.h index 85a2b3543..0ef7d9c83 100644 --- a/libs/error_injector/string/include/string_ei.h +++ b/libs/error_injector/string/include/string_ei.h @@ -29,6 +29,8 @@ extern "C" { CELIX_EI_DECLARE(strndup, char *); +CELIX_EI_DECLARE(strdup, char *); + #ifdef __cplusplus } #endif diff --git a/libs/error_injector/string/src/string_ei.cc b/libs/error_injector/string/src/string_ei.cc index 747478924..1b26f5ccd 100644 --- a/libs/error_injector/string/src/string_ei.cc +++ b/libs/error_injector/string/src/string_ei.cc @@ -22,12 +22,22 @@ #include extern "C" { -char* __real_strndup(const char* s, size_t n); +char *__real_strndup(const char *s, size_t n); CELIX_EI_DEFINE(strndup, char*) -char* __wrap_strndup(const char* s, size_t n) { +char *__wrap_strndup(const char *s, size_t n) { errno = ENOMEM; CELIX_EI_IMPL(strndup); errno = 0; return __real_strndup(s, n); } + +char *__real_strdup(const char *s); +CELIX_EI_DEFINE(strdup, char*) +char *__wrap_strdup(const char *s) { + errno = ENOMEM; + CELIX_EI_IMPL(strdup); + errno = 0; + return __real_strdup(s); +} + } \ No newline at end of file From 7936dca6ae540217fe072344faa0f0268dadd377 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 27 Dec 2023 17:51:46 +0800 Subject: [PATCH 13/62] Extract common functionality of dyn_interface and dyn_message into dyn_descriptor and improve ergonomics of dyn_message API. --- libs/dfi/CMakeLists.txt | 1 + libs/dfi/gtest/CMakeLists.txt | 1 + .../invalids/invalidExtraSection.descriptor | 15 + .../invalids/invalidMsgMissingName.descriptor | 9 + .../invalidMsgMissingNewline.descriptor | 10 + .../invalidMsgMissingVersion.descriptor | 3 +- .../invalids/invalidMsgType.descriptor | 10 + libs/dfi/gtest/src/dyn_interface_ei_tests.cc | 5 +- libs/dfi/gtest/src/dyn_interface_tests.cpp | 7 + libs/dfi/gtest/src/dyn_message_ei_tests.cc | 45 +++ libs/dfi/gtest/src/dyn_message_tests.cpp | 62 ++-- libs/dfi/include/dyn_message.h | 47 +-- libs/dfi/src/dyn_common.c | 45 ++- libs/dfi/src/dyn_common.h | 16 +- libs/dfi/src/dyn_descriptor.c | 178 +++++++++++ libs/dfi/src/dyn_descriptor.h | 51 +++ libs/dfi/src/dyn_interface.c | 209 ++----------- libs/dfi/src/dyn_interface_common.h | 6 +- libs/dfi/src/dyn_message.c | 293 ++++-------------- 19 files changed, 508 insertions(+), 505 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidExtraSection.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidMsgMissingName.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidMsgMissingNewline.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/invalidMsgType.descriptor create mode 100644 libs/dfi/gtest/src/dyn_message_ei_tests.cc create mode 100644 libs/dfi/src/dyn_descriptor.c create mode 100644 libs/dfi/src/dyn_descriptor.h diff --git a/libs/dfi/CMakeLists.txt b/libs/dfi/CMakeLists.txt index 2a9154956..f6787d987 100644 --- a/libs/dfi/CMakeLists.txt +++ b/libs/dfi/CMakeLists.txt @@ -29,6 +29,7 @@ if (CELIX_DFI) src/dyn_message.c src/json_serializer.c src/json_rpc.c + src/dyn_descriptor.c ) add_library(dfi SHARED ${SOURCES}) diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 187cc4404..7a1d7aaff 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -43,6 +43,7 @@ if (EI_TESTS) src/dyn_interface_ei_tests.cc src/dyn_common_ei_tests.cc src/dyn_type_ei_tests.cc + src/dyn_message_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut diff --git a/libs/dfi/gtest/descriptors/invalids/invalidExtraSection.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidExtraSection.descriptor new file mode 100644 index 000000000..bfd48d66e --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidExtraSection.descriptor @@ -0,0 +1,15 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(#am=handle;PDD#am=pre;*D)N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N +:invalidSection +invalidKey=invalidValue diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingName.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingName.descriptor new file mode 100644 index 000000000..9010cb841 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingName.descriptor @@ -0,0 +1,9 @@ +:header +type=message +version=1.0.0 +:annotations +classname=org.example.PointOfInterest +:types +location={DD lat long} +:message +{llocation;tt location name description} diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingNewline.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingNewline.descriptor new file mode 100644 index 000000000..4f2cf8788 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingNewline.descriptor @@ -0,0 +1,10 @@ +:header +type=message +name=poi +version=1.0.0 +:annotations +classname=org.example.PointOfInterest +:types +location={DD lat long} +:message +{llocation;tt location name description} \ No newline at end of file diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingVersion.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingVersion.descriptor index 1fd3595ac..ae9b13158 100644 --- a/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingVersion.descriptor +++ b/libs/dfi/gtest/descriptors/invalids/invalidMsgMissingVersion.descriptor @@ -1,7 +1,6 @@ -:head +:header type=message name=poi -version=1.0.0 :annotations classname=org.example.PointOfInterest :types diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMsgType.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMsgType.descriptor new file mode 100644 index 000000000..cf31bf579 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/invalidMsgType.descriptor @@ -0,0 +1,10 @@ +:header +type=message +name=poi +version=1.0.0 +:annotations +classname=org.example.PointOfInterest +:types +location={DD lat long} +:message +{llocation;ttX location name description} diff --git a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc index 4145d9e20..cfa1931bc 100644 --- a/libs/dfi/gtest/src/dyn_interface_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_interface_ei_tests.cc @@ -18,6 +18,7 @@ */ #include "dyn_interface.h" +#include "dyn_descriptor.h" #include "dyn_common.h" #include "celix_err.h" #include "celix_stdio_cleanup.h" @@ -61,14 +62,14 @@ TEST_F(DynInterfaceErrorInjectionTestSuite, ParseError) { rewind(desc); // not enough memory for type_entry when parsing types section - celix_ei_expect_calloc((void*) dynInterface_parse, 2, nullptr, 1); + celix_ei_expect_calloc((void*) celix_dynDescriptor_parse, 2, nullptr, 5); status = dynInterface_parse(desc, &dynIntf); ASSERT_NE(0, status); ASSERT_STREQ("Error allocating memory for type entry", celix_err_popLastError()); rewind(desc); // not enough memory for method_entry when parsing methods section - celix_ei_expect_calloc((void*) dynInterface_parse, 2, nullptr, 2); + celix_ei_expect_calloc((void*) celix_dynDescriptor_parse, 3, nullptr, 1); status = dynInterface_parse(desc, &dynIntf); ASSERT_NE(0, status); ASSERT_STREQ("Error allocating memory for method entry", celix_err_popLastError()); diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index 820ad83f9..a340eb3d1 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -227,6 +227,13 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + + /* invalid extra section */ + desc = fopen("descriptors/invalids/invalidExtraSection.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(1, status); + fclose(desc); desc=NULL; } } diff --git a/libs/dfi/gtest/src/dyn_message_ei_tests.cc b/libs/dfi/gtest/src/dyn_message_ei_tests.cc new file mode 100644 index 000000000..2d09f6581 --- /dev/null +++ b/libs/dfi/gtest/src/dyn_message_ei_tests.cc @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_message.h" +#include "celix_err.h" +#include "malloc_ei.h" + +#include + +class DynMessageErrorInjectionTestSuite : public ::testing::Test { +public: + DynMessageErrorInjectionTestSuite() = default; + ~DynMessageErrorInjectionTestSuite() override { + celix_ei_expect_calloc(nullptr, 0, nullptr); + } +}; + +TEST_F(DynMessageErrorInjectionTestSuite, ParseError) { + int status = 0; + dyn_message_type *dynMsg = NULL; + FILE *desc = fopen("descriptors/msg_example1.descriptor", "r"); + assert(desc != NULL); + //not enough memory for dyn_message_type + celix_ei_expect_calloc((void*) dynMessage_parse, 0, nullptr); + status = dynMessage_parse(desc, &dynMsg); + ASSERT_NE(0, status); + fclose(desc); + ASSERT_STREQ("Error allocating memory for dynamic message", celix_err_popLastError()); +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_message_tests.cpp b/libs/dfi/gtest/src/dyn_message_tests.cpp index 50109158b..533171554 100644 --- a/libs/dfi/gtest/src/dyn_message_tests.cpp +++ b/libs/dfi/gtest/src/dyn_message_tests.cpp @@ -34,20 +34,20 @@ extern "C" { #include "dyn_common.h" #include "dyn_message.h" +#include "celix_version.h" static void checkMessageVersion(dyn_message_type* dynMsg, const char* v){ int status = 0; - char *version = NULL; - status = dynMessage_getVersionString(dynMsg, &version); - ASSERT_EQ(0, status); + const char* version = dynMessage_getVersionString(dynMsg); ASSERT_STREQ(v, version); - version_pt msgVersion = NULL, localMsgVersion = NULL; + const celix_version_t* msgVersion = nullptr; + celix_version_t* localMsgVersion = nullptr; int cmpVersion = -1; version_createVersionFromString(version,&localMsgVersion); - status = dynMessage_getVersion(dynMsg,&msgVersion); + msgVersion = dynMessage_getVersion(dynMsg); ASSERT_EQ(0, status); - version_compareTo(msgVersion,localMsgVersion,&cmpVersion); + cmpVersion = celix_version_compareTo(msgVersion, localMsgVersion); ASSERT_EQ(cmpVersion,0); version_destroy(localMsgVersion); @@ -63,26 +63,23 @@ static void msg_test1(void) { ASSERT_EQ(0, status); fclose(desc); - char *name = NULL; - status = dynMessage_getName(dynMsg, &name); + const char* name = dynMessage_getName(dynMsg); ASSERT_EQ(0, status); ASSERT_STREQ("poi", name); checkMessageVersion(dynMsg,"1.0.0"); - char *annVal = NULL; + const char* annVal = NULL; status = dynMessage_getAnnotationEntry(dynMsg, "classname", &annVal); ASSERT_EQ(0, status); ASSERT_STREQ("org.example.PointOfInterest", annVal); - char *nonExist = NULL; + const char* nonExist = NULL; status = dynMessage_getHeaderEntry(dynMsg, "nonExisting", &nonExist); ASSERT_TRUE(status != 0); ASSERT_TRUE(nonExist == NULL); - dyn_type *msgType = NULL; - status = dynMessage_getMessageType(dynMsg, &msgType); - ASSERT_EQ(0, status); + const dyn_type* msgType = dynMessage_getMessageType(dynMsg); ASSERT_TRUE(msgType != NULL); dynMessage_destroy(dynMsg); @@ -98,26 +95,23 @@ static void msg_test2(void) { ASSERT_EQ(0, status); fclose(desc); - char *name = NULL; - status = dynMessage_getName(dynMsg, &name); + const char* name = dynMessage_getName(dynMsg); ASSERT_EQ(0, status); ASSERT_STREQ("track", name); checkMessageVersion(dynMsg,"0.0.1"); - char *annVal = NULL; + const char* annVal = NULL; status = dynMessage_getAnnotationEntry(dynMsg, "classname", &annVal); ASSERT_EQ(0, status); ASSERT_STREQ("org.example.Track", annVal); - char *nonExist = NULL; + const char* nonExist = NULL; status = dynMessage_getHeaderEntry(dynMsg, "nonExisting", &nonExist); ASSERT_TRUE(status != 0); ASSERT_TRUE(nonExist == NULL); - dyn_type *msgType = NULL; - status = dynMessage_getMessageType(dynMsg, &msgType); - ASSERT_EQ(0, status); + const dyn_type* msgType = dynMessage_getMessageType(dynMsg); ASSERT_TRUE(msgType != NULL); dynMessage_destroy(dynMsg); @@ -132,26 +126,23 @@ static void msg_test3(void) { ASSERT_EQ(0, status); fclose(desc); - char *name = NULL; - status = dynMessage_getName(dynMsg, &name); + const char* name = dynMessage_getName(dynMsg); ASSERT_EQ(0, status); ASSERT_STREQ("logEntry", name); checkMessageVersion(dynMsg,"1.0.0"); - char *annVal = NULL; + const char* annVal = NULL; status = dynMessage_getAnnotationEntry(dynMsg, "classname", &annVal); ASSERT_EQ(0, status); ASSERT_STREQ("org.example.LogEntry", annVal); - char *nonExist = NULL; + const char* nonExist = NULL; status = dynMessage_getHeaderEntry(dynMsg, "nonExisting", &nonExist); ASSERT_TRUE(status != 0); ASSERT_TRUE(nonExist == NULL); - dyn_type *msgType = NULL; - status = dynMessage_getMessageType(dynMsg, &msgType); - ASSERT_EQ(0, status); + const dyn_type* msgType = dynMessage_getMessageType(dynMsg); ASSERT_TRUE(msgType != NULL); dynMessage_destroy(dynMsg); @@ -206,6 +197,23 @@ static void msg_invalid(void) { ASSERT_EQ(1, status); fclose(desc); + desc = fopen("descriptors/invalids/invalidMsgMissingName.descriptor", "r"); + assert(desc != NULL); + status = dynMessage_parse(desc, &dynMsg); + ASSERT_NE(0, status); + fclose(desc); + + desc = fopen("descriptors/invalids/invalidMsgType.descriptor", "r"); + assert(desc != NULL); + status = dynMessage_parse(desc, &dynMsg); + ASSERT_NE(0, status); + fclose(desc); + + desc = fopen("descriptors/invalids/invalidMsgMissingNewline.descriptor", "r"); + assert(desc != NULL); + status = dynMessage_parse(desc, &dynMsg); + ASSERT_NE(0, status); + fclose(desc); } } diff --git a/libs/dfi/include/dyn_message.h b/libs/dfi/include/dyn_message.h index 33d5c2c22..3564ce674 100644 --- a/libs/dfi/include/dyn_message.h +++ b/libs/dfi/include/dyn_message.h @@ -21,6 +21,7 @@ #define __DYN_MESSAGE_H_ #include "dyn_type.h" +#include "celix_cleanup.h" #include "celix_version.h" #include "celix_dfi_export.h" @@ -53,50 +54,36 @@ typedef struct _dyn_message_type dyn_message_type; * @param[out] out The dynamic message type instance. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynMessage_parse(FILE *descriptor, dyn_message_type **out); +CELIX_DFI_EXPORT int dynMessage_parse(FILE* descriptor, dyn_message_type** out); /** * @brief Destroy the dynamic message type instance. * @param[in] msg The dynamic message type instance. */ -CELIX_DFI_EXPORT void dynMessage_destroy(dyn_message_type *msg); +CELIX_DFI_EXPORT void dynMessage_destroy(dyn_message_type* msg); + +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_message_type, dynMessage_destroy); /** * @brief Gets the name of the dynamic message type instance. - * * The dynamic message type instance is the owner of the name and the name should not be freed. - * - * In case of an error, an error message is added to celix_err. - * - * @param[in] msg The dynamic message type instance. - * @param[out] name The name of the dynamic message type instance. - * @return 0 if successful, 1 otherwise. + * @note It always returns valid string. */ -CELIX_DFI_EXPORT int dynMessage_getName(dyn_message_type *msg, char **name); +CELIX_DFI_EXPORT const char* dynMessage_getName(const dyn_message_type* msg); /** * @brief Gets the version of the given dynamic message type instance. - * * The dynamic message type instance is the owner of the version and the version should not be freed. - * - * @param[in] msg The dynamic message type instance. - * @param[out] version The version of the dynamic message type instance. - * @return 0 if successful, 1 otherwise. + * @note It always returns valid version. */ -CELIX_DFI_EXPORT int dynMessage_getVersion(dyn_message_type *msg, celix_version_t** version); +CELIX_DFI_EXPORT const celix_version_t* dynMessage_getVersion(const dyn_message_type* msg); /** * @brief Gets the version string of the given dynamic message type instance. - * * The dynamic message type instance is the owner of the version string and the version string should not be freed. - * - * In case of an error, an error message is added to celix_err. - * - * @param[in] msg The dynamic message type instance. - * @param[out] version The version string of the dynamic message type instance. - * @return 0 if successful, 1 otherwise. + * @note It always returns valid string. */ -CELIX_DFI_EXPORT int dynMessage_getVersionString(dyn_message_type *msg, char **version); +CELIX_DFI_EXPORT const char* dynMessage_getVersionString(const dyn_message_type* msg); /** * @brief Gets the value corresponding to the specified name, which comes from the header section of the given dynamic message type instance. @@ -110,7 +97,7 @@ CELIX_DFI_EXPORT int dynMessage_getVersionString(dyn_message_type *msg, char **v * @param[out] value The value corresponding to the specified name. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynMessage_getHeaderEntry(dyn_message_type *msg, const char *name, char **value); +CELIX_DFI_EXPORT int dynMessage_getHeaderEntry(dyn_message_type* msg, const char* name, const char** value); /** * @brief Gets the value corresponding to the specified name, which comes from the annotation section of the given dynamic message type instance. @@ -124,18 +111,14 @@ CELIX_DFI_EXPORT int dynMessage_getHeaderEntry(dyn_message_type *msg, const char * @param[out] value The value corresponding to the specified name. * @return 0 if successful, 1 otherwise. */ -CELIX_DFI_EXPORT int dynMessage_getAnnotationEntry(dyn_message_type *msg, const char *name, char **value); +CELIX_DFI_EXPORT int dynMessage_getAnnotationEntry(dyn_message_type* msg, const char* name, const char** value); /** * @brief Gets the message type from the given dynamic message type instance. - * * The dynamic message type instance is the owner of the message type and the message type should not be freed. - * - * @param[in] msg The dynamic message type instance. - * @param[out] type The message type. - * @return 0 + * @note It always returns valid dyn_type. */ -CELIX_DFI_EXPORT int dynMessage_getMessageType(dyn_message_type *msg, dyn_type **type); +CELIX_DFI_EXPORT const dyn_type* dynMessage_getMessageType(dyn_message_type* msg); #ifdef __cplusplus } diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 17783fd7b..6ea656e35 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -31,12 +31,12 @@ static const int OK = 0; static const int ERROR = 1; -int dynCommon_parseName(FILE *stream, char **result) { +int dynCommon_parseName(FILE* stream, char** result) { return dynCommon_parseNameAlsoAccept(stream, NULL, result); } -int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char **result) { - celix_autofree char *buf = NULL; +int dynCommon_parseNameAlsoAccept(FILE* stream, const char* acceptedChars, char** result) { + celix_autofree char* buf = NULL; size_t size = 0; celix_autoptr(FILE) name = open_memstream(&buf, &size); if (name == NULL) { @@ -70,10 +70,10 @@ int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char return OK; } -static int dynCommon_parseNameValue(FILE *stream, char **outName, char **outValue) { +static int dynCommon_parseNameValue(FILE* stream, char** outName, char** outValue) { int status; - celix_autofree char *name = NULL; - celix_autofree char *value = NULL; + celix_autofree char* name = NULL; + celix_autofree char* value = NULL; do { if ((status = dynCommon_parseName(stream, &name)) != OK) { break; @@ -92,7 +92,7 @@ static int dynCommon_parseNameValue(FILE *stream, char **outName, char **outValu return status; } -int dynCommon_eatChar(FILE *stream, int expected) { +int dynCommon_eatChar(FILE* stream, int expected) { int status = OK; int c = fgetc(stream); if (c != expected) { @@ -102,8 +102,8 @@ int dynCommon_eatChar(FILE *stream, int expected) { return status; } -void dynCommon_clearNamValHead(struct namvals_head *head) { - struct namval_entry *entry = TAILQ_FIRST(head); +void dynCommon_clearNamValHead(struct namvals_head* head) { + struct namval_entry* entry = TAILQ_FIRST(head); while (entry != NULL) { struct namval_entry *tmp = entry; @@ -118,15 +118,15 @@ void dynCommon_clearNamValHead(struct namvals_head *head) { } } -int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { +int dynCommon_parseNameValueSection(FILE* stream, struct namvals_head* head) { int status = OK; int peek = fgetc(stream); while (peek != ':' && peek != EOF) { ungetc(peek, stream); - celix_autofree char *name = NULL; - celix_autofree char *value = NULL; + celix_autofree char* name = NULL; + celix_autofree char* value = NULL; if ((status = dynCommon_parseNameValue(stream, &name, &value)) != OK) { return status; } @@ -135,7 +135,7 @@ int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { return status; } - struct namval_entry *entry = NULL; + struct namval_entry* entry = NULL; entry = calloc(1, sizeof(*entry)); if (entry == NULL) { celix_err_pushf("Error allocating memory for namval entry"); @@ -154,3 +154,22 @@ int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head) { return OK; } + +int dynCommon_getEntryForHead(const struct namvals_head* head, const char* name, const char** out) { + int status = OK; + char* value = NULL; + struct namval_entry* entry = NULL; + TAILQ_FOREACH(entry, head, entries) { + if (strcmp(name, entry->name) == 0) { + value = entry->value; + break; + } + } + if (value != NULL) { + *out = value; + } else { + status = ERROR; + celix_err_pushf("Cannot find '%s' in list", name); + } + return status; +} diff --git a/libs/dfi/src/dyn_common.h b/libs/dfi/src/dyn_common.h index ab74af5f9..66dc1c8f2 100644 --- a/libs/dfi/src/dyn_common.h +++ b/libs/dfi/src/dyn_common.h @@ -32,8 +32,8 @@ extern "C" { TAILQ_HEAD(namvals_head, namval_entry); struct namval_entry { - char *name; - char *value; + char* name; + char* value; TAILQ_ENTRY(namval_entry) entries; }; @@ -49,7 +49,7 @@ struct namval_entry { * @return 0 if successful, otherwise 1. * @alsoseee dynCommon_parseNameAlsoAccept */ -int dynCommon_parseName(FILE *stream, char **result); +int dynCommon_parseName(FILE* stream, char** result); /** * @brief Parse the name of dynamic type from the given stream. @@ -64,7 +64,7 @@ int dynCommon_parseName(FILE *stream, char **result); * @return 0 if successful, otherwise 1. * @alsoseee dynCommon_parseName */ -int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char **result); +int dynCommon_parseNameAlsoAccept(FILE* stream, const char* acceptedChars, char** result); /** * @brief Parses a section of name-value pairs from the given stream. The name is only allowed to contain [a-zA-Z0-9_]. @@ -81,7 +81,7 @@ int dynCommon_parseNameAlsoAccept(FILE *stream, const char *acceptedChars, char * @param[out] head The namvals_head structure where the parsed name-value pairs will be stored. * @return 0 if successful, otherwise 1. */ -int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head); +int dynCommon_parseNameValueSection(FILE* stream, struct namvals_head* head); /** * @brief Eat the given character from the given stream. @@ -92,14 +92,16 @@ int dynCommon_parseNameValueSection(FILE *stream, struct namvals_head *head); * @param[in] c The character to be eaten. * @return 0 if successful, otherwise 1. */ -int dynCommon_eatChar(FILE *stream, int c); +int dynCommon_eatChar(FILE* stream, int c); /** * @brief Clear the given name-value pairs. * * @param[in] head The name-value pairs to be cleared. */ -void dynCommon_clearNamValHead(struct namvals_head *head); +void dynCommon_clearNamValHead(struct namvals_head* head); + +int dynCommon_getEntryForHead(const struct namvals_head* head, const char* name, const char** out); #ifdef __cplusplus } diff --git a/libs/dfi/src/dyn_descriptor.c b/libs/dfi/src/dyn_descriptor.c new file mode 100644 index 000000000..be5ad8fa1 --- /dev/null +++ b/libs/dfi/src/dyn_descriptor.c @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_descriptor.h" +#include "celix_err.h" +#include "celix_stdlib_cleanup.h" + +#include + +static const int OK = 0; +static const int ERROR = 1; + +static int celix_dynDescriptor_parseTypes(celix_descriptor_t* descriptor, FILE *stream) { + int status; + + //expected input (Name)=\n + int peek = fgetc(stream); + while (peek != ':' && peek != EOF) { + ungetc(peek, stream); + + celix_autofree char* name = NULL; + if ((status = dynCommon_parseName(stream, &name)) != OK) { + return status; + } + + if ((status = dynCommon_eatChar(stream, '=')) != OK) { + return status; + } + + celix_autoptr(dyn_type) type = NULL; + if ((status = dynType_parseOfName(stream, celix_steal_ptr(name), &descriptor->types, &type)) != OK) { + return status; + } + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + return status; + } + + struct type_entry *entry = NULL; + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + celix_err_pushf("Error allocating memory for type entry"); + return ERROR; + } + entry->type = celix_steal_ptr(type); + TAILQ_INSERT_TAIL(&descriptor->types, entry, entries); + + peek = fgetc(stream); + } + if (peek != EOF) { + ungetc(peek, stream); + } + + return OK; +} + +static int celix_dynDescriptor_parseSection(celix_descriptor_t* descriptor, FILE *stream, + int (*parseSection)(celix_descriptor_t* descriptor, const char* secName, FILE *stream)) { + int status = OK; + celix_autofree char *sectionName = NULL; + + if ((status = dynCommon_eatChar(stream, ':')) != OK) { + return status; + } + + if ((status = dynCommon_parseName(stream, §ionName)) != OK) { + return status; + } + + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + return status; + } + + if (strcmp("header", sectionName) == 0) { + status = dynCommon_parseNameValueSection(stream, &descriptor->header); + } else if (strcmp("annotations", sectionName) == 0) { + status = dynCommon_parseNameValueSection(stream, &descriptor->annotations); + } else if (strcmp("types", sectionName) == 0) { + status = celix_dynDescriptor_parseTypes(descriptor, stream); + } else { + status = parseSection(descriptor, sectionName, stream); + } + + return status; +} + +static int celix_dynDescriptor_checkInterface(celix_descriptor_t* descriptor) { + //check header section + bool foundType = false; + bool foundVersion = false; + bool foundName = false; + struct namval_entry *entry = NULL; + TAILQ_FOREACH(entry, &descriptor->header, entries) { + if (strcmp(entry->name, "type") == 0) { + foundType = true; + } else if (strcmp(entry->name, "version") == 0) { + foundVersion = true; + } else if (strcmp(entry->name, "name") == 0) { + foundName = true; + } + } + + if (!foundType || !foundVersion || !foundName) { + celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); + return ERROR; + } + + return OK; +} + +int celix_dynDescriptor_parse(celix_descriptor_t* descriptor, FILE* stream, + int (*parseSection)(celix_descriptor_t* descriptor, const char* secName, FILE *stream)) { + int status = OK; + + char peek = (char)fgetc(stream); + while (peek == ':') { + ungetc(peek, stream); + if ((status = celix_dynDescriptor_parseSection(descriptor, stream, parseSection)) != OK) { + return status; + } + peek = (char)fgetc(stream); + } + + if (peek != EOF) { + celix_err_pushf("Descriptor does not start with ':'"); + return ERROR; + } + + if ((status = celix_dynDescriptor_checkInterface(descriptor)) != OK) { + return status; + } + + // We are sure that version field is present in the header + const char* version; + status = dynCommon_getEntryForHead(&descriptor->header, "version", &version); + assert(status == OK); + assert(version != NULL); + descriptor->version = celix_version_createVersionFromString(version); + if (descriptor->version == NULL) { + celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); + return ERROR; + } + return OK; +} + +void celix_dynDescriptor_destroy(celix_descriptor_t* descriptor) { + dynCommon_clearNamValHead(&descriptor->header); + dynCommon_clearNamValHead(&descriptor->annotations); + + struct type_entry *tInfo = TAILQ_FIRST(&descriptor->types); + while (tInfo != NULL) { + struct type_entry *tmp = tInfo; + tInfo = TAILQ_NEXT(tInfo, entries); + dynType_destroy(tmp->type); + free(tmp); + } + + if(descriptor->version!=NULL){ + celix_version_destroy(descriptor->version); + } + + free(descriptor); +} diff --git a/libs/dfi/src/dyn_descriptor.h b/libs/dfi/src/dyn_descriptor.h new file mode 100644 index 000000000..e7d085f76 --- /dev/null +++ b/libs/dfi/src/dyn_descriptor.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CELIX_DYN_DESCRIPTOR_H +#define CELIX_DYN_DESCRIPTOR_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "dyn_common.h" +#include "dyn_type.h" +#include "celix_version.h" + +#include + +#define CELIX_DESCRIPTOR_FIELDS \ + /* public */ \ + struct namvals_head header; \ + struct namvals_head annotations; \ + struct types_head types; \ + celix_version_t* version; \ + +typedef struct celix_descriptor { + CELIX_DESCRIPTOR_FIELDS +}celix_descriptor_t; + +int celix_dynDescriptor_parse(celix_descriptor_t* descriptor, FILE* stream, + int (*parseSection)(celix_descriptor_t* descriptor, const char* secName, FILE *stream)); + +void celix_dynDescriptor_destroy(celix_descriptor_t* descriptor); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_DYN_DESCRIPTOR_H diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 4d105f649..c3b8d7ae0 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -33,15 +33,11 @@ static const int OK = 0; static const int ERROR = 1; -static int dynInterface_checkInterface(dyn_interface_type *intf); -static int dynInterface_parseSection(dyn_interface_type *intf, FILE *stream); -static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream); -static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream); -static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream); -static int dynInterface_parseHeader(dyn_interface_type *intf, FILE *stream); -static int dynInterface_getEntryForHead(const struct namvals_head *head, const char *name, const char **value); - -int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { +static int dynInterface_checkInterface(dyn_interface_type* intf); +static int dynInterface_parseSection(dyn_interface_type* intf, const char* secName, FILE* stream); +static int dynInterface_parseMethods(dyn_interface_type* intf, FILE* stream); + +int dynInterface_parse(FILE* descriptor, dyn_interface_type** out) { int status = OK; celix_autoptr(dyn_interface_type) intf = calloc(1, sizeof(*intf)); @@ -55,58 +51,21 @@ int dynInterface_parse(FILE *descriptor, dyn_interface_type **out) { TAILQ_INIT(&intf->types); TAILQ_INIT(&intf->methods); - char peek = (char)fgetc(descriptor); - while (peek == ':') { - ungetc(peek, descriptor); - if ((status = dynInterface_parseSection(intf, descriptor)) != OK) { - return status; - } - peek = (char)fgetc(descriptor); - } - - if (peek != EOF) { - celix_err_pushf("Descriptor does not start with ':'"); - return ERROR; + if ((status = celix_dynDescriptor_parse((celix_descriptor_t*)intf, descriptor, + (int (*)(celix_descriptor_t*, const char*, FILE *))dynInterface_parseSection)) != OK) { + return status; } if ((status = dynInterface_checkInterface(intf)) != OK) { return status; } - // We are sure that version field is present in the header - const char* version = dynInterface_getVersionString(intf); - assert(version != NULL); - intf->version = celix_version_createVersionFromString(version); - if (intf->version == NULL) { - celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); - return ERROR; - } *out = celix_steal_ptr(intf); return OK; } -static int dynInterface_checkInterface(dyn_interface_type *intf) { - //check header section - bool foundType = false; - bool foundVersion = false; - bool foundName = false; - struct namval_entry *entry = NULL; - TAILQ_FOREACH(entry, &intf->header, entries) { - if (strcmp(entry->name, "type") == 0) { - foundType = true; - } else if (strcmp(entry->name, "version") == 0) { - foundVersion = true; - } else if (strcmp(entry->name, "name") == 0) { - foundName = true; - } - } - - if (!foundType || !foundVersion || !foundName) { - celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); - return ERROR; - } - - struct method_entry *mEntry = NULL; +static int dynInterface_checkInterface(dyn_interface_type* intf) { + struct method_entry* mEntry = NULL; TAILQ_FOREACH(mEntry, &intf->methods, entries) { const dyn_type* type = dynFunction_returnType(mEntry->dynFunc); int descriptor = dynType_descriptorType(type); @@ -119,90 +78,15 @@ static int dynInterface_checkInterface(dyn_interface_type *intf) { return OK; } -static int dynInterface_parseSection(dyn_interface_type *intf, FILE *stream) { - int status = OK; - celix_autofree char *sectionName = NULL; - - if ((status = dynCommon_eatChar(stream, ':')) != OK) { - return status; - } - - if ((status = dynCommon_parseName(stream, §ionName)) != OK) { - return status; - } - - if ((status = dynCommon_eatChar(stream, '\n')) != OK) { - return status; - } - - if (strcmp("header", sectionName) == 0) { - status = dynInterface_parseHeader(intf, stream); - } else if (strcmp("annotations", sectionName) == 0) { - status = dynInterface_parseAnnotations(intf, stream); - } else if (strcmp("types", sectionName) == 0) { - status = dynInterface_parseTypes(intf, stream); - } else if (strcmp("methods", sectionName) == 0) { - status = dynInterface_parseMethods(intf, stream); - } else { - status = ERROR; - celix_err_pushf("unsupported section '%s'", sectionName); - } - - return status; -} - -static int dynInterface_parseHeader(dyn_interface_type *intf, FILE *stream) { - return dynCommon_parseNameValueSection(stream, &intf->header); -} - -static int dynInterface_parseAnnotations(dyn_interface_type *intf, FILE *stream) { - return dynCommon_parseNameValueSection(stream, &intf->annotations); -} - -static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream) { - int status; - - //expected input (Name)=\n - int peek = fgetc(stream); - while (peek != ':' && peek != EOF) { - ungetc(peek, stream); - - celix_autofree char* name = NULL; - if ((status = dynCommon_parseName(stream, &name)) != OK) { - return status; - } - - if ((status = dynCommon_eatChar(stream, '=')) != OK) { - return status; - } - - celix_autoptr(dyn_type) type = NULL; - if ((status = dynType_parseOfName(stream, celix_steal_ptr(name), &intf->types, &type)) != OK) { - return status; - } - if ((status = dynCommon_eatChar(stream, '\n')) != OK) { - return status; - } - - struct type_entry *entry = NULL; - entry = calloc(1, sizeof(*entry)); - if (entry == NULL) { - celix_err_pushf("Error allocating memory for type entry"); - return ERROR; - } - entry->type = celix_steal_ptr(type); - TAILQ_INSERT_TAIL(&intf->types, entry, entries); - - peek = fgetc(stream); - } - if (peek != EOF) { - ungetc(peek, stream); +static int dynInterface_parseSection(dyn_interface_type* intf, const char* secName, FILE* stream) { + if (strcmp("methods", secName) != 0) { + celix_err_pushf("unsupported section '%s'", secName); + return ERROR; } - - return OK; + return dynInterface_parseMethods(intf, stream); } -static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream) { +static int dynInterface_parseMethods(dyn_interface_type* intf, FILE* stream) { int status = OK; //expected input (Name)=\n @@ -251,12 +135,9 @@ static int dynInterface_parseMethods(dyn_interface_type *intf, FILE *stream) { void dynInterface_destroy(dyn_interface_type* intf) { if (intf != NULL) { - dynCommon_clearNamValHead(&intf->header); - dynCommon_clearNamValHead(&intf->annotations); - - struct method_entry *mInfo = TAILQ_FIRST(&intf->methods); + struct method_entry* mInfo = TAILQ_FIRST(&intf->methods); while (mInfo != NULL) { - struct method_entry *mTmp = mInfo; + struct method_entry* mTmp = mInfo; mInfo = TAILQ_NEXT(mInfo, entries); if (mTmp->id != NULL) { @@ -267,67 +148,35 @@ void dynInterface_destroy(dyn_interface_type* intf) { } free(mTmp); } - - struct type_entry *tInfo = TAILQ_FIRST(&intf->types); - while (tInfo != NULL) { - struct type_entry *tmp = tInfo; - tInfo = TAILQ_NEXT(tInfo, entries); - dynType_destroy(tmp->type); - free(tmp); - } - - if(intf->version!=NULL){ - celix_version_destroy(intf->version); - } - - free(intf); - } + celix_dynDescriptor_destroy((celix_descriptor_t*)intf); + } } const char* dynInterface_getName(const dyn_interface_type* intf) { const char* name = NULL; - // dynInterface_checkInterface ensures that the name is present - (void)dynInterface_getEntryForHead(&intf->header, "name", &name); + // celix_dynDescriptor_checkInterface ensures that the name is present + (void)dynCommon_getEntryForHead(&intf->header, "name", &name); return name; } const celix_version_t* dynInterface_getVersion(const dyn_interface_type* intf){ - // dynInterface_checkInterface ensures that version is present + // celix_dynDescriptor_checkInterface ensures that version is present return intf->version; } const char* dynInterface_getVersionString(const dyn_interface_type* intf) { const char* version = NULL; - // dynInterface_checkInterface ensures that the version is present - (void)dynInterface_getEntryForHead(&intf->header, "version", &version); + // celix_dynDescriptor_checkInterface ensures that the version is present + (void)dynCommon_getEntryForHead(&intf->header, "version", &version); return version; } int dynInterface_getHeaderEntry(const dyn_interface_type* intf, const char* name, const char** value) { - return dynInterface_getEntryForHead(&intf->header, name, value); -} - -int dynInterface_getAnnotationEntry(const dyn_interface_type *intf, const char *name, const char **value) { - return dynInterface_getEntryForHead(&intf->annotations, name, value); + return dynCommon_getEntryForHead(&intf->header, name, value); } -static int dynInterface_getEntryForHead(const struct namvals_head *head, const char *name, const char **out) { - int status = OK; - char *value = NULL; - struct namval_entry *entry = NULL; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(name, entry->name) == 0) { - value = entry->value; - break; - } - } - if (value != NULL) { - *out = value; - } else { - status = ERROR; - celix_err_pushf("Cannot find '%s' in list", name); - } - return status; +int dynInterface_getAnnotationEntry(const dyn_interface_type* intf, const char* name, const char** value) { + return dynCommon_getEntryForHead(&intf->annotations, name, value); } const struct methods_head* dynInterface_methods(const dyn_interface_type* intf) { @@ -336,7 +185,7 @@ const struct methods_head* dynInterface_methods(const dyn_interface_type* intf) int dynInterface_nrOfMethods(const dyn_interface_type* intf) { int count = 0; - struct method_entry *entry = NULL; + struct method_entry* entry = NULL; TAILQ_FOREACH(entry, &intf->methods, entries) { count +=1; } diff --git a/libs/dfi/src/dyn_interface_common.h b/libs/dfi/src/dyn_interface_common.h index b3fcd5436..e1e6d66b3 100644 --- a/libs/dfi/src/dyn_interface_common.h +++ b/libs/dfi/src/dyn_interface_common.h @@ -21,6 +21,7 @@ #define _DYN_INTERFACE_COMMON_H_ #include "dyn_interface.h" +#include "dyn_descriptor.h" #include #include @@ -33,11 +34,8 @@ extern "C" { #endif struct _dyn_interface_type { - struct namvals_head header; - struct namvals_head annotations; - struct types_head types; + CELIX_DESCRIPTOR_FIELDS struct methods_head methods; - celix_version_t* version; }; #ifdef __cplusplus diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c index a8b49bd39..91e22295a 100644 --- a/libs/dfi/src/dyn_message.c +++ b/libs/dfi/src/dyn_message.c @@ -18,8 +18,8 @@ */ #include "dyn_message.h" +#include "dyn_descriptor.h" #include "celix_err.h" -#include "celix_stdlib_cleanup.h" #include #include @@ -28,283 +28,100 @@ #include "dyn_type.h" struct _dyn_message_type { - struct namvals_head header; - struct namvals_head annotations; - struct types_head types; - dyn_type *msgType; - celix_version_t* msgVersion; + CELIX_DESCRIPTOR_FIELDS + dyn_type* msgType; }; static const int OK = 0; static const int ERROR = 1; -static int dynMessage_parseSection(dyn_message_type *msg, FILE *stream); -static int dynMessage_parseAnnotations(dyn_message_type *msg, FILE *stream); -static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream); -static int dynMessage_parseMessage(dyn_message_type *msg, FILE *stream); -static int dynMessage_parseHeader(dyn_message_type *msg, FILE *stream); -static int dynMessage_checkMessage(dyn_message_type *msg); -static int dynMessage_getEntryForHead(struct namvals_head *head, const char *name, char **value); +static int dynMessage_parseSection(dyn_message_type* msg, const char* secName, FILE* stream); +static int dynMessage_parseMessage(dyn_message_type* msg, FILE* stream); -int dynMessage_parse(FILE *descriptor, dyn_message_type **out) { +int dynMessage_parse(FILE* descriptor, dyn_message_type** out) { int status = OK; - dyn_message_type *msg = calloc(1, sizeof(*msg)); - if (msg != NULL) { - TAILQ_INIT(&msg->header); - TAILQ_INIT(&msg->annotations); - TAILQ_INIT(&msg->types); - - char peek = (char)fgetc(descriptor); - while (peek == ':') { - ungetc(peek, descriptor); - status = dynMessage_parseSection(msg, descriptor); - if (status == OK) { - peek = (char)fgetc(descriptor); - } else { - break; - } - } - - if (status == OK) { - status = dynCommon_eatChar(descriptor, EOF); - } - - if (status == OK) { - status = dynMessage_checkMessage(msg); - } - - if (status == OK) { /* We are sure that version field is present in the header */ - char* version=NULL; - dynMessage_getVersionString(msg,&version); - if (version != NULL) { - msg->msgVersion = celix_version_createVersionFromString(version); - status = msg->msgVersion != NULL ? OK : ERROR; - } - if (status == ERROR) { - celix_err_pushf("Invalid version (%s) in parsed descriptor\n",version); - } - } - - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for dynamic message\n"); + celix_autoptr(dyn_message_type) msg = calloc(1, sizeof(*msg)); + if (msg == NULL) { + celix_err_push("Error allocating memory for dynamic message"); + return ERROR; } + TAILQ_INIT(&msg->header); + TAILQ_INIT(&msg->annotations); + TAILQ_INIT(&msg->types); - if (status == OK) { - *out = msg; - } else if (msg != NULL) { - celix_err_pushf("Error parsing msg\n"); - dynMessage_destroy(msg); - } - return status; -} - -static int dynMessage_checkMessage(dyn_message_type *msg) { - int status = OK; - - //check header section - if (status == OK) { - bool foundType = false; - bool foundVersion = false; - bool foundName = false; - struct namval_entry *entry = NULL; - TAILQ_FOREACH(entry, &msg->header, entries) { - if (strcmp(entry->name, "type") == 0) { - foundType = true; - } else if (strcmp(entry->name, "version") == 0) { - foundVersion = true; - } else if (strcmp(entry->name, "name") == 0) { - foundName = true; - } - } - - if (!foundType || !foundVersion || !foundName) { - status = ERROR; - celix_err_pushf("Parse Error. There must be a header section with a type, version and name entry"); - } + if ((status = celix_dynDescriptor_parse((celix_descriptor_t*)msg, descriptor, + (int (*)(celix_descriptor_t*, const char*, FILE*))dynMessage_parseSection)) != OK) { + return status; } + *out = celix_steal_ptr(msg); return status; } -static int dynMessage_parseSection(dyn_message_type *msg, FILE *stream) { - int status; - char *sectionName = NULL; - - status = dynCommon_eatChar(stream, ':'); - - if (status == OK) { - status = dynCommon_parseName(stream, §ionName); - } - - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); - } - - if (status == OK) { - if (strcmp("header", sectionName) == 0) { - status = dynMessage_parseHeader(msg, stream); - } else if (strcmp("annotations", sectionName) == 0) { - status = dynMessage_parseAnnotations(msg, stream); - } else if (strcmp("types", sectionName) == 0) { - status = dynMessage_parseTypes(msg, stream); - } else if (strcmp("message", sectionName) == 0) { - status = dynMessage_parseMessage(msg, stream); - } else { - status = ERROR; - celix_err_pushf("unsupported section '%s'", sectionName); - } +static int dynMessage_parseSection(dyn_message_type* msg, const char* secName, FILE* stream) { + if (strcmp("message", secName) != 0) { + celix_err_pushf("unsupported section '%s'", secName); + return ERROR; } - - if (sectionName != NULL) { - free(sectionName); - } - - return status; -} - -static int dynMessage_parseHeader(dyn_message_type *msg, FILE *stream) { - return dynCommon_parseNameValueSection(stream, &msg->header); -} - -static int dynMessage_parseAnnotations(dyn_message_type *msg, FILE *stream) { - return dynCommon_parseNameValueSection(stream, &msg->annotations); + return dynMessage_parseMessage(msg, stream); } -static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { - int status = OK; - - //expected input (Name)=\n - int peek = fgetc(stream); - while (peek != ':' && peek != EOF) { - ungetc(peek, stream); - - celix_autofree char* name = NULL; - status = dynCommon_parseName(stream, &name); - - if (status == OK) { - status = dynCommon_eatChar(stream, '='); - } - - dyn_type *type = NULL; - if (status == OK) { - status = dynType_parseOfName(stream, celix_steal_ptr(name), &msg->types, &type); - } - - if (status == OK) { - status = dynCommon_eatChar(stream, '\n'); - } - - struct type_entry *entry = NULL; - if (status == OK) { - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->type = type; - TAILQ_INSERT_TAIL(&msg->types, entry, entries); - } else { - status = ERROR; - celix_err_pushf("Error allocating memory for type entry"); - } - } - - if (status != OK) { - if (type != NULL) { - dynType_destroy(type); - } - break; - } - peek = fgetc(stream); - } - ungetc(peek, stream); - - return status; -} - -static int dynMessage_parseMessage(dyn_message_type *msg, FILE *stream) { +static int dynMessage_parseMessage(dyn_message_type* msg, FILE* stream) { int status; //expected input \n - char *name = NULL; - status = dynMessage_getName(msg, &name); + const char* name = NULL; + if ((status = dynCommon_getEntryForHead(&msg->header, "name", &name)) != OK) { + return status; + } - if (status == OK) { - status = dynType_parse(stream, name, &(msg->types), &(msg->msgType)); + if ((status = dynType_parse(stream, name, &(msg->types), &(msg->msgType))) != OK) { + return status; } + if ((status = dynCommon_eatChar(stream, '\n')) != OK) { + return status; + } return status; } -void dynMessage_destroy(dyn_message_type *msg) { +void dynMessage_destroy(dyn_message_type* msg) { if (msg != NULL) { - dynCommon_clearNamValHead(&msg->header); - dynCommon_clearNamValHead(&msg->annotations); - - struct type_entry *tInfo = TAILQ_FIRST(&msg->types); - while (tInfo != NULL) { - struct type_entry *tmp = tInfo; - tInfo = TAILQ_NEXT(tInfo, entries); - dynType_destroy(tmp->type); - free(tmp); - } - if (msg->msgType != NULL) { - dynType_destroy(msg->msgType); - } - - if(msg->msgVersion != NULL){ - celix_version_destroy(msg->msgVersion); + dynType_destroy(msg->msgType); } - - free(msg); - } -} - -int dynMessage_getName(dyn_message_type *msg, char **out) { - return dynMessage_getEntryForHead(&msg->header, "name", out); + celix_dynDescriptor_destroy((celix_descriptor_t*)msg); + } } -int dynMessage_getVersion(dyn_message_type *msg, celix_version_t** version){ - *version = msg->msgVersion; - if(*version==NULL){ - return ERROR; - } - return OK; +const char* dynMessage_getName(const dyn_message_type* msg) { + const char* name = NULL; + // celix_dynDescriptor_checkInterface ensures that the name is present + (void)dynCommon_getEntryForHead(&msg->header, "name", &name); + return name; } -int dynMessage_getVersionString(dyn_message_type *msg, char **version) { - return dynMessage_getEntryForHead(&msg->header, "version", version); +const celix_version_t* dynMessage_getVersion(const dyn_message_type* msg){ + // celix_dynDescriptor_checkInterface ensures that version is present + return msg->version; } -int dynMessage_getHeaderEntry(dyn_message_type *msg, const char *name, char **value) { - return dynMessage_getEntryForHead(&msg->header, name, value); +const char* dynMessage_getVersionString(const dyn_message_type* msg) { + const char* version = NULL; + // celix_dynDescriptor_checkInterface ensures that the version is present + (void)dynCommon_getEntryForHead(&msg->header, "version", &version); + return version; } -int dynMessage_getAnnotationEntry(dyn_message_type *msg, const char *name, char **value) { - return dynMessage_getEntryForHead(&msg->annotations, name, value); +int dynMessage_getHeaderEntry(dyn_message_type* msg, const char* name, const char** value) { + return dynCommon_getEntryForHead(&msg->header, name, value); } -static int dynMessage_getEntryForHead(struct namvals_head *head, const char *name, char **out) { - int status = OK; - char *value = NULL; - struct namval_entry *entry = NULL; - TAILQ_FOREACH(entry, head, entries) { - if (strcmp(name, entry->name) == 0) { - value = entry->value; - break; - } - } - if (value != NULL) { - *out = value; - } else { - status = ERROR; - celix_err_pushf("Cannot find '%s' in list", name); - } - return status; +int dynMessage_getAnnotationEntry(dyn_message_type* msg, const char* name, const char** value) { + return dynCommon_getEntryForHead(&msg->annotations, name, value); } -int dynMessage_getMessageType(dyn_message_type *msg, dyn_type **type) { - int status = OK; - *type = msg->msgType; - return status; +const dyn_type* dynMessage_getMessageType(dyn_message_type* msg) { + return msg->msgType; } From b7354954c8646398e97c473e5177668f757f4936 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 28 Dec 2023 10:48:28 +0800 Subject: [PATCH 14/62] Avoid explicit function pointer conversion. --- libs/dfi/src/dyn_interface.c | 8 ++++---- libs/dfi/src/dyn_message.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index c3b8d7ae0..819532705 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -34,7 +34,7 @@ static const int OK = 0; static const int ERROR = 1; static int dynInterface_checkInterface(dyn_interface_type* intf); -static int dynInterface_parseSection(dyn_interface_type* intf, const char* secName, FILE* stream); +static int dynInterface_parseSection(celix_descriptor_t* desc, const char* secName, FILE* stream); static int dynInterface_parseMethods(dyn_interface_type* intf, FILE* stream); int dynInterface_parse(FILE* descriptor, dyn_interface_type** out) { @@ -51,8 +51,7 @@ int dynInterface_parse(FILE* descriptor, dyn_interface_type** out) { TAILQ_INIT(&intf->types); TAILQ_INIT(&intf->methods); - if ((status = celix_dynDescriptor_parse((celix_descriptor_t*)intf, descriptor, - (int (*)(celix_descriptor_t*, const char*, FILE *))dynInterface_parseSection)) != OK) { + if ((status = celix_dynDescriptor_parse((celix_descriptor_t*)intf, descriptor, dynInterface_parseSection)) != OK) { return status; } @@ -78,7 +77,8 @@ static int dynInterface_checkInterface(dyn_interface_type* intf) { return OK; } -static int dynInterface_parseSection(dyn_interface_type* intf, const char* secName, FILE* stream) { +static int dynInterface_parseSection(celix_descriptor_t* desc, const char* secName, FILE* stream) { + dyn_interface_type* intf = (dyn_interface_type*)desc; if (strcmp("methods", secName) != 0) { celix_err_pushf("unsupported section '%s'", secName); return ERROR; diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c index 91e22295a..95cdc67fc 100644 --- a/libs/dfi/src/dyn_message.c +++ b/libs/dfi/src/dyn_message.c @@ -35,7 +35,7 @@ struct _dyn_message_type { static const int OK = 0; static const int ERROR = 1; -static int dynMessage_parseSection(dyn_message_type* msg, const char* secName, FILE* stream); +static int dynMessage_parseSection(celix_descriptor_t* desc, const char* secName, FILE* stream); static int dynMessage_parseMessage(dyn_message_type* msg, FILE* stream); int dynMessage_parse(FILE* descriptor, dyn_message_type** out) { @@ -50,8 +50,7 @@ int dynMessage_parse(FILE* descriptor, dyn_message_type** out) { TAILQ_INIT(&msg->annotations); TAILQ_INIT(&msg->types); - if ((status = celix_dynDescriptor_parse((celix_descriptor_t*)msg, descriptor, - (int (*)(celix_descriptor_t*, const char*, FILE*))dynMessage_parseSection)) != OK) { + if ((status = celix_dynDescriptor_parse((celix_descriptor_t*)msg, descriptor, dynMessage_parseSection)) != OK) { return status; } @@ -59,7 +58,8 @@ int dynMessage_parse(FILE* descriptor, dyn_message_type** out) { return status; } -static int dynMessage_parseSection(dyn_message_type* msg, const char* secName, FILE* stream) { +static int dynMessage_parseSection(celix_descriptor_t* desc, const char* secName, FILE* stream) { + dyn_message_type* msg = (dyn_message_type*)desc; if (strcmp("message", secName) != 0) { celix_err_pushf("unsupported section '%s'", secName); return ERROR; From d0c1ede21627abd5d87ee44f81f02b392b6d54d9 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 28 Dec 2023 20:53:10 +0800 Subject: [PATCH 15/62] Extend dynType_parseWithStreamOfName for code deduplication, and remove tail recursion from dynType_findType. --- libs/dfi/gtest/src/dyn_type_ei_tests.cc | 24 ++- libs/dfi/gtest/src/dyn_type_tests.cpp | 38 +++- libs/dfi/src/dyn_common.c | 2 +- libs/dfi/src/dyn_function.c | 2 - libs/dfi/src/dyn_type.c | 260 +++++++++--------------- libs/dfi/src/dyn_type_common.c | 41 ++-- libs/dfi/src/dyn_type_common.h | 5 +- 7 files changed, 174 insertions(+), 198 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc index 7a2deb622..c1e5b29a7 100644 --- a/libs/dfi/gtest/src/dyn_type_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -44,7 +44,7 @@ class DynTypeErrorInjectionTestSuite : public ::testing::Test { DynTypeErrorInjectionTestSuite& operator=(DynTypeErrorInjectionTestSuite&&) = delete; }; -TEST_F(DynTypeErrorInjectionTestSuite, ParseTypeErrors) { +TEST_F(DynTypeErrorInjectionTestSuite, ParseComplexTypeErrors) { dyn_type *type = NULL; const char* descriptor = "{D{DD b_1 b_2}I a b c}"; @@ -68,3 +68,25 @@ TEST_F(DynTypeErrorInjectionTestSuite, ParseTypeErrors) { ASSERT_NE(0, status); ASSERT_STREQ("Error strdup'ing name 'hello'", celix_err_popLastError()); } + +TEST_F(DynTypeErrorInjectionTestSuite, ParseNestedTypeErrors) { + dyn_type *type = NULL; + const char* descriptor = "Tnode={Lnode;Lnode; left right};{Lnode; head}"; + int status = 0; + + // fail to allocate type_entry + celix_ei_expect_calloc((void*)dynType_parseWithStr, 4, nullptr); + status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating entry", celix_err_popLastError()); +} + +TEST_F(DynTypeErrorInjectionTestSuite, ParseEnumTypeErrors) { + dyn_type *type = NULL; + int rc = 0; + // fail to allocate meta_entry + celix_ei_expect_calloc((void*)dynType_parseWithStr, 4, nullptr, 1); + rc = dynType_parseWithStr("#v1=0;#v2=1;E", NULL, NULL, &type); + ASSERT_NE(0, rc); + celix_err_printErrors(stderr, nullptr, nullptr); +} diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index ed4bbef49..e858a1781 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -60,6 +60,7 @@ class DynTypeTests : public ::testing::Test { DynTypeTests() { } ~DynTypeTests() override { + celix_err_resetErrors(); } }; @@ -326,10 +327,32 @@ TEST_F(DynTypeTests, SchemaEndsWithoutNullTerminator) { celix_err_printErrors(stderr, nullptr, nullptr); } +TEST_F(DynTypeTests, MetaInfoMissingName) { + dyn_type *type = NULL; + auto rc = dynType_parseWithStr(R"(#=1;)", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + ASSERT_STREQ("Failed to parse meta properties", celix_err_popLastError()); + ASSERT_STREQ("Parsed empty name", celix_err_popLastError()); +} + +TEST_F(DynTypeTests, MetaInfoMissingEquality) { + dyn_type *type = NULL; + auto rc = dynType_parseWithStr(R"(#testMetaInfo 1;)", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + celix_err_printErrors(stderr, nullptr, nullptr); +} + +TEST_F(DynTypeTests, MetaInfoMissingSemicolon) { + dyn_type *type = NULL; + auto rc = dynType_parseWithStr(R"(#testMetaInfo=1 )", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + celix_err_printErrors(stderr, nullptr, nullptr); +} + TEST_F(DynTypeTests, MetaInfoMissingValue) { dyn_type *type = NULL; - auto rc = dynType_parseWithStr(R"(#testMetaInfo=)", nullptr, nullptr, &type); - ASSERT_EQ(1, rc); + auto rc = dynType_parseWithStr(R"(#testMetaInfo=;)", nullptr, nullptr, &type); + ASSERT_NE(0, rc); celix_err_printErrors(stderr, nullptr, nullptr); } @@ -357,4 +380,15 @@ TEST_F(DynTypeTests, ParseReferenceFailed) { auto rc = dynType_parseWithStr(R"(Ttype={DD a b};ltype)", nullptr, nullptr, &type); ASSERT_EQ(3, rc); celix_err_printErrors(stderr, nullptr, nullptr); + //missing ';' + rc = dynType_parseWithStr(R"(Ttype={DD a b};Ltype)", nullptr, nullptr, &type); + ASSERT_EQ(3, rc); + celix_err_printErrors(stderr, nullptr, nullptr); +} + +TEST_F(DynTypeTests, ParseSequenceFailed) { + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("Tval={DD a b};Titem={Jtlval;DDJ a text val c d e};**[Lite;", NULL, NULL, &type); + ASSERT_NE(0, rc); } \ No newline at end of file diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 6ea656e35..06b8c2cc6 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -61,7 +61,7 @@ int dynCommon_parseNameAlsoAccept(FILE* stream, const char* acceptedChars, char* } if (size == 0) { - celix_err_pushf("Parsed empty name"); + celix_err_push("Parsed empty name"); return ERROR; } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index 69a7dcf99..de045797a 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -35,8 +35,6 @@ static int dynFunction_initCif(dyn_function_type *dynFunc); static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descriptor); static void dynFunction_ffiBind(ffi_cif *cif, void *ret, void *args[], void *userData); -ffi_type * dynType_ffiType(dyn_type *type); - int dynFunction_parse(FILE *descriptor, struct types_head *refTypes, dyn_function_type **out) { int status = OK; dyn_function_type *dynFunc = NULL; diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 9b995423e..b9660c1b7 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -36,14 +36,11 @@ static const int MEM_ERROR = 2; static const int PARSE_ERROR = 3; static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); -static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); +static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result, int (*)(FILE*, dyn_type*)); static void dynType_clear(dyn_type* type); static void dynType_clearComplex(dyn_type* type); static void dynType_clearSequence(dyn_type* type); static void dynType_clearTypedPointer(dyn_type* type); -ffi_type* dynType_ffiType(dyn_type* type); - -static struct type_entry* dynType_allocTypeEntry(void); static ffi_type* dynType_ffiTypeFor(int c); static int dynType_parseAny(FILE* stream, dyn_type* type); @@ -88,11 +85,11 @@ int dynType_parse(FILE* descriptorStream, const char* name, const struct types_h int dynType_parseOfName(FILE* descriptorStream, char* name, const struct types_head* refTypes, dyn_type** type) { - return dynType_parseWithStreamOfName(descriptorStream, name, NULL, refTypes, type); + return dynType_parseWithStreamOfName(descriptorStream, name, NULL, refTypes, type, dynType_parseAny); } int dynType_parseWithStr(const char* descriptor, const char* name, const struct types_head* refTypes, dyn_type** type) { - int status = OK; + int status; celix_autoptr(FILE) stream = fmemopen((char *)descriptor, strlen(descriptor), "r"); if (stream == NULL) { celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); @@ -106,7 +103,7 @@ int dynType_parseWithStr(const char* descriptor, const char* name, const struct return PARSE_ERROR; } *type = celix_steal_ptr(result); - return status; + return OK; } static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { @@ -118,11 +115,12 @@ static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* par return MEM_ERROR; } } - return dynType_parseWithStreamOfName(stream, typeName, parent, refTypes, result); + return dynType_parseWithStreamOfName(stream, typeName, parent, refTypes, result, dynType_parseAny); } -static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { - int status = OK; +static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, + dyn_type** result, int (*parse)(FILE*, dyn_type*)) { + int status; celix_autofree char* typeName = name; celix_autoptr(dyn_type) type = calloc(1, sizeof(*type)); if (type == NULL) { @@ -135,11 +133,11 @@ static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* par TAILQ_INIT(&type->nestedTypesHead); TAILQ_INIT(&type->metaProperties); type->name = celix_steal_ptr(typeName); - if ((status = dynType_parseAny(stream, type)) != OK) { + if ((status = parse(stream, type)) != OK) { return status; } *result = celix_steal_ptr(type); - return status; + return OK; } static int dynType_parseAny(FILE* stream, dyn_type* type) { @@ -190,58 +188,52 @@ static int dynType_parseAny(FILE* stream, dyn_type* type) { static int dynType_parseMetaInfo(FILE* stream, dyn_type* type) { int status = OK; - char* name = NULL; - char* value = NULL; + celix_autofree char* name = NULL; + celix_autofree char* value = NULL; - struct meta_entry *entry = calloc(1, sizeof(*entry)); - if (entry == NULL) { - status = ERROR; - } - - if (status == OK) { - status = dynCommon_parseName(stream, &name); + if (dynCommon_parseName(stream, &name) != OK) { + status = PARSE_ERROR; + goto bail_out; } - - if (status == OK) { - status = dynCommon_eatChar(stream, '='); + if (dynCommon_eatChar(stream, '=') != OK) { + status = PARSE_ERROR; + goto bail_out; } - - if (status == OK) { - status = dynCommon_parseName(stream, &value); + if (dynCommon_parseName(stream, &value) != OK) { + status = PARSE_ERROR; + goto bail_out; } - - if (status == OK) { - status = dynCommon_eatChar(stream, ';'); + if (dynCommon_eatChar(stream, ';') != OK) { + status = PARSE_ERROR; + goto bail_out; } - - if (status == OK) { - entry->name = name; - entry->value = value; - TAILQ_INSERT_TAIL(&type->metaProperties, entry, entries); - } else { - celix_err_pushf("Failed to parse meta properties"); - free(name); - free(value); - free(entry); + struct meta_entry *entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + status = MEM_ERROR; + goto bail_out; } + entry->name = celix_steal_ptr(name); + entry->value = celix_steal_ptr(value); + TAILQ_INSERT_TAIL(&type->metaProperties, entry, entries); + return OK; +bail_out: + celix_err_push("Failed to parse meta properties"); return status; } static int dynType_parseText(FILE* stream, dyn_type* type) { - int status = OK; type->type = DYN_TYPE_TEXT; type->descriptor = 't'; type->ffiType = &ffi_type_pointer; - return status; + return OK; } static int dynType_parseEnum(FILE* stream, dyn_type* type) { - int status = OK; type->ffiType = &ffi_type_sint32; type->descriptor = 'E'; type->type = DYN_TYPE_SIMPLE; - return status; + return OK; } static int dynType_parseComplex(FILE* stream, dyn_type* type) { @@ -255,31 +247,22 @@ static int dynType_parseComplex(FILE* stream, dyn_type* type) { struct complex_type_entry* entry = NULL; while (c != ' ' && c != '}') { ungetc(c,stream); - entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->type = calloc(1, sizeof(*entry->type)); + celix_autoptr(dyn_type) subType = NULL; + status = dynType_parseWithStreamOfName(stream, NULL, type, NULL, &subType, dynType_parseAny); + if (status != OK) { + return status; } - if (entry != NULL && entry->type != NULL) { - entry->type->parent = type; - entry->type->type = DYN_TYPE_INVALID; - TAILQ_INIT(&entry->type->nestedTypesHead); - TAILQ_INIT(&entry->type->metaProperties); - TAILQ_INSERT_TAIL(&type->complex.entriesHead, entry, entries); - status = dynType_parseAny(stream, entry->type); - } else { - free(entry); - status = MEM_ERROR; + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { celix_err_pushf("Error allocating memory for type"); + return MEM_ERROR; } - - if (status != OK) { - break; - } - + entry->type = celix_steal_ptr(subType); + TAILQ_INSERT_TAIL(&type->complex.entriesHead, entry, entries); c = fgetc(stream); } -// loop over names + // loop over names if (status == OK) { entry = TAILQ_FIRST(&type->complex.entriesHead); char* name = NULL; @@ -331,7 +314,7 @@ static int dynType_parseComplex(FILE* stream, dyn_type* type) { } if (status == OK) { - dynType_prepCif(type->ffiType); + (void)ffi_get_struct_offsets(FFI_DEFAULT_ABI, type->ffiType, NULL); } @@ -340,66 +323,42 @@ static int dynType_parseComplex(FILE* stream, dyn_type* type) { static int dynType_parseNestedType(FILE* stream, dyn_type* type) { int status = OK; - char* name = NULL; - struct type_entry* entry = NULL; - - entry = dynType_allocTypeEntry(); - if (entry != NULL) { - entry->type->parent = type; - entry->type->type = DYN_TYPE_INVALID; - TAILQ_INIT(&entry->type->nestedTypesHead); - TAILQ_INIT(&entry->type->metaProperties); - TAILQ_INSERT_TAIL(&type->nestedTypesHead, entry, entries); - status = dynCommon_parseName(stream, &name); - entry->type->name = name; - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating entry"); - } - - if (status == OK) { - int c = fgetc(stream); - if (c != '=') { - status = PARSE_ERROR; - celix_err_pushf("Error parsing nested type expected '=' got '%c'", c); - } + celix_autofree char* name = NULL; + celix_autoptr(dyn_type) subType = NULL; + if ((status = dynCommon_parseName(stream, &name)) != OK) { + return status; } - - if (status == OK) { - status = dynType_parseAny(stream, entry->type); - int c = fgetc(stream); - if (c != ';') { - status = PARSE_ERROR; - celix_err_pushf("Expected ';' got '%c'\n", c); - } + if (dynCommon_eatChar(stream, '=') != OK) { + return PARSE_ERROR; } - - return status; + if ((status = dynType_parseWithStreamOfName(stream, celix_steal_ptr(name), type, NULL, &subType, dynType_parseAny)) != OK) { + return status; + } + if (dynCommon_eatChar(stream, ';') != OK) { + return PARSE_ERROR; + } + struct type_entry* entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + celix_err_pushf("Error allocating entry"); + return MEM_ERROR; + } + entry->type = celix_steal_ptr(subType); + TAILQ_INSERT_TAIL(&type->nestedTypesHead, entry, entries); + return OK; } static int dynType_parseReference(FILE* stream, dyn_type* type) { - int status = OK; + int status; type->type = DYN_TYPE_TYPED_POINTER; type->descriptor = '*'; - type->ffiType = &ffi_type_pointer; type->typedPointer.typedType = NULL; - dyn_type* subType = calloc(1, sizeof(*subType)); - - if (subType != NULL) { - type->typedPointer.typedType = subType; - subType->parent = type; - subType->type = DYN_TYPE_INVALID; - TAILQ_INIT(&subType->nestedTypesHead); - TAILQ_INIT(&subType->metaProperties); - status = dynType_parseRefByValue(stream, subType); - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for subtype\n"); + if ((status = dynType_parseWithStreamOfName(stream, NULL, type, NULL, + &type->typedPointer.typedType, dynType_parseRefByValue)) != OK) { + return status; } - - return status; + return OK; } static int dynType_parseRefByValue(FILE* stream, dyn_type* type) { @@ -407,46 +366,26 @@ static int dynType_parseRefByValue(FILE* stream, dyn_type* type) { type->type = DYN_TYPE_REF; type->descriptor = 'l'; - char* name = NULL; - status = dynCommon_parseName(stream, &name); - if (status == OK) { - dyn_type* ref = dynType_findType(type, name); - if (ref != NULL) { - type->ref.ref = ref; - } else { - status = PARSE_ERROR; - celix_err_pushf("Error cannot find type '%s'", name); - } - free(name); - } - - if (status ==OK) { - int c = fgetc(stream); - if (c != ';') { - status = PARSE_ERROR; - celix_err_pushf("Error expected ';' got '%c'", c); - } + celix_autofree char* name = NULL; + if ((status = dynCommon_parseName(stream, &name)) != OK) { + return status; } - - return status; -} - -static struct type_entry* dynType_allocTypeEntry(void) { - struct type_entry* entry = calloc(1, sizeof(*entry)); - if (entry != NULL) { - entry->type = calloc(1, sizeof(*entry->type)); - if (entry->type == NULL) { - free(entry); - entry = NULL; - } + dyn_type* ref = dynType_findType(type, name); + if (ref == NULL) { + celix_err_pushf("Error cannot find type '%s'", name); + return PARSE_ERROR; + } + type->ref.ref = ref; + if (dynCommon_eatChar(stream, ';') != OK) { + return PARSE_ERROR; } - return entry; + return OK; } static ffi_type* seq_types[] = {&ffi_type_uint32, &ffi_type_uint32, &ffi_type_pointer, NULL}; static int dynType_parseSequence(FILE* stream, dyn_type* type) { - int status = OK; + int status; type->type = DYN_TYPE_SEQUENCE; type->descriptor = '['; @@ -455,29 +394,26 @@ static int dynType_parseSequence(FILE* stream, dyn_type* type) { type->sequence.seqType.size = 0; type->sequence.seqType.alignment = 0; - status = dynType_parseWithStream(stream, NULL, type, NULL, &type->sequence.itemType); - - if (status == OK) { - type->ffiType = &type->sequence.seqType; - dynType_prepCif(&type->sequence.seqType); + status = dynType_parseWithStreamOfName(stream, NULL, type, NULL, &type->sequence.itemType, dynType_parseAny); + if (status != OK) { + return status; } - return status; + type->ffiType = &type->sequence.seqType; + (void)ffi_get_struct_offsets(FFI_DEFAULT_ABI, type->ffiType, NULL); + return OK; } static int dynType_parseSimple(int c, dyn_type* type) { - int status = OK; ffi_type* ffiType = dynType_ffiTypeFor(c); - if (ffiType != NULL) { - type->type = DYN_TYPE_SIMPLE; - type->descriptor = c; - type->ffiType = ffiType; - } else { - status = PARSE_ERROR; + if (ffiType == NULL) { celix_err_pushf("Error unsupported type '%c'", c); + return PARSE_ERROR; } - - return status; + type->type = DYN_TYPE_SIMPLE; + type->descriptor = c; + type->ffiType = ffiType; + return OK; } static int dynType_parseTypedPointer(FILE* stream, dyn_type* type) { @@ -486,7 +422,7 @@ static int dynType_parseTypedPointer(FILE* stream, dyn_type* type) { type->descriptor = '*'; type->ffiType = &ffi_type_pointer; - status = dynType_parseWithStream(stream, NULL, type, NULL, &type->typedPointer.typedType); + status = dynType_parseWithStreamOfName(stream, NULL, type, NULL, &type->typedPointer.typedType, dynType_parseAny); return status; } diff --git a/libs/dfi/src/dyn_type_common.c b/libs/dfi/src/dyn_type_common.c index 6de30f453..1005fddf3 100644 --- a/libs/dfi/src/dyn_type_common.c +++ b/libs/dfi/src/dyn_type_common.c @@ -27,34 +27,28 @@ #include #include -dyn_type * dynType_findType(dyn_type *type, char *name) { - dyn_type *result = NULL; - - struct type_entry *entry = NULL; - if (type->referenceTypes != NULL) { - TAILQ_FOREACH(entry, type->referenceTypes, entries) { - if (strcmp(name, entry->type->name) == 0) { - result = entry->type; - break; +dyn_type* dynType_findType(dyn_type *type, char *name) { + for (dyn_type *current = type; current != NULL; current = current->parent) { + struct type_entry *entry = NULL; + if (current->name != NULL && strcmp(current->name, name) == 0) { + return current; + } + if (current->referenceTypes != NULL) { + TAILQ_FOREACH(entry, current->referenceTypes, entries) { + if (strcmp(name, entry->type->name) == 0) { + return entry->type; + } } } - } - if (result == NULL) { struct type_entry *nEntry = NULL; - TAILQ_FOREACH(nEntry, &type->nestedTypesHead, entries) { + TAILQ_FOREACH(nEntry, ¤t->nestedTypesHead, entries) { if (strcmp(name, nEntry->type->name) == 0) { - result = nEntry->type; - break; + return nEntry->type; } } } - - if (result == NULL && type->parent != NULL) { - result = dynType_findType(type->parent, name); - } - - return result; + return NULL; } ffi_type * dynType_ffiType(dyn_type * type) { @@ -68,10 +62,3 @@ ffi_type * dynType_ffiType(dyn_type * type) { return type->ffiType; } -void dynType_prepCif(ffi_type *type) { - ffi_cif cif; - ffi_type *args[1]; - args[0] = type; - ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_uint, args); -} - diff --git a/libs/dfi/src/dyn_type_common.h b/libs/dfi/src/dyn_type_common.h index 8638b63a7..34e5b4a9d 100644 --- a/libs/dfi/src/dyn_type_common.h +++ b/libs/dfi/src/dyn_type_common.h @@ -58,9 +58,8 @@ struct _dyn_type { }; }; -dyn_type * dynType_findType(dyn_type* type, char* name); -ffi_type * dynType_ffiType(dyn_type* type); -void dynType_prepCif(ffi_type* type); +dyn_type* dynType_findType(dyn_type* type, char* name); +ffi_type* dynType_ffiType(dyn_type* type); #ifdef __cplusplus } From 242287949eb5467983621560129025b9897034b4 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 29 Dec 2023 21:45:08 +0800 Subject: [PATCH 16/62] Refactor dynType_parseComplex to return early on error and handle error of missing the closing brace. --- libs/dfi/gtest/src/dyn_message_tests.cpp | 3 + libs/dfi/gtest/src/dyn_type_ei_tests.cc | 18 ++++++ libs/dfi/gtest/src/dyn_type_tests.cpp | 9 ++- libs/dfi/src/dyn_type.c | 79 ++++++++++-------------- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_message_tests.cpp b/libs/dfi/gtest/src/dyn_message_tests.cpp index 533171554..85e5578b1 100644 --- a/libs/dfi/gtest/src/dyn_message_tests.cpp +++ b/libs/dfi/gtest/src/dyn_message_tests.cpp @@ -34,6 +34,7 @@ extern "C" { #include "dyn_common.h" #include "dyn_message.h" +#include "celix_err.h" #include "celix_version.h" static void checkMessageVersion(dyn_message_type* dynMsg, const char* v){ @@ -224,6 +225,7 @@ class DynMessageTests : public ::testing::Test { DynMessageTests() { } ~DynMessageTests() override { + celix_err_resetErrors(); } }; @@ -242,6 +244,7 @@ TEST_F(DynMessageTests, msg_test3) { TEST_F(DynMessageTests, msg_test4) { msg_test4(); + celix_err_printErrors(stderr, nullptr, nullptr); } TEST_F(DynMessageTests, msg_invalid) { diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc index c1e5b29a7..32c6599d2 100644 --- a/libs/dfi/gtest/src/dyn_type_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -67,6 +67,24 @@ TEST_F(DynTypeErrorInjectionTestSuite, ParseComplexTypeErrors) { status = dynType_parseWithStr(descriptor, "hello", NULL, &type); ASSERT_NE(0, status); ASSERT_STREQ("Error strdup'ing name 'hello'", celix_err_popLastError()); + + // fail to allocate complex_type_entry + celix_ei_expect_calloc((void*)dynType_parseWithStr, 4, nullptr); + status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for complex_type_entry", celix_err_popLastError()); + + // fail to allocate ffi_type elements + celix_ei_expect_calloc((void*)dynType_parseWithStr, 4, nullptr, 4); + status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for ffi_type elements", celix_err_popLastError()); + + // fail to allocate complex types + celix_ei_expect_calloc((void*)dynType_parseWithStr, 4, nullptr, 5); + status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for complex types", celix_err_popLastError()); } TEST_F(DynTypeErrorInjectionTestSuite, ParseNestedTypeErrors) { diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index e858a1781..0599d8f5a 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -101,7 +101,7 @@ CREATE_EXAMPLES_TEST(EX10) CREATE_EXAMPLES_TEST(EX11) CREATE_EXAMPLES_TEST(EX12) CREATE_EXAMPLES_TEST(EX13) -CREATE_EXAMPLES_TEST(EX14) +//CREATE_EXAMPLES_TEST(EX14) CREATE_EXAMPLES_TEST(EX15) CREATE_EXAMPLES_TEST(EX16) CREATE_EXAMPLES_TEST(EX17) @@ -319,6 +319,13 @@ TEST_F(DynTypeTests, ComplexHasEmptyName) { celix_err_printErrors(stderr, nullptr, nullptr); } +TEST_F(DynTypeTests, ComplexTypeMissingClosingBrace) { + dyn_type *type = NULL; + auto rc = dynType_parseWithStr(R"({II a b)", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error parsing complex type, expected '}'", celix_err_popLastError()); +} + TEST_F(DynTypeTests, SchemaEndsWithoutNullTerminator) { dyn_type *type = NULL; //ends with '-' diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index b9660c1b7..0edb9a726 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -237,6 +237,7 @@ static int dynType_parseEnum(FILE* stream, dyn_type* type) { } static int dynType_parseComplex(FILE* stream, dyn_type* type) { + size_t nbEntries = 0; int status = OK; type->type = DYN_TYPE_COMPLEX; type->descriptor = '{'; @@ -254,69 +255,55 @@ static int dynType_parseComplex(FILE* stream, dyn_type* type) { } entry = calloc(1, sizeof(*entry)); if (entry == NULL) { - celix_err_pushf("Error allocating memory for type"); + celix_err_push("Error allocating memory for complex_type_entry"); return MEM_ERROR; } entry->type = celix_steal_ptr(subType); TAILQ_INSERT_TAIL(&type->complex.entriesHead, entry, entries); + nbEntries += 1; c = fgetc(stream); } // loop over names - if (status == OK) { - entry = TAILQ_FIRST(&type->complex.entriesHead); - char* name = NULL; - while (c == ' ' && entry != NULL) { - status = dynCommon_parseName(stream, &name); - if (status == OK) { - entry->name = name; - entry = TAILQ_NEXT(entry, entries); - } else { - break; - } - c = getc(stream); + entry = TAILQ_FIRST(&type->complex.entriesHead); + char* name = NULL; + // the current implementation permits trailing unnamed fields, i.e. number of names is less than number of fields + while (c == ' ' && entry != NULL) { + if ((status = dynCommon_parseName(stream, &name)) != OK) { + return status; } + entry->name = name; + entry = TAILQ_NEXT(entry, entries); + c = getc(stream); } - - int count = 0; - if (status == OK) { - TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { - count +=1; - } + if (c != '}') { + celix_err_push("Error parsing complex type, expected '}'"); + return PARSE_ERROR; } - if (status == OK) { - type->complex.structType.type = FFI_TYPE_STRUCT; - type->complex.structType.elements = calloc(count + 1, sizeof(ffi_type*)); - if (type->complex.structType.elements != NULL) { - type->complex.structType.elements[count] = NULL; - int index = 0; - TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { - type->complex.structType.elements[index++] = dynType_ffiType(entry->type); - } - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for elements"); - } + type->complex.structType.type = FFI_TYPE_STRUCT; + type->complex.structType.elements = calloc(nbEntries + 1, sizeof(ffi_type*)); + if (type->complex.structType.elements == NULL) { + celix_err_push("Error allocating memory for ffi_type elements"); + return MEM_ERROR; } - - if (status == OK) { - type->complex.types = calloc(count, sizeof(dyn_type *)); - if (type->complex.types != NULL) { - int index = 0; - TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { - type->complex.types[index++] = entry->type; - } - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for type"); - } + type->complex.structType.elements[nbEntries] = NULL; + int index = 0; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + type->complex.structType.elements[index++] = dynType_ffiType(entry->type); } - if (status == OK) { - (void)ffi_get_struct_offsets(FFI_DEFAULT_ABI, type->ffiType, NULL); + type->complex.types = calloc(nbEntries, sizeof(dyn_type *)); + if (type->complex.types == NULL) { + celix_err_pushf("Error allocating memory for complex types"); + return MEM_ERROR; + } + index = 0; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + type->complex.types[index++] = entry->type; } + (void)ffi_get_struct_offsets(FFI_DEFAULT_ABI, type->ffiType, NULL); return status; } From 53c5da763b764662df58366595d0b8cd7d23fd3c Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 31 Dec 2023 19:17:24 +0800 Subject: [PATCH 17/62] Remove tail recursion in dynType_alloc and add more parsing tests. --- libs/dfi/gtest/src/dyn_type_ei_tests.cc | 12 +++++++++++ libs/dfi/gtest/src/dyn_type_tests.cpp | 22 ++++++++++++++++++-- libs/dfi/src/dyn_type.c | 27 ++++++++++--------------- libs/dfi/src/dyn_type_common.c | 4 ---- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc index 32c6599d2..432d8ac25 100644 --- a/libs/dfi/gtest/src/dyn_type_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -108,3 +108,15 @@ TEST_F(DynTypeErrorInjectionTestSuite, ParseEnumTypeErrors) { ASSERT_NE(0, rc); celix_err_printErrors(stderr, nullptr, nullptr); } + +TEST_F(DynTypeErrorInjectionTestSuite, AllocateErrors) { + celix_autoptr(dyn_type) type = NULL; + int rc = 0; + rc = dynType_parseWithStr("#v1=0;#v2=1;E", NULL, NULL, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_calloc((void*)dynType_alloc, 0, nullptr); + void* buf = nullptr; + rc = dynType_alloc(type, &buf); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error allocating memory for type 'E'", celix_err_popLastError()); +} diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index 0599d8f5a..97f9b7380 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -326,6 +326,13 @@ TEST_F(DynTypeTests, ComplexTypeMissingClosingBrace) { ASSERT_STREQ("Error parsing complex type, expected '}'", celix_err_popLastError()); } +TEST_F(DynTypeTests, ComplexTypeWithMoreNamesThanFields) { + dyn_type *type = NULL; + auto rc = dynType_parseWithStr(R"({II a b c})", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error parsing complex type, expected '}'", celix_err_popLastError()); +} + TEST_F(DynTypeTests, SchemaEndsWithoutNullTerminator) { dyn_type *type = NULL; //ends with '-' @@ -365,8 +372,14 @@ TEST_F(DynTypeTests, MetaInfoMissingValue) { TEST_F(DynTypeTests, ParseNestedTypeFailed) { dyn_type *type = NULL; + int rc; + //missing name + rc = dynType_parseWithStr(R"(T={DD a b};)", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + ASSERT_STREQ("Parsed empty name", celix_err_popLastError()); + //missing '=' - auto rc = dynType_parseWithStr(R"(Ttype)", nullptr, nullptr, &type); + rc = dynType_parseWithStr(R"(Ttype)", nullptr, nullptr, &type); ASSERT_EQ(3, rc); celix_err_printErrors(stderr, nullptr, nullptr); @@ -383,8 +396,13 @@ TEST_F(DynTypeTests, ParseNestedTypeFailed) { TEST_F(DynTypeTests, ParseReferenceFailed) { dyn_type *type = NULL; + int rc; + // missing name + rc = dynType_parseWithStr(R"(Ttype={DD a b};l;)", nullptr, nullptr, &type); + ASSERT_NE(0, rc); + ASSERT_STREQ("Parsed empty name", celix_err_popLastError()); //missing ';' - auto rc = dynType_parseWithStr(R"(Ttype={DD a b};ltype)", nullptr, nullptr, &type); + rc = dynType_parseWithStr(R"(Ttype={DD a b};ltype)", nullptr, nullptr, &type); ASSERT_EQ(3, rc); celix_err_printErrors(stderr, nullptr, nullptr); //missing ';' diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 0edb9a726..a2d69475c 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -427,10 +427,7 @@ static void dynType_clear(dyn_type* type) { while (entry != NULL) { tmp = entry; entry = TAILQ_NEXT(entry, entries); - if (tmp->type != NULL) { - dynType_destroy(tmp->type); - tmp->type = NULL; - } + dynType_destroy(tmp->type); free(tmp); } @@ -499,21 +496,19 @@ static void dynType_clearTypedPointer(dyn_type* type) { } int dynType_alloc(const dyn_type* type, void** bufLoc) { - int status = OK; + const dyn_type* current = type; - if (type->type == DYN_TYPE_REF) { - status = dynType_alloc(type->ref.ref, bufLoc); - } else { - void* inst = calloc(1, type->ffiType->size); - if (inst != NULL) { - *bufLoc = inst; - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for type '%c'", type->descriptor); - } + while (current->type == DYN_TYPE_REF) { + current = current->ref.ref; + } + void* inst = calloc(1, current->ffiType->size); + if (inst == NULL) { + celix_err_pushf("Error allocating memory for type '%c'", current->descriptor); + return MEM_ERROR; } + *bufLoc = inst; - return status; + return OK; } diff --git a/libs/dfi/src/dyn_type_common.c b/libs/dfi/src/dyn_type_common.c index 1005fddf3..385b14f78 100644 --- a/libs/dfi/src/dyn_type_common.c +++ b/libs/dfi/src/dyn_type_common.c @@ -53,10 +53,6 @@ dyn_type* dynType_findType(dyn_type *type, char *name) { ffi_type * dynType_ffiType(dyn_type * type) { if (type->type == DYN_TYPE_REF) { - if (type->ref.ref == NULL) { - celix_err_pushf("Error. Ref for %s is not (yet) initialized", type->name); - return NULL; - } return type->ref.ref->ffiType; } return type->ffiType; From 78b7d2d81f509a1a0faa5d7f263fda2a71007bfc Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 7 Jan 2024 19:46:19 +0800 Subject: [PATCH 18/62] Refactor dynType_sequence_alloc and dynType_sequence_reserve. --- libs/dfi/gtest/src/dyn_type_ei_tests.cc | 52 ++++++++++++++++ libs/dfi/gtest/src/dyn_type_tests.cpp | 79 +++++++++++++++++++++---- libs/dfi/src/dyn_type.c | 57 +++++++++--------- 3 files changed, 146 insertions(+), 42 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc index 432d8ac25..02738eb57 100644 --- a/libs/dfi/gtest/src/dyn_type_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -33,9 +33,11 @@ class DynTypeErrorInjectionTestSuite : public ::testing::Test { } ~DynTypeErrorInjectionTestSuite() override { + celix_ei_expect_realloc(nullptr, 0, nullptr); celix_ei_expect_strdup(nullptr, 0, nullptr); celix_ei_expect_calloc(nullptr, 0, nullptr); celix_ei_expect_fmemopen(nullptr, 0, nullptr); + celix_err_resetErrors(); } // delete other constructors and assign operators DynTypeErrorInjectionTestSuite(DynTypeErrorInjectionTestSuite const&) = delete; @@ -120,3 +122,53 @@ TEST_F(DynTypeErrorInjectionTestSuite, AllocateErrors) { ASSERT_NE(0, rc); ASSERT_STREQ("Error allocating memory for type 'E'", celix_err_popLastError()); } + +TEST_F(DynTypeErrorInjectionTestSuite, SequenceAllocateError) { + struct double_sequence { + uint32_t cap; + uint32_t len; + double* buf; + }; + + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("[D", NULL, NULL, &type); + ASSERT_EQ(0, rc); + + struct double_sequence *seq = NULL; + rc = dynType_alloc(type, (void **)&seq); + ASSERT_EQ(0, rc); + ASSERT_TRUE(seq != NULL); + celix_ei_expect_calloc((void*)dynType_sequence_alloc, 0, nullptr); + rc = dynType_sequence_alloc(type, seq, 1); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error allocating memory for buf", celix_err_popLastError()); + + dynType_free(type, seq); + dynType_destroy(type); +} + +TEST_F(DynTypeErrorInjectionTestSuite, SequenceReserveError) { + struct double_sequence { + uint32_t cap; + uint32_t len; + double* buf; + }; + + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("[D", NULL, NULL, &type); + ASSERT_EQ(0, rc); + + struct double_sequence *seq = NULL; + rc = dynType_alloc(type, (void **)&seq); + ASSERT_EQ(0, rc); + ASSERT_TRUE(seq != NULL); + celix_ei_expect_realloc((void*)dynType_sequence_reserve, 0, nullptr); + rc = dynType_sequence_reserve(type, seq, 1); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error allocating memory for buf", celix_err_popLastError()); + + dynType_free(type, seq); + dynType_destroy(type); +} diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index 97f9b7380..f3cb50790 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -249,7 +249,7 @@ TEST_F(DynTypeTests, SequenceWithPointerTest) { struct val val; double c; double d; - long e; + int64_t e; }; struct item_sequence { @@ -260,7 +260,7 @@ TEST_F(DynTypeTests, SequenceWithPointerTest) { dyn_type *type = NULL; int rc = 0; - rc = dynType_parseWithStr("Tval={DD a b};Titem={Jtlval;DDJ a text val c d e};**[Litem;", NULL, NULL, &type); + rc = dynType_parseWithStr("Tval={DD a b};Titem={Jtlval;DDJ a text val c d e};[Litem;", NULL, NULL, &type); ASSERT_EQ(0, rc); struct item_sequence *seq = NULL; @@ -268,22 +268,75 @@ TEST_F(DynTypeTests, SequenceWithPointerTest) { ASSERT_EQ(0, rc); ASSERT_TRUE(seq != NULL); + rc = dynType_sequence_alloc(type, seq, 1); + ASSERT_EQ(0, rc); + struct item **loc = NULL; + rc = dynType_sequence_increaseLengthAndReturnLastLoc(type, seq, (void **)&loc); + ASSERT_EQ(0, rc); + ASSERT_EQ(loc, &seq->buf[0]); + ASSERT_EQ(sizeof(struct item), dynType_size(dynType_typedPointer_getTypedType(dynType_sequence_itemType(type)))); + rc = dynType_alloc(dynType_typedPointer_getTypedType(dynType_sequence_itemType(type)), (void**)loc); + ASSERT_EQ(0, rc); + ASSERT_EQ(seq->buf[0], *loc); dynType_free(type, seq); - /* + rc = dynType_sequence_alloc(type, nullptr, 1); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error null sequence", celix_err_popLastError()); + dynType_destroy(type); +} - struct item_sequence *items = (struct item_sequence *) calloc(1,sizeof(struct item_sequence)); - items->buf = (struct item **) calloc(2, sizeof(struct item *)); - items->cap = 2; - items->len = 2; - items->buf[0] = (struct item *)calloc(1, sizeof(struct item)); - items->buf[0]->text = strdup("boe"); - items->buf[1] = (struct item *)calloc(1, sizeof(struct item)); - items->buf[1]->text = strdup("boe2"); - dynType_free(type, items); - */ +TEST_F(DynTypeTests, SequenceReserve) { + + struct val { + double a; + double b; + }; + + struct item { + int64_t a; + const char *text; + struct val val; + double c; + double d; + int64_t e; + }; + + struct item_sequence { + uint32_t cap; + uint32_t len; + struct item **buf; + }; + + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("Tval={DD a b};Titem={Jtlval;DDJ a text val c d e};[Litem;", NULL, NULL, &type); + ASSERT_EQ(0, rc); + + struct item_sequence *seq = NULL; + + // sequence buffer should be zeroed + rc = dynType_alloc(type, (void **)&seq); + ASSERT_EQ(0, rc); + ASSERT_TRUE(seq != NULL); + rc = dynType_sequence_reserve(type, seq, 2); + ASSERT_EQ(0, rc); + ASSERT_EQ(nullptr, seq->buf[0]); + ASSERT_EQ(nullptr, seq->buf[1]); + ASSERT_EQ(2, seq->cap); + ASSERT_EQ(0, seq->len); + + // no need to expand capacity + rc = dynType_sequence_reserve(type, seq, 1); + ASSERT_EQ(0, rc); + dynType_free(type, seq); + + // try to reverse for null seq + rc = dynType_sequence_reserve(type, nullptr, 2); + ASSERT_NE(0, rc); + ASSERT_STREQ("Error null sequence", celix_err_popLastError()); dynType_destroy(type); } diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index a2d69475c..e629900b5 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -580,44 +580,43 @@ void dynType_sequence_init(const dyn_type* type, void* inst) { int dynType_sequence_alloc(const dyn_type* type, void* inst, uint32_t cap) { assert(type->type == DYN_TYPE_SEQUENCE); - int status = OK; struct generic_sequence* seq = inst; - if (seq != NULL) { - size_t size = dynType_size(type->sequence.itemType); - seq->buf = calloc(cap, size); - if (seq->buf != NULL) { - seq->cap = cap; - seq->len = 0; - } else { - seq->cap = 0; - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for buf"); - } - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for seq"); + if (seq == NULL) { + celix_err_pushf("Error null sequence"); + return ERROR; } - return status; + size_t size = dynType_size(type->sequence.itemType); + seq->buf = calloc(cap, size); + if (seq->buf == NULL) { + seq->cap = 0; + celix_err_pushf("Error allocating memory for buf"); + return MEM_ERROR; + } + seq->cap = cap; + seq->len = 0; + return OK; } int dynType_sequence_reserve(const dyn_type* type, void* inst, uint32_t cap) { assert(type->type == DYN_TYPE_SEQUENCE); int status = OK; struct generic_sequence* seq = inst; - if (seq != NULL && seq->cap < cap) { - size_t size = dynType_size(type->sequence.itemType); - seq->buf = realloc(seq->buf, (size_t)(cap * size)); - if (seq->buf != NULL) { - seq->cap = cap; - } else { - seq->cap = 0; - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for buf"); - } - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for seq"); + if (seq == NULL) { + celix_err_pushf("Error null sequence"); + return ERROR; + } + if (seq->cap >= cap) { + return OK; + } + size_t size = dynType_size(type->sequence.itemType); + seq->buf = realloc(seq->buf, (size_t)(cap * size)); + if (seq->buf == NULL) { + seq->cap = 0; + celix_err_pushf("Error allocating memory for buf"); + return MEM_ERROR; } + memset(seq->buf+seq->cap*size, 0, (cap-seq->cap)*size); + seq->cap = cap; return status; } From 84b45788ee1200abcdf72b4171b38d6d4152ad4e Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 8 Jan 2024 21:35:11 +0800 Subject: [PATCH 19/62] Refactor other dynType_sequence methods. --- libs/dfi/gtest/src/dyn_type_tests.cpp | 48 +++++++++++++++++++++++++++ libs/dfi/include/dyn_type.h | 2 +- libs/dfi/src/dyn_type.c | 43 ++++++++++-------------- libs/dfi/src/json_serializer.c | 2 +- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index f3cb50790..cd7e266a4 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -268,6 +268,7 @@ TEST_F(DynTypeTests, SequenceWithPointerTest) { ASSERT_EQ(0, rc); ASSERT_TRUE(seq != NULL); + dynType_sequence_init(type, seq); rc = dynType_sequence_alloc(type, seq, 1); ASSERT_EQ(0, rc); struct item **loc = NULL; @@ -341,6 +342,53 @@ TEST_F(DynTypeTests, SequenceReserve) { dynType_destroy(type); } +TEST_F(DynTypeTests, FillSequenceTest) { + struct double_sequence { + uint32_t cap; + uint32_t len; + double* buf; + }; + + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("[D", NULL, NULL, &type); + ASSERT_EQ(0, rc); + + struct double_sequence *seq = NULL; + rc = dynType_alloc(type, (void **)&seq); + ASSERT_EQ(0, rc); + ASSERT_TRUE(seq != NULL); + + rc = dynType_sequence_alloc(type, seq, 1); + ASSERT_EQ(0, rc); + + void* loc; + rc = dynType_sequence_increaseLengthAndReturnLastLoc(type, seq, (void **)&loc); + ASSERT_EQ(0, rc); + + double val = 2.0; + dynType_simple_setValue(dynType_sequence_itemType(type), loc, &val); + ASSERT_EQ(val, seq->buf[0]); + + rc = dynType_sequence_increaseLengthAndReturnLastLoc(type, seq, (void **)&loc); + ASSERT_NE(0, rc); + ASSERT_STREQ("Cannot increase sequence length beyond capacity (1)", celix_err_popLastError()); + + rc = dynType_sequence_reserve(type, seq, 2); + ASSERT_EQ(0, rc); + rc = dynType_sequence_locForIndex(type, seq, 1, (void **)&loc); + ASSERT_NE(0, rc); + ASSERT_STREQ("Requesting index (1) outsize defined length (1) but within capacity", celix_err_popLastError()); + + + rc = dynType_sequence_locForIndex(type, seq, 2, (void **)&loc); + ASSERT_NE(0, rc); + ASSERT_STREQ("Requested index (2) is greater than capacity (2) of sequence", celix_err_popLastError()); + + dynType_free(type, seq); + dynType_destroy(type); +} + TEST_F(DynTypeTests, EnumTest) { dyn_type *type = NULL; int rc = 0; diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 635004b27..7ae474719 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -311,7 +311,7 @@ CELIX_DFI_EXPORT int dynType_sequence_reserve(const dyn_type* type, void* inst, * @return 0 if successful. * @retval 1 if the index is out of bounds. */ -CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, int index, void** valLoc); +CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, uint32_t index, void** valLoc); /** * @brief Increase the length of the sequence by one and return the value location for the last element. diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index e629900b5..474009205 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -651,9 +651,11 @@ static void dynType_deepFree(const dyn_type* type, void* loc, bool alsoDeleteSel case DYN_TYPE_SIMPLE: //nop break; +//LCOV_EXCL_START default: - celix_err_pushf("Unexpected switch case. cannot free dyn type %c\n", type->descriptor); + assert(0 && "Unexpected switch case. cannot free dyn type"); break; +//LCOV_EXCL_STOP } if (alsoDeleteSelf) { @@ -666,7 +668,7 @@ static void dynType_freeSequenceType(const dyn_type* type, void* seqLoc) { struct generic_sequence* seq = seqLoc; const dyn_type* itemType = dynType_sequence_itemType(type); void* itemLoc = NULL; - int i; + uint32_t i; for (i = 0; i < seq->len; ++i) { dynType_sequence_locForIndex(type, seqLoc, i, &itemLoc); dynType_deepFree(itemType, itemLoc, false); @@ -688,50 +690,39 @@ uint32_t dynType_sequence_length(const void *seqLoc) { return seq->len; } -int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, int index, void** out) { +int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, uint32_t index, void** out) { assert(type->type == DYN_TYPE_SEQUENCE); - int status = OK; - struct generic_sequence* seq = seqLoc; size_t itemSize = dynType_size(type->sequence.itemType); if (index >= seq->cap) { - status = ERROR; - celix_err_pushf("Requested index (%i) is greater than capacity (%u) of sequence", index, seq->cap); + celix_err_pushf("Requested index (%u) is greater than capacity (%u) of sequence", index, seq->cap); + return ERROR; } if (index >= seq->len) { - status = ERROR; - celix_err_pushf("Requesting index (%i) outsize defined length (%u) but within capacity", index, seq->len); + celix_err_pushf("Requesting index (%u) outsize defined length (%u) but within capacity", index, seq->len); + return ERROR; } - if (status == OK) { - char* valLoc = seq->buf + (index * itemSize); - (*out) = valLoc; - } + char* valLoc = seq->buf + (index * itemSize); + (*out) = valLoc; - return status; + return OK; } int dynType_sequence_increaseLengthAndReturnLastLoc(const dyn_type* type, void* seqLoc, void** valLoc) { assert(type->type == DYN_TYPE_SEQUENCE); - int status = OK; struct generic_sequence* seq = seqLoc; - int lastIndex = seq->len; - if (seq->len < seq->cap) { - seq->len += 1; - } else { - status = ERROR; + uint32_t lastIndex = seq->len; + if (seq->len >= seq->cap) { celix_err_pushf("Cannot increase sequence length beyond capacity (%u)", seq->cap); + return ERROR; } - - if (status == OK) { - status = dynType_sequence_locForIndex(type, seqLoc, lastIndex, valLoc); - } - - return status; + seq->len += 1; + return dynType_sequence_locForIndex(type, seqLoc, lastIndex, valLoc); } const dyn_type* dynType_sequence_itemType(const dyn_type* type) { diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index 34d4f577b..a98e3ea85 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -437,7 +437,7 @@ static int jsonSerializer_writeSequence(const dyn_type *type, void *input, json_ const dyn_type *itemType = dynType_sequence_itemType(type); uint32_t len = dynType_sequence_length(input); - int i = 0; + uint32_t i = 0; void *itemLoc = NULL; json_t *item = NULL; for (i = 0; i < len; i += 1) { From e84ac213d2dd6a7f03bb303ac535d70cf9464d73 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 9 Jan 2024 20:54:21 +0800 Subject: [PATCH 20/62] Make double-reference work for dynType, and improve dynType_print robustness and coverage. --- libs/dfi/gtest/src/dyn_type_ei_tests.cc | 18 ++++++++ libs/dfi/gtest/src/dyn_type_tests.cpp | 55 +++++++++++++++++++++++-- libs/dfi/src/dyn_type.c | 53 +++++++++++++----------- libs/dfi/src/dyn_type_common.c | 4 +- 4 files changed, 102 insertions(+), 28 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc index 02738eb57..c52bd3e05 100644 --- a/libs/dfi/gtest/src/dyn_type_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -172,3 +172,21 @@ TEST_F(DynTypeErrorInjectionTestSuite, SequenceReserveError) { dynType_free(type, seq); dynType_destroy(type); } + +TEST_F(DynTypeErrorInjectionTestSuite, TextAllocateError) { + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("t", NULL, NULL, &type); + ASSERT_EQ(0, rc); + ASSERT_EQ(DYN_TYPE_TEXT, dynType_type(type)); + + char** val = nullptr; + rc = dynType_alloc(type, (void**)&val); + ASSERT_EQ(0, rc); + celix_ei_expect_strdup((void*)dynType_text_allocAndInit, 0, nullptr); + rc = dynType_text_allocAndInit(type, val, "test"); + ASSERT_NE(0, rc); + ASSERT_STREQ("Cannot allocate memory for string", celix_err_popLastError()); + dynType_free(type, val); + dynType_destroy(type); +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index cd7e266a4..b714aa7ae 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ - #include "gtest/gtest.h" +#include "celix_stdio_cleanup.h" +#include "celix_stdlib_cleanup.h" extern "C" { #include "dyn_common.h" @@ -30,6 +31,7 @@ extern "C" { //printf("\n-- example %s with descriptor string '%s' --\n", exName, descriptorStr); int status = dynType_parseWithStr(descriptorStr, exName, NULL, &type); ASSERT_EQ(0, status); + ASSERT_STREQ(exName, dynType_getName(type)); //MEM check, to try to ensure no mem leaks/corruptions occur. int i; @@ -82,11 +84,17 @@ class DynTypeTests : public ::testing::Test { #define EX15 "Tsample={jDD time val1 val2};Tresult={jDlsample; time result sample};Lresult;" #define EX16 "Tpoi={BDD id lat lon};Lpoi;" #define EX17 "{#v1=0;#v2=1;E#v1=9;#v2=10;E enum1 enum2}" +#define EX18 "Ttext=t;ltext;" +#define EX19 "Tsample={DD vala valb};Tref=lsample;;lref;" +#define EX20 "TINTEGER=I;Tsample={DlINTEGER; vala valb};Tref=lsample;;lref;" #define CREATE_EXAMPLES_TEST(DESC) \ TEST_F(DynTypeTests, ParseTestExample ## DESC) { \ runTest(DESC, #DESC); \ - } + } \ + TEST_F(DynTypeTests, ParseTestExampleNoName ## DESC) { \ + runTest(DESC, nullptr); \ + } CREATE_EXAMPLES_TEST(EX1) CREATE_EXAMPLES_TEST(EX2) @@ -101,10 +109,13 @@ CREATE_EXAMPLES_TEST(EX10) CREATE_EXAMPLES_TEST(EX11) CREATE_EXAMPLES_TEST(EX12) CREATE_EXAMPLES_TEST(EX13) -//CREATE_EXAMPLES_TEST(EX14) +CREATE_EXAMPLES_TEST(EX14) CREATE_EXAMPLES_TEST(EX15) CREATE_EXAMPLES_TEST(EX16) CREATE_EXAMPLES_TEST(EX17) +CREATE_EXAMPLES_TEST(EX18) +CREATE_EXAMPLES_TEST(EX19) +CREATE_EXAMPLES_TEST(EX20) TEST_F(DynTypeTests, ParseRandomGarbageTest) { /* @@ -223,6 +234,17 @@ TEST_F(DynTypeTests, MetaInfoTest) { ASSERT_EQ(0, rc); + auto entries = dynType_metaEntries(type); + struct meta_entry* entry = NULL; + size_t nbEntries = 0; + TAILQ_FOREACH(entry, entries, entries) { + nbEntries++; + ASSERT_STREQ("a", entry->name); + ASSERT_STREQ("t", entry->value); + } + ASSERT_EQ(1, nbEntries); + + const char *val = NULL; val = dynType_getMetaInfo(type, "a"); ASSERT_TRUE(val != NULL); @@ -399,6 +421,33 @@ TEST_F(DynTypeTests, EnumTest) { dynType_destroy(type); } + +TEST_F(DynTypeTests, PrintNullTypeTest) { + celix_autofree char* buf = nullptr; + size_t bufSize = 0; + celix_autoptr(FILE) result = open_memstream(&buf, &bufSize); + dynType_print(nullptr, result); + fflush(result); + ASSERT_STREQ("invalid type\n", buf); +} + +TEST_F(DynTypeTests, TextTest) { + dyn_type *type = NULL; + int rc = 0; + rc = dynType_parseWithStr("t", NULL, NULL, &type); + ASSERT_EQ(0, rc); + ASSERT_EQ(DYN_TYPE_TEXT, dynType_type(type)); + + char** val = nullptr; + rc = dynType_alloc(type, (void**)&val); + ASSERT_EQ(0, rc); + rc = dynType_text_allocAndInit(type, val, "test"); + ASSERT_EQ(0, rc); + ASSERT_STREQ("test", *val); + dynType_free(type, val); + dynType_destroy(type); +} + TEST_F(DynTypeTests, NrOfEntriesTest) { dyn_type *type = NULL; int rc = dynType_parseWithStr("{DD}", NULL, NULL, &type); diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 474009205..be1363d68 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -59,11 +59,12 @@ static void dynType_printSequence(const char* name, const dyn_type* type, int de static void dynType_printSimple(const char* name, const dyn_type* type, int depth, FILE* stream); static void dynType_printEnum(const char* name, const dyn_type* type, int depth, FILE* stream); static void dynType_printTypedPointer(const char* name, const dyn_type* type, int depth, FILE* stream); +static void dynType_printText(const char* name, const dyn_type* type, int depth, FILE* stream); static void dynType_printDepth(int depth, FILE* stream); static void dynType_printTypes(const dyn_type* type, FILE* stream); -static void dynType_printComplexType(dyn_type* type, FILE* stream); -static void dynType_printSimpleType(dyn_type* type, FILE* stream); +static void dynType_printComplexType(const dyn_type* type, FILE* stream); +static void dynType_printSimpleType(const dyn_type* type, FILE* stream); static int dynType_parseText(FILE* stream, dyn_type* type); static int dynType_parseEnum(FILE* stream, dyn_type* type); @@ -534,7 +535,7 @@ const dyn_type* dynType_complex_dynTypeAt(const dyn_type* type, int index) { assert(type->type == DYN_TYPE_COMPLEX); assert(index >= 0); dyn_type* sub = type->complex.types[index]; - if (sub->type == DYN_TYPE_REF) { + while (sub->type == DYN_TYPE_REF) { sub = sub->ref.ref; } return sub; @@ -728,7 +729,7 @@ int dynType_sequence_increaseLengthAndReturnLastLoc(const dyn_type* type, void* const dyn_type* dynType_sequence_itemType(const dyn_type* type) { assert(type->type == DYN_TYPE_SEQUENCE); dyn_type *itemType = type->sequence.itemType; - if (itemType->type == DYN_TYPE_REF) { + while (itemType->type == DYN_TYPE_REF) { itemType = itemType->ref.ref; } return itemType; @@ -835,7 +836,7 @@ static unsigned short dynType_getOffset(const dyn_type* type, int index) { size_t dynType_size(const dyn_type* type) { const dyn_type* rType = type; - if (type->type == DYN_TYPE_REF) { + while (rType->type == DYN_TYPE_REF) { rType = type->ref.ref; } return rType->ffiType->size; @@ -848,7 +849,7 @@ int dynType_type(const dyn_type* type) { const dyn_type* dynType_typedPointer_getTypedType(const dyn_type* type) { assert(type->type == DYN_TYPE_TYPED_POINTER); dyn_type* typedType = type->typedPointer.typedType; - if (typedType->type == DYN_TYPE_REF) { + while (typedType->type == DYN_TYPE_REF) { typedType = typedType->ref.ref; } return typedType; @@ -857,22 +858,16 @@ const dyn_type* dynType_typedPointer_getTypedType(const dyn_type* type) { int dynType_text_allocAndInit(const dyn_type* type, void* textLoc, const char* value) { assert(type->type == DYN_TYPE_TEXT); - int status = 0; const char* str = strdup(value); char const** loc = textLoc; - if (str != NULL) { - *loc = str; - } else { - status = ERROR; + if (str == NULL) { celix_err_pushf("Cannot allocate memory for string"); + return MEM_ERROR; } - return status; + *loc = str; + return OK; } - - - - void dynType_print(const dyn_type *type, FILE *stream) { if (type != NULL) { dynType_printTypes(type, stream); @@ -893,9 +888,10 @@ static void dynType_printDepth(int depth, FILE *stream) { static void dynType_printAny(const char* name, const dyn_type* type, int depth, FILE *stream) { const dyn_type* toPrint = type; - if (toPrint->type == DYN_TYPE_REF) { + while (toPrint->type == DYN_TYPE_REF) { toPrint = toPrint->ref.ref; } + name = (name != NULL) ? name : "(unnamed)"; switch(toPrint->type) { case DYN_TYPE_COMPLEX : dynType_printComplex(name, toPrint, depth, stream); @@ -909,9 +905,14 @@ static void dynType_printAny(const char* name, const dyn_type* type, int depth, case DYN_TYPE_TYPED_POINTER : dynType_printTypedPointer(name, toPrint, depth, stream); break; + case DYN_TYPE_TEXT: + dynType_printText(name, toPrint, depth, stream); + break; +//LCOV_EXCL_START default : - fprintf(stream, "TODO Unsupported type %d\n", toPrint->type); + assert(0 && "Unexpected switch case. cannot print dyn type"); break; +//LCOV_EXCL_STOP } } @@ -990,6 +991,12 @@ static void dynType_printTypedPointer(const char* name, const dyn_type* type, in dynType_printAny(subName, type->typedPointer.typedType, depth + 1, stream); } +static void dynType_printText(const char* name, const dyn_type* type, int depth, FILE* stream) { + dynType_printDepth(depth, stream); + fprintf(stream, "%s: text type, size is %zu, alignment is %i, descriptor is '%c'.\n", + name, type->ffiType->size, type->ffiType->alignment, type->descriptor); +} + static void dynType_printTypes(const dyn_type* type, FILE* stream) { dyn_type* parent = type->parent; @@ -1006,7 +1013,7 @@ static void dynType_printTypes(const dyn_type* type, FILE* stream) { struct type_entry* entry = NULL; TAILQ_FOREACH(entry, &type->nestedTypesHead, entries) { dyn_type* toPrint = entry->type; - if (toPrint->type == DYN_TYPE_REF) { + while (toPrint->type == DYN_TYPE_REF) { toPrint = toPrint->ref.ref; } @@ -1018,7 +1025,7 @@ static void dynType_printTypes(const dyn_type* type, FILE* stream) { dynType_printSimpleType(toPrint, stream); break; default : - printf("TODO Print Type\n"); + printf("TODO Print Type %d\n", toPrint->type); break; } } @@ -1040,7 +1047,7 @@ static void dynType_printTypes(const dyn_type* type, FILE* stream) { } } -static void dynType_printComplexType(dyn_type *type, FILE *stream) { +static void dynType_printComplexType(const dyn_type *type, FILE *stream) { fprintf(stream, "type '%s': complex type, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); struct complex_type_entry *entry = NULL; @@ -1051,7 +1058,7 @@ static void dynType_printComplexType(dyn_type *type, FILE *stream) { fprintf(stream, "}\n"); } -static void dynType_printSimpleType(dyn_type *type, FILE *stream) { - fprintf(stream, "\ttype '%s': simple type, size is %zu, alignment is %i, descriptor is '%c'\n", type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); +static void dynType_printSimpleType(const dyn_type *type, FILE *stream) { + fprintf(stream, "type '%s': simple type, size is %zu, alignment is %i, descriptor is '%c'\n", type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); } diff --git a/libs/dfi/src/dyn_type_common.c b/libs/dfi/src/dyn_type_common.c index 385b14f78..885da8064 100644 --- a/libs/dfi/src/dyn_type_common.c +++ b/libs/dfi/src/dyn_type_common.c @@ -52,8 +52,8 @@ dyn_type* dynType_findType(dyn_type *type, char *name) { } ffi_type * dynType_ffiType(dyn_type * type) { - if (type->type == DYN_TYPE_REF) { - return type->ref.ref->ffiType; + while (type->type == DYN_TYPE_REF) { + type = type->ref.ref; } return type->ffiType; } From a6e22f762fcb4d583a4b60d837299aed2a8d3f79 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 9 Jan 2024 21:00:20 +0800 Subject: [PATCH 21/62] Upgrade to conan 1.62. --- .github/workflows/coverage.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8ed1777d8..76601a2e0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -19,7 +19,7 @@ jobs: - name: Install conan and lcov run: | sudo apt-get install -yq --no-install-recommends lcov - sudo pip install conan==1.59.0 + sudo pip install conan==1.62.0 - name: Setup Conan Profile run: | conan profile new default --detect diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index df2994f1f..d5b87a93f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Install build dependencies run: | - sudo pip install -U conan==1.59.0 + sudo pip install -U conan==1.62.0 sudo apt-get install -yq --no-install-recommends ninja-build - name: Setup Conan Profile env: From 1ff4bc15cc7b494bc537f3bc0d61563a5d72be64 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 10 Jan 2024 15:13:39 +0800 Subject: [PATCH 22/62] Skip typedefs for reference when printing a dynType. --- libs/dfi/src/dyn_type.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index be1363d68..84e3d5f51 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -938,8 +938,8 @@ static void dynType_printComplex(const char* name, const dyn_type* type, int dep static void dynType_printSequence(const char* name, const dyn_type* type, int depth, FILE* stream) { dynType_printDepth(depth, stream); - fprintf(stream, "sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", - type->ffiType->size, type->ffiType->alignment, type->descriptor); + fprintf(stream, "%s: sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", + name, type->ffiType->size, type->ffiType->alignment, type->descriptor); dynType_printDepth(depth + 1, stream); fprintf(stream, "cap: simple type, size is %zu, alignment is %i.\n", @@ -998,16 +998,8 @@ static void dynType_printText(const char* name, const dyn_type* type, int depth, } static void dynType_printTypes(const dyn_type* type, FILE* stream) { - - dyn_type* parent = type->parent; - struct type_entry* pentry = NULL; - while (parent != NULL) { - TAILQ_FOREACH(pentry, &parent->nestedTypesHead, entries) { - if (pentry->type == type) { - return; - } - } - parent = parent->parent; + if (type->type == DYN_TYPE_REF) { + return; } struct type_entry* entry = NULL; From 0bde38e56e27d5c76596853c71b3d2a3c58c77c2 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 10 Jan 2024 15:31:05 +0800 Subject: [PATCH 23/62] Only Skip typedefs already printed when printing a dynType. --- libs/dfi/src/dyn_type.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 84e3d5f51..0ad3e330d 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -938,8 +938,8 @@ static void dynType_printComplex(const char* name, const dyn_type* type, int dep static void dynType_printSequence(const char* name, const dyn_type* type, int depth, FILE* stream) { dynType_printDepth(depth, stream); - fprintf(stream, "%s: sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", - name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + fprintf(stream, "sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", + type->ffiType->size, type->ffiType->alignment, type->descriptor); dynType_printDepth(depth + 1, stream); fprintf(stream, "cap: simple type, size is %zu, alignment is %i.\n", @@ -999,7 +999,20 @@ static void dynType_printText(const char* name, const dyn_type* type, int depth, static void dynType_printTypes(const dyn_type* type, FILE* stream) { if (type->type == DYN_TYPE_REF) { - return; + const dyn_type* real = type->ref.ref; + while (real->type == DYN_TYPE_REF) { + real = real->ref.ref; + } + dyn_type* parent = type->parent; + struct type_entry* pentry = NULL; + while (parent != NULL) { + TAILQ_FOREACH(pentry, &parent->nestedTypesHead, entries) { + if (pentry->type == real) { + return; + } + } + parent = parent->parent; + } } struct type_entry* entry = NULL; From e3562a5fd40f80b3640c283b713315446866d55d Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 11 Jan 2024 20:09:05 +0800 Subject: [PATCH 24/62] Apply const qualifier to dynFunction. --- libs/dfi/include/dyn_function.h | 24 ++++++------- libs/dfi/src/dyn_function.c | 64 ++++++++++++++++----------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index c9f34d5d4..3f8002840 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -66,7 +66,7 @@ enum dyn_function_argument_meta { * @retval 1 If there is not enough memory to create dyn_function_type. * @retval 2 Errors other than out-of-memory. */ -CELIX_DFI_EXPORT int dynFunction_parse(FILE *descriptorStream, struct types_head *refTypes, dyn_function_type **dynFunc); +CELIX_DFI_EXPORT int dynFunction_parse(FILE* descriptorStream, struct types_head* refTypes, dyn_function_type** dynFunc); /** * @brief Creates a dyn_function_type according to the given function descriptor string. @@ -82,14 +82,14 @@ CELIX_DFI_EXPORT int dynFunction_parse(FILE *descriptorStream, struct types_head * @retval 1 If there is not enough memory to create dyn_function_type. * @retval 2 Errors other than out-of-memory. */ -CELIX_DFI_EXPORT int dynFunction_parseWithStr(const char *descriptor, struct types_head *refTypes, dyn_function_type **dynFunc); +CELIX_DFI_EXPORT int dynFunction_parseWithStr(const char* descriptor, struct types_head* refTypes, dyn_function_type** dynFunc); /** * @brief Returns the number of arguments of the given dynamic function type instance. * @param[in] dynFunc The dynamic type instance for function. * @return The number of arguments. */ -CELIX_DFI_EXPORT int dynFunction_nrOfArguments(dyn_function_type *dynFunc); +CELIX_DFI_EXPORT int dynFunction_nrOfArguments(const dyn_function_type* dynFunc); /** * @brief Returns the argument type for the given argument index. @@ -97,7 +97,7 @@ CELIX_DFI_EXPORT int dynFunction_nrOfArguments(dyn_function_type *dynFunc); * @param[in] argumentNr The argument index. * @return The argument type. */ -CELIX_DFI_EXPORT const dyn_type* dynFunction_argumentTypeForIndex(dyn_function_type* dynFunc, int argumentNr); +CELIX_DFI_EXPORT const dyn_type* dynFunction_argumentTypeForIndex(const dyn_function_type* dynFunc, int argumentNr); /** * @brief Returns the argument meta for the given argument index. @@ -105,20 +105,20 @@ CELIX_DFI_EXPORT const dyn_type* dynFunction_argumentTypeForIndex(dyn_function_t * @param[in] argumentNr The argument index. * @return The argument meta. */ -CELIX_DFI_EXPORT enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(dyn_function_type *dynFunc, int argumentNr); +CELIX_DFI_EXPORT enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(const dyn_function_type* dynFunc, int argumentNr); /** * @brief Returns the return value type for the given dynamic function type instance. * @param[in] dynFunc The dynamic type instance for function. * @return The return value type. */ -CELIX_DFI_EXPORT const dyn_type* dynFunction_returnType(dyn_function_type *dynFunction); +CELIX_DFI_EXPORT const dyn_type* dynFunction_returnType(const dyn_function_type* dynFunction); /** * @brief Destroys the given dynamic function type instance. * @param[in] dynFunc The dynamic type instance for function. */ -CELIX_DFI_EXPORT void dynFunction_destroy(dyn_function_type *dynFunc); +CELIX_DFI_EXPORT void dynFunction_destroy(dyn_function_type* dynFunc); CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_function_type, dynFunction_destroy); @@ -130,7 +130,7 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(dyn_function_type, dynFunction_destroy); * @param[in] argValues The argument values. * @return 0 */ -CELIX_DFI_EXPORT int dynFunction_call(dyn_function_type *dynFunc, void(*fn)(void), void *returnValue, void **argValues); +CELIX_DFI_EXPORT int dynFunction_call(const dyn_function_type* dynFunc, void(*fn)(void), void* returnValue, void** argValues); /** * @brief Creates a closure for the given dynamic function type instance. @@ -142,7 +142,7 @@ CELIX_DFI_EXPORT int dynFunction_call(dyn_function_type *dynFunc, void(*fn)(void * @retval 1 If there is not enough memory to create the closure. * @retval 2 Errors other than out-of-memory. */ -CELIX_DFI_EXPORT int dynFunction_createClosure(dyn_function_type *func, void (*bind)(void *, void **, void*), void *userData, void(**fn)(void)); +CELIX_DFI_EXPORT int dynFunction_createClosure(dyn_function_type* func, void (*bind)(void*, void**, void*), void* userData, void(**fn)(void)); /** * @brief Returns the function pointer for the given dynamic function type instance. @@ -150,18 +150,18 @@ CELIX_DFI_EXPORT int dynFunction_createClosure(dyn_function_type *func, void (*b * @param[out] fn The function pointer. * @return 0 If successful, 1 if the dynamic function type instance has no function pointer. */ -CELIX_DFI_EXPORT int dynFunction_getFnPointer(dyn_function_type *func, void (**fn)(void)); +CELIX_DFI_EXPORT int dynFunction_getFnPointer(const dyn_function_type* func, void (**fn)(void)); /** * Returns whether the function has a return type. * Will return false if return is void. */ -CELIX_DFI_EXPORT bool dynFunction_hasReturn(dyn_function_type *dynFunction); +CELIX_DFI_EXPORT bool dynFunction_hasReturn(const dyn_function_type* dynFunction); /** * @brief Returns the name of the given dynamic function type instance. */ -CELIX_DFI_EXPORT const char* dynFunction_getName(const dyn_function_type *func); +CELIX_DFI_EXPORT const char* dynFunction_getName(const dyn_function_type* func); #ifdef __cplusplus } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index de045797a..2365eb3f9 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -31,13 +31,13 @@ static const int MEM_ERROR = 1; static const int PARSE_ERROR = 2; static const int ERROR = 2; -static int dynFunction_initCif(dyn_function_type *dynFunc); -static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descriptor); -static void dynFunction_ffiBind(ffi_cif *cif, void *ret, void *args[], void *userData); +static int dynFunction_initCif(dyn_function_type* dynFunc); +static int dynFunction_parseDescriptor(dyn_function_type* dynFunc, FILE* descriptor); +static void dynFunction_ffiBind(ffi_cif* cif, void* ret, void* args[], void* userData); -int dynFunction_parse(FILE *descriptor, struct types_head *refTypes, dyn_function_type **out) { +int dynFunction_parse(FILE* descriptor, struct types_head* refTypes, dyn_function_type** out) { int status = OK; - dyn_function_type *dynFunc = NULL; + dyn_function_type* dynFunc = NULL; dynFunc = calloc(1, sizeof(*dynFunc)); @@ -58,9 +58,9 @@ int dynFunction_parse(FILE *descriptor, struct types_head *refTypes, dyn_functio } if (status == OK) { - dyn_function_argument_type *arg = NULL; + dyn_function_argument_type* arg = NULL; TAILQ_FOREACH(arg, &dynFunc->arguments, entries) { - const char *meta = dynType_getMetaInfo(arg->type, "am"); + const char* meta = dynType_getMetaInfo(arg->type, "am"); if (meta == NULL) { arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; } else if (strcmp(meta, "handle") == 0) { @@ -88,9 +88,9 @@ int dynFunction_parse(FILE *descriptor, struct types_head *refTypes, dyn_functio return status; } -int dynFunction_parseWithStr(const char *descriptor, struct types_head *refTypes, dyn_function_type **out) { +int dynFunction_parseWithStr(const char* descriptor, struct types_head* refTypes, dyn_function_type** out) { int status = OK; - FILE *stream = fmemopen((char *)descriptor, strlen(descriptor) + 1, "r"); + FILE* stream = fmemopen((char* )descriptor, strlen(descriptor) + 1, "r"); if (stream != NULL) { status = dynFunction_parse(stream, refTypes, out); fclose(stream); @@ -101,9 +101,9 @@ int dynFunction_parseWithStr(const char *descriptor, struct types_head *refTypes return status; } -static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descriptor) { +static int dynFunction_parseDescriptor(dyn_function_type* dynFunc, FILE* descriptor) { int status = OK; - char *name = NULL; + char* name = NULL; status = dynCommon_parseName(descriptor, &name); @@ -121,13 +121,13 @@ static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descrip int nextChar = fgetc(descriptor); int index = 0; - dyn_type *type = NULL; + dyn_type* type = NULL; char argName[32]; while (nextChar != ')' && status == 0) { ungetc(nextChar, descriptor); type = NULL; - dyn_function_argument_type *arg = NULL; + dyn_function_argument_type* arg = NULL; status = dynType_parse(descriptor, NULL, dynFunc->refTypes, &type); if (status == OK) { @@ -159,9 +159,9 @@ static int dynFunction_parseDescriptor(dyn_function_type *dynFunc, FILE *descrip return status; } -enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(dyn_function_type *dynFunc, int argumentNr) { +enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(const dyn_function_type* dynFunc, int argumentNr) { enum dyn_function_argument_meta result = 0; - dyn_function_argument_type *arg = NULL; + dyn_function_argument_type* arg = NULL; int index = 0; TAILQ_FOREACH(arg, &dynFunc->arguments, entries) { if (index == argumentNr) { @@ -174,11 +174,11 @@ enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(dyn_function_ty } -static int dynFunction_initCif(dyn_function_type *dynFunc) { +static int dynFunction_initCif(dyn_function_type* dynFunc) { int status = 0; unsigned int nargs = 0; - dyn_function_argument_type *entry = NULL; + dyn_function_argument_type* entry = NULL; TAILQ_FOREACH(entry, &dynFunc->arguments, entries) { nargs +=1; } @@ -189,8 +189,8 @@ static int dynFunction_initCif(dyn_function_type *dynFunc) { dynFunc->ffiArguments[entry->index] = dynType_ffiType(entry->type); } - ffi_type **args = dynFunc->ffiArguments; - ffi_type *returnType = dynType_ffiType(dynFunc->funcReturn); + ffi_type** args = dynFunc->ffiArguments; + ffi_type* returnType = dynType_ffiType(dynFunc->funcReturn); int ffiResult = ffi_prep_cif(&dynFunc->cif, FFI_DEFAULT_ABI, nargs, returnType, args); if (ffiResult != FFI_OK) { @@ -200,7 +200,7 @@ static int dynFunction_initCif(dyn_function_type *dynFunc) { return status; } -void dynFunction_destroy(dyn_function_type *dynFunc) { +void dynFunction_destroy(dyn_function_type* dynFunc) { if (dynFunc != NULL) { if (dynFunc->funcReturn != NULL) { dynType_destroy(dynFunc->funcReturn); @@ -215,8 +215,8 @@ void dynFunction_destroy(dyn_function_type *dynFunc) { free(dynFunc->ffiArguments); } - dyn_function_argument_type *entry = NULL; - dyn_function_argument_type *tmp = NULL; + dyn_function_argument_type* entry = NULL; + dyn_function_argument_type* tmp = NULL; entry = TAILQ_FIRST(&dynFunc->arguments); while (entry != NULL) { if (entry->name != NULL) { @@ -232,17 +232,17 @@ void dynFunction_destroy(dyn_function_type *dynFunc) { } } -int dynFunction_call(dyn_function_type *dynFunc, void(*fn)(void), void *returnValue, void **argValues) { - ffi_call(&dynFunc->cif, fn, returnValue, argValues); +int dynFunction_call(const dyn_function_type* dynFunc, void(*fn)(void), void* returnValue, void** argValues) { + ffi_call((ffi_cif*)&dynFunc->cif, fn, returnValue, argValues); return 0; } -static void dynFunction_ffiBind(ffi_cif *cif, void *ret, void *args[], void *userData) { - dyn_function_type *dynFunc = userData; +static void dynFunction_ffiBind(ffi_cif* cif, void* ret, void* args[], void* userData) { + dyn_function_type* dynFunc = userData; dynFunc->bind(dynFunc->userData, args, ret); } -int dynFunction_createClosure(dyn_function_type *dynFunc, void (*bind)(void *, void **, void*), void *userData, void(**out)(void)) { +int dynFunction_createClosure(dyn_function_type* dynFunc, void (*bind)(void*, void**, void*), void* userData, void(**out)(void)) { int status = 0; void (*fn)(void); dynFunc->ffiClosure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&fn); @@ -265,7 +265,7 @@ int dynFunction_createClosure(dyn_function_type *dynFunc, void (*bind)(void *, v return status; } -int dynFunction_getFnPointer(dyn_function_type *dynFunc, void (**fn)(void)) { +int dynFunction_getFnPointer(const dyn_function_type* dynFunc, void (**fn)(void)) { int status = 0; if (dynFunc != NULL && dynFunc->fn != NULL) { (*fn) = dynFunc->fn; @@ -275,7 +275,7 @@ int dynFunction_getFnPointer(dyn_function_type *dynFunc, void (**fn)(void)) { return status; } -int dynFunction_nrOfArguments(dyn_function_type *dynFunc) { +int dynFunction_nrOfArguments(const dyn_function_type* dynFunc) { int count = 0; dyn_function_argument_type *entry = NULL; TAILQ_FOREACH(entry, &dynFunc->arguments, entries) { @@ -284,7 +284,7 @@ int dynFunction_nrOfArguments(dyn_function_type *dynFunc) { return count; } -const dyn_type* dynFunction_argumentTypeForIndex(dyn_function_type* dynFunc, int argumentNr) { +const dyn_type* dynFunction_argumentTypeForIndex(const dyn_function_type* dynFunc, int argumentNr) { dyn_type* result = NULL; int index = 0; dyn_function_argument_type* entry = NULL; @@ -298,11 +298,11 @@ const dyn_type* dynFunction_argumentTypeForIndex(dyn_function_type* dynFunc, int return result; } -const dyn_type* dynFunction_returnType(dyn_function_type *dynFunction) { +const dyn_type* dynFunction_returnType(const dyn_function_type *dynFunction) { return dynFunction->funcReturn; } -bool dynFunction_hasReturn(dyn_function_type* dynFunction) { +bool dynFunction_hasReturn(const dyn_function_type* dynFunction) { const dyn_type* t = dynFunction_returnType(dynFunction); return t->descriptor != 'V'; } From bfc57bdd14d77a81a4392f4f294b55f19d127e02 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 12 Jan 2024 23:05:15 +0800 Subject: [PATCH 25/62] Refactor dynFunction_parse and dynFunction_parseWithStr. --- libs/dfi/error_injector/CMakeLists.txt | 3 +- libs/dfi/error_injector/ffi/CMakeLists.txt | 26 ++++++ libs/dfi/error_injector/ffi/include/ffi_ei.h | 33 ++++++++ libs/dfi/error_injector/ffi/src/ffi_ei.cc | 29 +++++++ libs/dfi/gtest/CMakeLists.txt | 4 +- libs/dfi/gtest/src/dyn_function_ei_tests.cc | 63 +++++++++++++++ libs/dfi/gtest/src/dyn_function_tests.cpp | 27 ++++--- libs/dfi/src/dyn_function.c | 83 +++++++++----------- 8 files changed, 205 insertions(+), 63 deletions(-) create mode 100644 libs/dfi/error_injector/ffi/CMakeLists.txt create mode 100644 libs/dfi/error_injector/ffi/include/ffi_ei.h create mode 100644 libs/dfi/error_injector/ffi/src/ffi_ei.cc create mode 100644 libs/dfi/gtest/src/dyn_function_ei_tests.cc diff --git a/libs/dfi/error_injector/CMakeLists.txt b/libs/dfi/error_injector/CMakeLists.txt index f6d2a84c9..bc63f6796 100644 --- a/libs/dfi/error_injector/CMakeLists.txt +++ b/libs/dfi/error_injector/CMakeLists.txt @@ -15,4 +15,5 @@ # specific language governing permissions and limitations # under the License. -add_subdirectory(dfi) \ No newline at end of file +add_subdirectory(dfi) +add_subdirectory(ffi) diff --git a/libs/dfi/error_injector/ffi/CMakeLists.txt b/libs/dfi/error_injector/ffi/CMakeLists.txt new file mode 100644 index 000000000..e58a7f3ac --- /dev/null +++ b/libs/dfi/error_injector/ffi/CMakeLists.txt @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +add_library(ffi_ei STATIC src/ffi_ei.cc) + +target_include_directories(ffi_ei PUBLIC include) +target_link_libraries(ffi_ei PUBLIC Celix::error_injector libffi::libffi) + +target_link_options(ffi_ei INTERFACE + LINKER:--wrap,ffi_prep_cif +) +add_library(Celix::ffi_ei ALIAS ffi_ei) diff --git a/libs/dfi/error_injector/ffi/include/ffi_ei.h b/libs/dfi/error_injector/ffi/include/ffi_ei.h new file mode 100644 index 000000000..58a3d8f38 --- /dev/null +++ b/libs/dfi/error_injector/ffi/include/ffi_ei.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef CELIX_FFI_EI_H +#define CELIX_FFI_EI_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "celix_error_injector.h" +#include + +CELIX_EI_DECLARE(ffi_prep_cif, ffi_status); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_FFI_EI_H diff --git a/libs/dfi/error_injector/ffi/src/ffi_ei.cc b/libs/dfi/error_injector/ffi/src/ffi_ei.cc new file mode 100644 index 000000000..5320b3d37 --- /dev/null +++ b/libs/dfi/error_injector/ffi/src/ffi_ei.cc @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ffi_ei.h" + +extern "C" { +ffi_status __real_ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, ffi_type *rtype, ffi_type **atypes); +CELIX_EI_DEFINE(ffi_prep_cif, ffi_status) +ffi_status __wrap_ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, ffi_type *rtype, ffi_type **atypes) { + CELIX_EI_IMPL(ffi_prep_cif); + return __real_ffi_prep_cif(cif, abi, nargs, rtype, atypes); +} +} \ No newline at end of file diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 7a1d7aaff..7c2a7bfd9 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -18,7 +18,7 @@ add_executable(test_dfi src/dyn_example_functions.c - src/dyn_type_tests.cpp + src/dyn_type_tests.cpp src/dyn_function_tests.cpp src/dyn_closure_tests.cpp src/dyn_interface_tests.cpp @@ -44,12 +44,14 @@ if (EI_TESTS) src/dyn_common_ei_tests.cc src/dyn_type_ei_tests.cc src/dyn_message_ei_tests.cc + src/dyn_function_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut Celix::malloc_ei Celix::stdio_ei Celix::string_ei + Celix::ffi_ei GTest::gtest GTest::gtest_main ) add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) diff --git a/libs/dfi/gtest/src/dyn_function_ei_tests.cc b/libs/dfi/gtest/src/dyn_function_ei_tests.cc new file mode 100644 index 000000000..89e03e7c5 --- /dev/null +++ b/libs/dfi/gtest/src/dyn_function_ei_tests.cc @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "dyn_function.h" +#include "dyn_example_functions.h" + +#include "celix_err.h" +#include "ffi_ei.h" +#include "malloc_ei.h" +#include "stdio_ei.h" + +#include +#include +#include +#include + +class DynFunctionErrorInjectionTestSuite : public ::testing::Test { +public: + DynFunctionErrorInjectionTestSuite() = default; + ~DynFunctionErrorInjectionTestSuite() override { + celix_ei_expect_fmemopen(nullptr, 0, nullptr); + celix_ei_expect_ffi_prep_cif(nullptr, 0, FFI_OK); + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_err_resetErrors(); + } +}; + +TEST_F(DynFunctionErrorInjectionTestSuite, ParseError) { + dyn_function_type *dynFunc = nullptr; + int rc; + celix_ei_expect_calloc((void*)dynFunction_parse, 0, nullptr); + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error allocating memory for dyn function", celix_err_popLastError()); + + celix_ei_expect_ffi_prep_cif((void*)dynFunction_parse, 1, FFI_BAD_TYPEDEF); + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error initializing cif 1", celix_err_popLastError()); + + celix_ei_expect_fmemopen((void*)dynFunction_parseWithStr, 0, nullptr); + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc); + std::string result = "Error creating mem stream for descriptor string. "; + result += strerror(ENOMEM); + EXPECT_STREQ(result.c_str(), celix_err_popLastError()); +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 0af571e42..da43d3a8e 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -17,13 +17,14 @@ * under the License. */ -#include "gtest/gtest.h" #include "dyn_example_functions.h" - #include "dyn_common.h" #include "dyn_function.h" +#include "celix_err.h" + #include +#include #define INVALID_FUNC_DESCRIPTOR "example$[D)V"//$ is an invalid symbol, missing ( #define INVALID_FUNC_TYPE_DESCRIPTOR "example(H)A"//H and A are invalid types @@ -33,7 +34,9 @@ class DynFunctionTests : public ::testing::Test { public: DynFunctionTests() { } - ~DynFunctionTests() override = default; + ~DynFunctionTests() override { + celix_err_resetErrors(); + } }; @@ -251,20 +254,18 @@ TEST_F(DynFunctionTests, DynFuncTest5) { EXPECT_TRUE(func_test5()); } -extern "C" { -static bool func_invalid() { +TEST_F(DynFunctionTests, InvalidDynFuncTest) { + dyn_function_type *dynFunc = nullptr; int rc1 = dynFunction_parseWithStr(INVALID_FUNC_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc1); + EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); + EXPECT_STREQ("Expected '(' token got '$'", celix_err_popLastError()); dynFunc = nullptr; int rc2 = dynFunction_parseWithStr(INVALID_FUNC_TYPE_DESCRIPTOR, nullptr, &dynFunc); - return rc1 != 0 && rc2 != 0; -} -} - -TEST_F(DynFunctionTests, InvalidDynFuncTest) { - //NOTE only using libffi with extern C, because combining libffi with EXPECT_*/ASSERT_* call leads to - //corrupted memory. Note that libffi is a function for interfacing with C not C++ - EXPECT_TRUE(func_invalid()); + EXPECT_NE(0, rc2); + EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); + EXPECT_STREQ("Error unsupported type 'H'", celix_err_popLastError()); } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index 2365eb3f9..aed58f850 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -37,67 +37,53 @@ static void dynFunction_ffiBind(ffi_cif* cif, void* ret, void* args[], void* use int dynFunction_parse(FILE* descriptor, struct types_head* refTypes, dyn_function_type** out) { int status = OK; - dyn_function_type* dynFunc = NULL; - - dynFunc = calloc(1, sizeof(*dynFunc)); + celix_autoptr(dyn_function_type) dynFunc = calloc(1, sizeof(*dynFunc)); + if (dynFunc == NULL) { + celix_err_pushf("Error allocating memory for dyn function"); + return MEM_ERROR; + } - if (dynFunc != NULL) { - TAILQ_INIT(&dynFunc->arguments); - dynFunc->refTypes = refTypes; - status = dynFunction_parseDescriptor(dynFunc, descriptor); - if (status == 0) { - int rc = dynFunction_initCif(dynFunc); - if (rc != 0) { - celix_err_pushf("Error initializing cif"); - status = ERROR; - } - } - } else { - celix_err_pushf("Error allocating memory for dyn function\n"); - status = MEM_ERROR; + TAILQ_INIT(&dynFunc->arguments); + dynFunc->refTypes = refTypes; + status = dynFunction_parseDescriptor(dynFunc, descriptor); + if (status != OK) { + celix_err_pushf("Error parsing descriptor"); + return status; + } + int rc = dynFunction_initCif(dynFunc); + if (rc != 0) { + return ERROR; } - if (status == OK) { - dyn_function_argument_type* arg = NULL; - TAILQ_FOREACH(arg, &dynFunc->arguments, entries) { - const char* meta = dynType_getMetaInfo(arg->type, "am"); - if (meta == NULL) { - arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; - } else if (strcmp(meta, "handle") == 0) { - arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__HANDLE; - } else if (strcmp(meta, "pre") == 0) { - arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT; - } else if (strcmp(meta, "out") == 0) { - arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__OUTPUT; - } else { - arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; - } + dyn_function_argument_type* arg = NULL; + TAILQ_FOREACH(arg, &dynFunc->arguments, entries) { + const char* meta = dynType_getMetaInfo(arg->type, "am"); + if (meta == NULL) { + arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; + } else if (strcmp(meta, "handle") == 0) { + arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__HANDLE; + } else if (strcmp(meta, "pre") == 0) { + arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT; + } else if (strcmp(meta, "out") == 0) { + arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__OUTPUT; + } else { + arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; } } - - if (status == OK) { - *out = dynFunc; - } else { - celix_err_pushf("Failed to Create dyn function"); - if (dynFunc != NULL) { - dynFunction_destroy(dynFunc); - } - } - - return status; + *out = celix_steal_ptr(dynFunc); + return OK; } int dynFunction_parseWithStr(const char* descriptor, struct types_head* refTypes, dyn_function_type** out) { int status = OK; FILE* stream = fmemopen((char* )descriptor, strlen(descriptor) + 1, "r"); - if (stream != NULL) { - status = dynFunction_parse(stream, refTypes, out); - fclose(stream); - } else { - status = MEM_ERROR; + if (stream == NULL) { celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); + return MEM_ERROR; } + status = dynFunction_parse(stream, refTypes, out); + fclose(stream); return status; } @@ -194,6 +180,7 @@ static int dynFunction_initCif(dyn_function_type* dynFunc) { int ffiResult = ffi_prep_cif(&dynFunc->cif, FFI_DEFAULT_ABI, nargs, returnType, args); if (ffiResult != FFI_OK) { + celix_err_pushf("Error initializing cif %d", ffiResult); status = 1; } From 1c370b1e4c45e3c768ce57c0b0ede60aeb4da3f6 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sat, 13 Jan 2024 20:15:45 +0800 Subject: [PATCH 26/62] Add more tests for dynFunction_parse. --- libs/dfi/gtest/src/dyn_function_ei_tests.cc | 7 ++++++- libs/dfi/src/dyn_function.c | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_function_ei_tests.cc b/libs/dfi/gtest/src/dyn_function_ei_tests.cc index 89e03e7c5..ced99dea3 100644 --- a/libs/dfi/gtest/src/dyn_function_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_function_ei_tests.cc @@ -52,7 +52,12 @@ TEST_F(DynFunctionErrorInjectionTestSuite, ParseError) { celix_ei_expect_ffi_prep_cif((void*)dynFunction_parse, 1, FFI_BAD_TYPEDEF); rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); EXPECT_NE(0, rc); - EXPECT_STREQ("Error initializing cif 1", celix_err_popLastError()); + EXPECT_STREQ("Error ffi_prep_cif 1", celix_err_popLastError()); + + celix_ei_expect_calloc((void*)dynFunction_parse, 1, nullptr, 4); + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error allocating memory for ffi args", celix_err_popLastError()); celix_ei_expect_fmemopen((void*)dynFunction_parseWithStr, 0, nullptr); rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index aed58f850..2d8644c60 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -50,9 +50,9 @@ int dynFunction_parse(FILE* descriptor, struct types_head* refTypes, dyn_functio celix_err_pushf("Error parsing descriptor"); return status; } - int rc = dynFunction_initCif(dynFunc); - if (rc != 0) { - return ERROR; + status = dynFunction_initCif(dynFunc); + if (status != OK) { + return status; } dyn_function_argument_type* arg = NULL; @@ -161,8 +161,6 @@ enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(const dyn_funct static int dynFunction_initCif(dyn_function_type* dynFunc) { - int status = 0; - unsigned int nargs = 0; dyn_function_argument_type* entry = NULL; TAILQ_FOREACH(entry, &dynFunc->arguments, entries) { @@ -170,6 +168,10 @@ static int dynFunction_initCif(dyn_function_type* dynFunc) { } dynFunc->ffiArguments = calloc(nargs, sizeof(ffi_type*)); + if (dynFunc->ffiArguments == NULL) { + celix_err_push("Error allocating memory for ffi args"); + return MEM_ERROR; + } TAILQ_FOREACH(entry, &dynFunc->arguments, entries) { dynFunc->ffiArguments[entry->index] = dynType_ffiType(entry->type); @@ -180,11 +182,11 @@ static int dynFunction_initCif(dyn_function_type* dynFunc) { int ffiResult = ffi_prep_cif(&dynFunc->cif, FFI_DEFAULT_ABI, nargs, returnType, args); if (ffiResult != FFI_OK) { - celix_err_pushf("Error initializing cif %d", ffiResult); - status = 1; + celix_err_pushf("Error ffi_prep_cif %d", ffiResult); + return ERROR; } - return status; + return OK; } void dynFunction_destroy(dyn_function_type* dynFunc) { From 86a90957bced2e41540ed53b18983833e2807cb8 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 14 Jan 2024 15:26:42 +0800 Subject: [PATCH 27/62] Code cleanup and testing coverage improvement of dynFunction. 1. Apply early return to dynFunction_parseDescriptor. 2. Release resources in strict reverse order in dynFunction_destroy. 3. Improve error handling of dynFunction_createClosure. 4. More test cases to improve coverage. --- libs/dfi/error_injector/ffi/CMakeLists.txt | 2 + libs/dfi/error_injector/ffi/include/ffi_ei.h | 4 + libs/dfi/error_injector/ffi/src/ffi_ei.cc | 15 +++ libs/dfi/gtest/CMakeLists.txt | 1 + libs/dfi/gtest/src/dyn_function_ei_tests.cc | 51 ++++++++ libs/dfi/gtest/src/dyn_function_tests.cpp | 81 ++++++------ libs/dfi/src/dyn_function.c | 127 +++++++++---------- 7 files changed, 170 insertions(+), 111 deletions(-) diff --git a/libs/dfi/error_injector/ffi/CMakeLists.txt b/libs/dfi/error_injector/ffi/CMakeLists.txt index e58a7f3ac..c79a429a1 100644 --- a/libs/dfi/error_injector/ffi/CMakeLists.txt +++ b/libs/dfi/error_injector/ffi/CMakeLists.txt @@ -22,5 +22,7 @@ target_link_libraries(ffi_ei PUBLIC Celix::error_injector libffi::libffi) target_link_options(ffi_ei INTERFACE LINKER:--wrap,ffi_prep_cif + LINKER:--wrap,ffi_closure_alloc + LINKER:--wrap,ffi_prep_closure_loc ) add_library(Celix::ffi_ei ALIAS ffi_ei) diff --git a/libs/dfi/error_injector/ffi/include/ffi_ei.h b/libs/dfi/error_injector/ffi/include/ffi_ei.h index 58a3d8f38..f1dd1fb0b 100644 --- a/libs/dfi/error_injector/ffi/include/ffi_ei.h +++ b/libs/dfi/error_injector/ffi/include/ffi_ei.h @@ -27,6 +27,10 @@ extern "C" { CELIX_EI_DECLARE(ffi_prep_cif, ffi_status); +CELIX_EI_DECLARE(ffi_closure_alloc, void*); + +CELIX_EI_DECLARE(ffi_prep_closure_loc, ffi_status); + #ifdef __cplusplus } #endif diff --git a/libs/dfi/error_injector/ffi/src/ffi_ei.cc b/libs/dfi/error_injector/ffi/src/ffi_ei.cc index 5320b3d37..f34dc5b69 100644 --- a/libs/dfi/error_injector/ffi/src/ffi_ei.cc +++ b/libs/dfi/error_injector/ffi/src/ffi_ei.cc @@ -26,4 +26,19 @@ ffi_status __wrap_ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, ff CELIX_EI_IMPL(ffi_prep_cif); return __real_ffi_prep_cif(cif, abi, nargs, rtype, atypes); } + +void *__real_ffi_closure_alloc(size_t size, void **code); +CELIX_EI_DEFINE(ffi_closure_alloc, void*) +void *__wrap_ffi_closure_alloc(size_t size, void **code) { + CELIX_EI_IMPL(ffi_closure_alloc); + return __real_ffi_closure_alloc(size, code); +} + +ffi_status __real_ffi_prep_closure_loc(ffi_closure *closure, ffi_cif *cif, void (*fun)(ffi_cif *, void *, void **, void *), void *user_data, void *codeloc); +CELIX_EI_DEFINE(ffi_prep_closure_loc, ffi_status) +ffi_status __wrap_ffi_prep_closure_loc(ffi_closure *closure, ffi_cif *cif, void (*fun)(ffi_cif *, void *, void **, void *), void *user_data, void *codeloc) { + CELIX_EI_IMPL(ffi_prep_closure_loc); + return __real_ffi_prep_closure_loc(closure, cif, fun, user_data, codeloc); +} + } \ No newline at end of file diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 7c2a7bfd9..ded190e6b 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -52,6 +52,7 @@ if (EI_TESTS) Celix::stdio_ei Celix::string_ei Celix::ffi_ei + Celix::asprintf_ei GTest::gtest GTest::gtest_main ) add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) diff --git a/libs/dfi/gtest/src/dyn_function_ei_tests.cc b/libs/dfi/gtest/src/dyn_function_ei_tests.cc index ced99dea3..1327c5f8c 100644 --- a/libs/dfi/gtest/src/dyn_function_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_function_ei_tests.cc @@ -21,6 +21,7 @@ #include "dyn_example_functions.h" #include "celix_err.h" +#include "asprintf_ei.h" #include "ffi_ei.h" #include "malloc_ei.h" #include "stdio_ei.h" @@ -34,6 +35,7 @@ class DynFunctionErrorInjectionTestSuite : public ::testing::Test { public: DynFunctionErrorInjectionTestSuite() = default; ~DynFunctionErrorInjectionTestSuite() override { + celix_ei_expect_asprintf(nullptr, 0, -1); celix_ei_expect_fmemopen(nullptr, 0, nullptr); celix_ei_expect_ffi_prep_cif(nullptr, 0, FFI_OK); celix_ei_expect_calloc(nullptr, 0, nullptr); @@ -65,4 +67,53 @@ TEST_F(DynFunctionErrorInjectionTestSuite, ParseError) { std::string result = "Error creating mem stream for descriptor string. "; result += strerror(ENOMEM); EXPECT_STREQ(result.c_str(), celix_err_popLastError()); + + celix_ei_expect_asprintf((void*) dynFunction_parse, 1, -1, 3); + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); + EXPECT_STREQ("Error allocating argument name", celix_err_popLastError()); + + + celix_ei_expect_calloc((void*)dynFunction_parse, 1, nullptr, 3); + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); + EXPECT_STREQ("Error allocating arg", celix_err_popLastError()); +} + +class DynClosureErrorInjectionTestSuite : public ::testing::Test { +public: + DynClosureErrorInjectionTestSuite() = default; + ~DynClosureErrorInjectionTestSuite() override { + celix_ei_expect_ffi_prep_closure_loc(nullptr, 0, FFI_OK); + celix_ei_expect_ffi_closure_alloc(nullptr, 0, nullptr); + } +}; + + +static void example1_binding(void*, void* args[], void *out) { + int32_t a = *((int32_t *)args[0]); + int32_t b = *((int32_t *)args[1]); + int32_t c = *((int32_t *)args[2]); + int32_t *ret = (int32_t *)out; + *ret = a + b + c; +} + +TEST_F(DynClosureErrorInjectionTestSuite, CreatError) { + celix_autoptr(dyn_function_type) dynFunc = nullptr; + int rc; + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + ASSERT_EQ(0, rc); + + int32_t (*func)(int32_t a, int32_t b, int32_t c) = NULL; + celix_ei_expect_ffi_closure_alloc((void*)dynFunction_createClosure, 0, nullptr); + rc = dynFunction_createClosure(dynFunc, example1_binding, NULL, (void(**)(void))&func); + ASSERT_NE(0, rc); + EXPECT_NE(0, dynFunction_getFnPointer(dynFunc, (void(**)(void))&func)); + + celix_ei_expect_ffi_prep_closure_loc((void*)dynFunction_createClosure, 0, FFI_BAD_ABI); + rc = dynFunction_createClosure(dynFunc, example1_binding, NULL, (void(**)(void))&func); + ASSERT_NE(0, rc); + EXPECT_NE(0, dynFunction_getFnPointer(dynFunc, (void(**)(void))&func)); } \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index da43d3a8e..4dcb28f13 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -40,37 +40,29 @@ class DynFunctionTests : public ::testing::Test { }; +TEST_F(DynFunctionTests, DynFuncTest1) { + dyn_function_type *dynFunc = nullptr; + int rc; + void (*fp)(void) = (void (*)(void)) example1; -extern "C" { - static bool func_test1() { - dyn_function_type *dynFunc = nullptr; - int rc; - void (*fp)(void) = (void (*)(void)) example1; - - rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); - - ffi_sarg rVal = 0; - int32_t a = 2; - int32_t b = 4; - int32_t c = 8; - void *values[3]; - values[0] = &a; - values[1] = &b; - values[2] = &c; + rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); + ASSERT_EQ(0, rc); + EXPECT_TRUE(dynFunction_hasReturn(dynFunc)); - if (rc == 0) { - rc = dynFunction_call(dynFunc, fp, &rVal, values); - dynFunction_destroy(dynFunc); - } + ffi_sarg rVal = 0; + int32_t a = 2; + int32_t b = 4; + int32_t c = 8; + void *values[3]; + values[0] = &a; + values[1] = &b; + values[2] = &c; - return rc == 0 && rVal == 14; - } -} + rc = dynFunction_call(dynFunc, fp, &rVal, values); + dynFunction_destroy(dynFunc); -TEST_F(DynFunctionTests, DynFuncTest1) { - //NOTE only using libffi with extern C, because combining libffi with EXPECT_*/ASSERT_* call leads to - //corrupted memory. Note that libffi is a function for interfacing with C not C++ - EXPECT_TRUE(func_test1()); + EXPECT_EQ(0, rc); + EXPECT_EQ(14, rVal); } extern "C" { @@ -190,13 +182,14 @@ TEST_F(DynFunctionTests, DynFuncTest3) { EXPECT_TRUE(func_test3()); } -extern "C" { -static bool func_test4() { +TEST_F(DynFunctionTests, DynFuncTest4) { dyn_function_type *dynFunc = nullptr; void (*fp)(void) = (void(*)(void)) example4Func; int rc; rc = dynFunction_parseWithStr(EXAMPLE4_DESCRIPTOR, nullptr, &dynFunc); + ASSERT_EQ(0, rc); + EXPECT_FALSE(dynFunction_hasReturn(dynFunc)); double buf[4]; buf[0] = 1.1; @@ -208,19 +201,9 @@ static bool func_test4() { void *args[1]; args[0] = &seq; - if (rc == 0) { - rc = dynFunction_call(dynFunc, fp, nullptr, args); - dynFunction_destroy(dynFunc); - } - - return rc == 0; -} -} - -TEST_F(DynFunctionTests, DynFuncTest4) { - //NOTE only using libffi with extern C, because combining libffi with EXPECT_*/ASSERT_* call leads to - //corrupted memory. Note that libffi is a function for interfacing with C not C++ - EXPECT_TRUE(func_test4()); + rc = dynFunction_call(dynFunc, fp, nullptr, args); + dynFunction_destroy(dynFunc); + EXPECT_EQ(0, rc); } extern "C" { @@ -260,12 +243,24 @@ TEST_F(DynFunctionTests, InvalidDynFuncTest) { int rc1 = dynFunction_parseWithStr(INVALID_FUNC_DESCRIPTOR, nullptr, &dynFunc); EXPECT_NE(0, rc1); EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); - EXPECT_STREQ("Expected '(' token got '$'", celix_err_popLastError()); + EXPECT_STREQ("Error parsing, expected token '(' got '$' at position 8", celix_err_popLastError()); dynFunc = nullptr; int rc2 = dynFunction_parseWithStr(INVALID_FUNC_TYPE_DESCRIPTOR, nullptr, &dynFunc); EXPECT_NE(0, rc2); EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); EXPECT_STREQ("Error unsupported type 'H'", celix_err_popLastError()); + + dynFunc = nullptr; + int rc3 = dynFunction_parseWithStr("$xample(III)I", nullptr, &dynFunc); + EXPECT_NE(0, rc3); + EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); + EXPECT_STREQ("Parsed empty name", celix_err_popLastError()); + + dynFunc = nullptr; + int rc4 = dynFunction_parseWithStr("example(III", nullptr, &dynFunc); + EXPECT_NE(0, rc4); + EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); + EXPECT_STREQ("Error missing ')'", celix_err_popLastError()); } diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index 2d8644c60..a68f4b016 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -19,7 +19,9 @@ #include "dyn_function.h" #include "dyn_function_common.h" +#include "celix_cleanup.h" #include "celix_err.h" +#include "celix_stdlib_cleanup.h" #include #include @@ -77,7 +79,7 @@ int dynFunction_parse(FILE* descriptor, struct types_head* refTypes, dyn_functio int dynFunction_parseWithStr(const char* descriptor, struct types_head* refTypes, dyn_function_type** out) { int status = OK; - FILE* stream = fmemopen((char* )descriptor, strlen(descriptor) + 1, "r"); + FILE* stream = fmemopen((char* )descriptor, strlen(descriptor), "r"); if (stream == NULL) { celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); return MEM_ERROR; @@ -89,60 +91,53 @@ int dynFunction_parseWithStr(const char* descriptor, struct types_head* refTypes static int dynFunction_parseDescriptor(dyn_function_type* dynFunc, FILE* descriptor) { int status = OK; - char* name = NULL; - status = dynCommon_parseName(descriptor, &name); - - if (status == OK) { - dynFunc->name = name; + if ((status = dynCommon_parseName(descriptor, &dynFunc->name)) != OK) { + return status; } - if (status == OK) { - int c = fgetc(descriptor); - if ( c != '(') { - status = PARSE_ERROR; - celix_err_pushf("Expected '(' token got '%c'", c); - } + status = dynCommon_eatChar(descriptor, '('); + if (status != OK) { + return PARSE_ERROR; } int nextChar = fgetc(descriptor); int index = 0; - dyn_type* type = NULL; - char argName[32]; - while (nextChar != ')' && status == 0) { + while (nextChar != ')' && nextChar != EOF) { ungetc(nextChar, descriptor); - type = NULL; + celix_autoptr(dyn_type) type = NULL; + celix_autofree char* argName = NULL; dyn_function_argument_type* arg = NULL; - status = dynType_parse(descriptor, NULL, dynFunc->refTypes, &type); - if (status == OK) { - arg = calloc(1, sizeof(*arg)); - if (arg != NULL) { - arg->index = index; - arg->type = type; - snprintf(argName, 32, "arg%04i", index); - arg->name = strdup(argName); - - index += 1; - } else { - celix_err_pushf("Error allocating memory"); - status = MEM_ERROR; - } + if ((status = dynType_parse(descriptor, NULL, dynFunc->refTypes, &type)) != OK) { + return status; } - if (status == OK) { - TAILQ_INSERT_TAIL(&dynFunc->arguments, arg, entries); + if (asprintf(&argName, "arg%04i", index) == -1) { + celix_err_pushf("Error allocating argument name"); + return MEM_ERROR; } + arg = calloc(1, sizeof(*arg)); + if (arg == NULL) { + celix_err_pushf("Error allocating arg"); + return MEM_ERROR; + } + arg->index = index++; + arg->type = celix_steal_ptr(type); + arg->name = celix_steal_ptr(argName); + + TAILQ_INSERT_TAIL(&dynFunc->arguments, arg, entries); + nextChar = fgetc(descriptor); } - - if (status == 0) { - status = dynType_parse(descriptor, NULL, dynFunc->refTypes, &dynFunc->funcReturn); + if (nextChar != ')') { + celix_err_push("Error missing ')'"); + return PARSE_ERROR; } - - return status; + + return dynType_parse(descriptor, NULL, dynFunc->refTypes, &dynFunc->funcReturn); } enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(const dyn_function_type* dynFunc, int argumentNr) { @@ -191,22 +186,19 @@ static int dynFunction_initCif(dyn_function_type* dynFunc) { void dynFunction_destroy(dyn_function_type* dynFunc) { if (dynFunc != NULL) { - if (dynFunc->funcReturn != NULL) { - dynType_destroy(dynFunc->funcReturn); - } + // release resource in strict reverse order if (dynFunc->ffiClosure != NULL) { ffi_closure_free(dynFunc->ffiClosure); } - if (dynFunc->name != NULL) { - free(dynFunc->name); - } if (dynFunc->ffiArguments != NULL) { free(dynFunc->ffiArguments); } - + if (dynFunc->funcReturn != NULL) { + dynType_destroy(dynFunc->funcReturn); + } dyn_function_argument_type* entry = NULL; dyn_function_argument_type* tmp = NULL; - entry = TAILQ_FIRST(&dynFunc->arguments); + entry = TAILQ_FIRST(&dynFunc->arguments); while (entry != NULL) { if (entry->name != NULL) { free(entry->name); @@ -216,7 +208,9 @@ void dynFunction_destroy(dyn_function_type* dynFunc) { entry = TAILQ_NEXT(entry, entries); free(tmp); } - + if (dynFunc->name != NULL) { + free(dynFunc->name); + } free(dynFunc); } } @@ -231,37 +225,34 @@ static void dynFunction_ffiBind(ffi_cif* cif, void* ret, void* args[], void* use dynFunc->bind(dynFunc->userData, args, ret); } +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(ffi_closure, ffi_closure_free) + int dynFunction_createClosure(dyn_function_type* dynFunc, void (*bind)(void*, void**, void*), void* userData, void(**out)(void)) { - int status = 0; void (*fn)(void); - dynFunc->ffiClosure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&fn); - if (dynFunc->ffiClosure != NULL) { - int rc = ffi_prep_closure_loc(dynFunc->ffiClosure, &dynFunc->cif, dynFunction_ffiBind, dynFunc, fn); - if (rc != FFI_OK) { - status = 1; - } - } else { - status = 2; + celix_autoptr(ffi_closure) ffiClosure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&fn); + if (ffiClosure == NULL) { + return MEM_ERROR; } - - if (status == 0) { - dynFunc->userData = userData; - dynFunc->bind = bind; - dynFunc->fn = fn; - *out =fn; + int rc = ffi_prep_closure_loc(ffiClosure, &dynFunc->cif, dynFunction_ffiBind, dynFunc, fn); + if (rc != FFI_OK) { + return ERROR; } - return status; + dynFunc->ffiClosure = celix_steal_ptr(ffiClosure); + dynFunc->userData = userData; + dynFunc->bind = bind; + dynFunc->fn = fn; + *out =fn; + + return OK; } int dynFunction_getFnPointer(const dyn_function_type* dynFunc, void (**fn)(void)) { - int status = 0; - if (dynFunc != NULL && dynFunc->fn != NULL) { - (*fn) = dynFunc->fn; - } else { - status = 1; + if (dynFunc == NULL || dynFunc->fn == NULL) { + return ERROR; } - return status; + (*fn) = dynFunc->fn; + return OK; } int dynFunction_nrOfArguments(const dyn_function_type* dynFunc) { From 4c2b4d41e438442c93e9c91311f28af8eff37893 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 15 Jan 2024 22:01:44 +0800 Subject: [PATCH 28/62] Treat unknown complex member as error when parsing and refactor json deserialization. --- libs/dfi/gtest/CMakeLists.txt | 1 + libs/dfi/gtest/src/json_rpc_tests.cpp | 54 +----- .../dfi/gtest/src/json_serializer_ei_tests.cc | 53 ++++++ libs/dfi/gtest/src/json_serializer_tests.cpp | 19 ++ libs/dfi/include/json_serializer.h | 16 +- libs/dfi/src/json_serializer.c | 171 ++++++------------ 6 files changed, 141 insertions(+), 173 deletions(-) create mode 100644 libs/dfi/gtest/src/json_serializer_ei_tests.cc diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index ded190e6b..c780e3214 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -45,6 +45,7 @@ if (EI_TESTS) src/dyn_type_ei_tests.cc src/dyn_message_ei_tests.cc src/dyn_function_ei_tests.cc + src/json_serializer_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 35db8c53b..b98d9961b 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -879,54 +879,6 @@ extern "C" { free(result); - dynInterface_destroy(intf); - } - - void testResponseForwardCompatibility(void) { - dyn_interface_type *intf = nullptr; - FILE *desc = fopen("descriptors/example6.descriptor", "r"); - ASSERT_TRUE(desc != nullptr); - int rc = dynInterface_parse(desc, &intf); - 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), "cpt") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); - - struct tst_CptData *cptData{nullptr}; - void *out = &cptData; - - void *args[3]; - args[0] = nullptr; - args[1] = nullptr; - args[2] = &out; - int rsErrno = 0; - - //provider has more reply - rc = jsonRpc_handleReply(func, R"({"r":{"d":1.0, "t":"hello compatibility", "s":[1.0,2.0,3.0], "e":"v1", "e2":"v2"}})", args, &rsErrno); - EXPECT_EQ(0, rc); - EXPECT_EQ(0, rsErrno); - EXPECT_NE(cptData , nullptr); - EXPECT_EQ(cptData->d , 1.0); - EXPECT_STREQ(cptData->t , "hello compatibility"); - EXPECT_EQ(cptData->s.len , 3); - EXPECT_EQ(cptData->s.cap , 3); - EXPECT_EQ(cptData->s.buf[0] , 1.0); - EXPECT_EQ(cptData->s.buf[1] , 2.0); - EXPECT_EQ(cptData->s.buf[2] , 3.0); - EXPECT_EQ(cptData->e , v1); - free(cptData->t); - free(cptData->s.buf); - free(cptData); - dynInterface_destroy(intf); } } @@ -1035,8 +987,4 @@ TEST_F(JsonRpcTests, callTestConstChar) { TEST_F(JsonRpcTests, testRequestBackwardCompatibility) { testRequestBackwardCompatibility(); -} - -TEST_F(JsonRpcTests, testResponseForwardCompatibility) { - testResponseForwardCompatibility(); -} +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/json_serializer_ei_tests.cc b/libs/dfi/gtest/src/json_serializer_ei_tests.cc new file mode 100644 index 000000000..dfd18b8b0 --- /dev/null +++ b/libs/dfi/gtest/src/json_serializer_ei_tests.cc @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "json_serializer.h" +#include "dyn_type.h" +#include "celix_err.h" +#include "malloc_ei.h" + +#include + +class JsonSerializerErrorInjectionTestSuite : public ::testing::Test { +public: + JsonSerializerErrorInjectionTestSuite() = default; + ~JsonSerializerErrorInjectionTestSuite() override { + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_err_resetErrors(); + } +}; + +TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { + int rc; + dyn_type *type; + void *inst; + + //simple string + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr("t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + auto inputStr = R"("hello")"; + celix_ei_expect_calloc((void*) dynType_alloc, 0, nullptr); + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '\"hello\"'", celix_err_popLastError()); + EXPECT_STREQ("Error allocating memory for type 't'", celix_err_popLastError()); + dynType_destroy(type); +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index 6105928d3..0d924f71e 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -559,6 +559,25 @@ static void parseTests() { ASSERT_EQ(1, rc); celix_err_printErrors(stderr, nullptr, nullptr); dynType_destroy(type); + + // unknown member + rc = dynType_parseWithStr("{t a}", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"({"a":"hello", "b":"world"})"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_EQ(1, rc); + celix_err_printErrors(stderr, nullptr, nullptr); + dynType_destroy(type); + + //simple string + rc = dynType_parseWithStr("t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"("hello")"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_EQ(0, rc); + EXPECT_STREQ("hello", *(char**)inst); + dynType_free(type, inst); + dynType_destroy(type); } /*********** write example 1 ************************/ diff --git a/libs/dfi/include/json_serializer.h b/libs/dfi/include/json_serializer.h index 5d38b7ebc..1c4cb44c7 100644 --- a/libs/dfi/include/json_serializer.h +++ b/libs/dfi/include/json_serializer.h @@ -31,19 +31,21 @@ extern "C" { #endif /** - * @brief Deserialize a JSON string to a given type. + * @brief Deserialize a JSON string buffer to a given type. + * @note The string buffer doesn't need to be null-terminated. * * Caller is the owner of the out parameter and should release it using dynType_free. * * In case of an error, an error message is added to celix_err. * * @param[in] type The type to deserialize to. - * @param[in] input The JSON string to deserialize. - * @param[out] out The deserialized result. + * @param[in] input The JSON string buffer to deserialize. + * @param[in] length The length of the given JSON string buffer. + * @param[out] result The deserialized result. * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonSerializer_deserialize(dyn_type *type, const char *input, size_t length, void **result); +CELIX_DFI_EXPORT int jsonSerializer_deserialize(const dyn_type* type, const char* input, size_t length, void** result); /** * @brief Deserialize a JSON object to a given type. @@ -58,7 +60,7 @@ CELIX_DFI_EXPORT int jsonSerializer_deserialize(dyn_type *type, const char *inpu * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonSerializer_deserializeJson(const dyn_type *type, json_t *input, void **result); +CELIX_DFI_EXPORT int jsonSerializer_deserializeJson(const dyn_type* type, json_t* input, void** result); /** * @brief Serialize a given type to a JSON string. @@ -73,7 +75,7 @@ CELIX_DFI_EXPORT int jsonSerializer_deserializeJson(const dyn_type *type, json_t * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonSerializer_serialize(dyn_type *type, const void* input, char **output); +CELIX_DFI_EXPORT int jsonSerializer_serialize(const dyn_type* type, const void* input, char** output); /** * @brief Serialize a given type to a JSON object. @@ -88,7 +90,7 @@ CELIX_DFI_EXPORT int jsonSerializer_serialize(dyn_type *type, const void* input, * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonSerializer_serializeJson(const dyn_type *type, const void* input, json_t **out); +CELIX_DFI_EXPORT int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t** out); #ifdef __cplusplus } diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index a98e3ea85..da9953669 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -28,87 +28,67 @@ #include #include -static int jsonSerializer_createType(const dyn_type *type, json_t *object, void **result); -static int jsonSerializer_parseObject(const dyn_type *type, json_t *object, void *inst); -static int jsonSerializer_parseObjectMember(const dyn_type *type, const char *name, json_t *val, void *inst); -static int jsonSerializer_parseSequence(const dyn_type *seq, json_t *array, void *seqLoc); -static int jsonSerializer_parseAny(const dyn_type *type, void *input, json_t *val); -static int jsonSerializer_parseEnum(const dyn_type *type, const char* enum_name, int32_t *out); +static int jsonSerializer_createType(const dyn_type* type, json_t* object, void** result); +static int jsonSerializer_parseObject(const dyn_type* type, json_t* object, void* inst); +static int jsonSerializer_parseObjectMember(const dyn_type* type, const char* name, json_t* val, void* inst); +static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void* seqLoc); +static int jsonSerializer_parseAny(const dyn_type* type, void* input, json_t* val); +static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out); -static int jsonSerializer_writeAny(const dyn_type *type, void *input, json_t **val); -static int jsonSerializer_writeComplex(const dyn_type *type, void *input, json_t **val); -static int jsonSerializer_writeSequence(const dyn_type *type, void *input, json_t **out); -static int jsonSerializer_writeEnum(const dyn_type *type, int32_t enum_value, json_t **out); +static int jsonSerializer_writeAny(const dyn_type* type, void* input, json_t** val); +static int jsonSerializer_writeComplex(const dyn_type* type, void* input, json_t** val); +static int jsonSerializer_writeSequence(const dyn_type* type, void* input, json_t** out); +static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t** out); static int OK = 0; static int ERROR = 1; -int jsonSerializer_deserialize(dyn_type *type, const char *input, size_t length, void **result) { - assert(dynType_type(type) == DYN_TYPE_COMPLEX || dynType_type(type) == DYN_TYPE_SEQUENCE); +int jsonSerializer_deserialize(const dyn_type* type, const char* input, size_t length, void** result) { int status = 0; json_error_t error; - json_t *root = json_loadb(input, length, JSON_DECODE_ANY, &error); - - if (root != NULL) { - status = jsonSerializer_deserializeJson(type, root, result); - json_decref(root); - } else { - status = ERROR; + json_auto_t* root = json_loadb(input, length, JSON_DECODE_ANY, &error); + if (root == NULL) { celix_err_pushf("Error parsing json input '%.*s'. Error is: %s\n", (int)length, input, error.text); + return ERROR; } + status = jsonSerializer_deserializeJson(type, root, result); if (status != OK) { - celix_err_pushf("Error cannot deserialize json. Input is '%s'\n", input); + celix_err_pushf("Error cannot deserialize json. Input is '%s'", input); } return status; } -int jsonSerializer_deserializeJson(const dyn_type *type, json_t *input, void **out) { +int jsonSerializer_deserializeJson(const dyn_type* type, json_t* input, void** out) { return jsonSerializer_createType(type, input, out); } -static int jsonSerializer_createType(const dyn_type *type, json_t *val, void **result) { +static int jsonSerializer_createType(const dyn_type* type, json_t* val, void** result) { assert(val != NULL); int status = OK; - void *inst = NULL; - - if (dynType_descriptorType(type) == 't') { - if (json_typeof(val) == JSON_STRING) { - //note a deserialized C string is a sequence of memory for the actual string and a - //pointer to that sequence. That pointer also needs to reside in the memory (heap). - const char *s = json_string_value(val); - inst = calloc(1, sizeof(char*)); - *((char**)inst) = strdup(s); - } else { - status = ERROR; - celix_err_pushf("Expected json_string type got %i\n", json_typeof(val)); - } - } else { - status = dynType_alloc(type, &inst); + void* inst = NULL; - if (status == OK) { - assert(inst != NULL); - status = jsonSerializer_parseAny(type, inst, val); - } + if ((status = dynType_alloc(type, &inst)) != OK) { + return status; } - if (status == OK) { - *result = inst; - } else { + if ((status = jsonSerializer_parseAny(type, inst, val)) != OK) { + dynType_free(type, inst); *result = NULL; - dynType_free(type, inst); + return status; } - return status; + *result = inst; + return OK; } -static int jsonSerializer_parseObject(const dyn_type *type, json_t *object, void *inst) { +static int jsonSerializer_parseObject(const dyn_type* type, json_t* object, void* inst) { assert(object != NULL); int status = 0; - json_t *value; - const char *key; + json_t* value; + const char* key; json_object_foreach(object, key, value) { status = jsonSerializer_parseObjectMember(type, key, value, inst); @@ -120,13 +100,14 @@ static int jsonSerializer_parseObject(const dyn_type *type, json_t *object, void return status; } -static int jsonSerializer_parseObjectMember(const dyn_type *type, const char *name, json_t *val, void *inst) { - void *valp = NULL; +static int jsonSerializer_parseObjectMember(const dyn_type* type, const char* name, json_t* val, void* inst) { + void* valp = NULL; const dyn_type* valType = NULL; int index = dynType_complex_indexForName(type, name); if (index < 0) { - return OK;//We should ignore unknown name in request or response. Satisfy forward compatibility for responses. + celix_err_pushf("Cannot find index for member '%s'", name); + return ERROR; } valp = dynType_complex_valLocAt(type, index, inst); @@ -135,87 +116,54 @@ static int jsonSerializer_parseObjectMember(const dyn_type *type, const char *na return jsonSerializer_parseAny(valType, valp, val); } -static int jsonSerializer_parseAny(const dyn_type *type, void *loc, json_t *val) { +static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) { int status = OK; - const dyn_type *subType = NULL; + const dyn_type* subType = NULL; char c = dynType_descriptorType(type); - /* - printf("parseAny with descriptor '%c' :", c); - json_dumpf(val, stdout, 0); //TODO remove - printf("\n"); - */ - - bool *z; //Z - float *f; //F - double *d; //D - char *b; //B - int *n; //N - int16_t *s; //S - int32_t *i; //I - int32_t *E; //E - int64_t *l; //J - uint8_t *ub; //b - uint16_t *us; //s - uint32_t *ui; //i - uint64_t *ul; //j - switch (c) { case 'Z' : - z = loc; - *z = (bool) json_is_true(val); + *(bool*)loc = (bool) json_is_true(val); break; case 'F' : - f = loc; - *f = (float) json_real_value(val); + *(float*)loc = (float) json_real_value(val); break; case 'D' : - d = loc; - *d = json_real_value(val); + *(double*)loc = json_real_value(val); break; case 'N' : - n = loc; - *n = (int) json_integer_value(val); + *(int*)loc = (int) json_integer_value(val); break; case 'B' : - b = loc; - *b = (char) json_integer_value(val); + *(char*)loc = (char) json_integer_value(val); break; case 'S' : - s = loc; - *s = (int16_t) json_integer_value(val); + *(int16_t*)loc = (int16_t) json_integer_value(val); break; case 'I' : - i = loc; - *i = (int32_t) json_integer_value(val); + *(int32_t*)loc = (int32_t) json_integer_value(val); break; case 'J' : - l = loc; - *l = (int64_t) json_integer_value(val); + *(int64_t*)loc = (int64_t) json_integer_value(val); break; case 'b' : - ub = loc; - *ub = (uint8_t) json_integer_value(val); + *(uint8_t*)loc = (uint8_t) json_integer_value(val); break; case 's' : - us = loc; - *us = (uint16_t) json_integer_value(val); + *(uint16_t*)loc = (uint16_t) json_integer_value(val); break; case 'i' : - ui = loc; - *ui = (uint32_t) json_integer_value(val); + *(uint32_t*)loc = (uint32_t) json_integer_value(val); break; case 'j' : - ul = loc; - *ul = (uint64_t) json_integer_value(val); + *(uint64_t*)loc = (uint64_t) json_integer_value(val); break; case 'E' : if (json_is_null(val)) { //nop } else if (json_is_string(val)){ - E = loc; - status = jsonSerializer_parseEnum(type, json_string_value(val), E); + status = jsonSerializer_parseEnum(type, json_string_value(val), loc); } else { status = ERROR; celix_err_pushf("Expected json string for enum type but got %i", json_typeof(val)); @@ -248,13 +196,10 @@ static int jsonSerializer_parseAny(const dyn_type *type, void *loc, json_t *val) subType = dynType_typedPointer_getTypedType(type); status = jsonSerializer_createType(subType, val, (void **) loc); break; - case 'P' : - status = ERROR; - celix_err_pushf("Untyped pointer are not supported for serialization"); - break; case 'l': status = jsonSerializer_parseAny(type->ref.ref, loc, val); break; + case 'P' : default : status = ERROR; celix_err_pushf("Error provided type '%c' not supported for JSON\n", dynType_descriptorType(type)); @@ -264,7 +209,7 @@ static int jsonSerializer_parseAny(const dyn_type *type, void *loc, json_t *val) return status; } -static int jsonSerializer_parseSequence(const dyn_type *seq, json_t *array, void *seqLoc) { +static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void* seqLoc) { assert(dynType_type(seq) == DYN_TYPE_SEQUENCE); int status = OK; @@ -272,11 +217,11 @@ static int jsonSerializer_parseSequence(const dyn_type *seq, json_t *array, void status = dynType_sequence_alloc(seq, seqLoc, (int) size); if (status == OK) { - const dyn_type *itemType = dynType_sequence_itemType(seq); + const dyn_type* itemType = dynType_sequence_itemType(seq); size_t index; - json_t *val; + json_t* val; json_array_foreach(array, index, val) { - void *valLoc = NULL; + void* valLoc = NULL; status = dynType_sequence_increaseLengthAndReturnLastLoc(seq, seqLoc, &valLoc); if (status == OK) { status = jsonSerializer_parseAny(itemType, valLoc, val); @@ -290,10 +235,10 @@ static int jsonSerializer_parseSequence(const dyn_type *seq, json_t *array, void return status; } -int jsonSerializer_serialize(dyn_type *type, const void* input, char **output) { +int jsonSerializer_serialize(const dyn_type* type, const void* input, char** output) { int status = OK; - json_t *root = NULL; + json_t* root = NULL; status = jsonSerializer_serializeJson(type, input, &root); if (status == OK) { @@ -304,8 +249,8 @@ int jsonSerializer_serialize(dyn_type *type, const void* input, char **output) { return status; } -static int jsonSerializer_parseEnum(const dyn_type *type, const char* enum_name, int32_t *out) { - struct meta_entry * entry; +static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out) { + struct meta_entry* entry; TAILQ_FOREACH(entry, &type->metaProperties, entries) { if (0 == strcmp(enum_name, entry->name)) { @@ -318,7 +263,7 @@ static int jsonSerializer_parseEnum(const dyn_type *type, const char* enum_name, return ERROR; } -int jsonSerializer_serializeJson(const dyn_type *type, const void* input, json_t **out) { +int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t** out) { return jsonSerializer_writeAny(type, (void*)input /*TODO update static function to take const void**/, out); } From 4c6d4dbd05f5ae947881be1070f1c1004f3988f5 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 16 Jan 2024 23:00:40 +0800 Subject: [PATCH 29/62] More tests for `jsonSerializer_parseAny`. --- conanfile.py | 2 + libs/dfi/gtest/CMakeLists.txt | 1 + libs/dfi/gtest/src/dyn_type_ei_tests.cc | 4 +- .../dfi/gtest/src/json_serializer_ei_tests.cc | 44 ++++++++++++++++++- libs/dfi/gtest/src/json_serializer_tests.cpp | 9 ++++ libs/dfi/src/dyn_type.c | 4 +- libs/dfi/src/json_serializer.c | 37 +++++++++------- libs/error_injector/CMakeLists.txt | 6 +++ libs/error_injector/jansson/CMakeLists.txt | 28 ++++++++++++ .../jansson/include/jansson_ei.h | 33 ++++++++++++++ libs/error_injector/jansson/src/jansson_ei.cc | 31 +++++++++++++ 11 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 libs/error_injector/jansson/CMakeLists.txt create mode 100644 libs/error_injector/jansson/include/jansson_ei.h create mode 100644 libs/error_injector/jansson/src/jansson_ei.cc diff --git a/conanfile.py b/conanfile.py index b36bc30bb..10dffeb3e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -338,6 +338,8 @@ def generate(self): lst = [x.ref.name for x in self.requires.values()] if "mdnsresponder" in lst: tc.cache_variables["BUILD_ERROR_INJECTOR_MDNSRESPONDER"] = "ON" + if "jansson" in lst: + tc.cache_variables["BUILD_ERROR_INJECTOR_JANSSON"] = "ON" tc.cache_variables["CELIX_ERR_BUFFER_SIZE"] = str(self.options.celix_err_buffer_size) # tc.cache_variables["CMAKE_PROJECT_Celix_INCLUDE"] = os.path.join(self.build_folder, "conan_paths.cmake") # the following is workaround for https://github.com/conan-io/conan/issues/7192 diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index c780e3214..030c2a2c5 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -54,6 +54,7 @@ if (EI_TESTS) Celix::string_ei Celix::ffi_ei Celix::asprintf_ei + Celix::jansson_ei GTest::gtest GTest::gtest_main ) add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc index c52bd3e05..cf5c80560 100644 --- a/libs/dfi/gtest/src/dyn_type_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -142,7 +142,7 @@ TEST_F(DynTypeErrorInjectionTestSuite, SequenceAllocateError) { celix_ei_expect_calloc((void*)dynType_sequence_alloc, 0, nullptr); rc = dynType_sequence_alloc(type, seq, 1); ASSERT_NE(0, rc); - ASSERT_STREQ("Error allocating memory for buf", celix_err_popLastError()); + ASSERT_STREQ("Error allocating memory for seq buf", celix_err_popLastError()); dynType_free(type, seq); dynType_destroy(type); @@ -167,7 +167,7 @@ TEST_F(DynTypeErrorInjectionTestSuite, SequenceReserveError) { celix_ei_expect_realloc((void*)dynType_sequence_reserve, 0, nullptr); rc = dynType_sequence_reserve(type, seq, 1); ASSERT_NE(0, rc); - ASSERT_STREQ("Error allocating memory for buf", celix_err_popLastError()); + ASSERT_STREQ("Error allocating memory for seq buf", celix_err_popLastError()); dynType_free(type, seq); dynType_destroy(type); diff --git a/libs/dfi/gtest/src/json_serializer_ei_tests.cc b/libs/dfi/gtest/src/json_serializer_ei_tests.cc index dfd18b8b0..c1a538166 100644 --- a/libs/dfi/gtest/src/json_serializer_ei_tests.cc +++ b/libs/dfi/gtest/src/json_serializer_ei_tests.cc @@ -20,14 +20,19 @@ #include "json_serializer.h" #include "dyn_type.h" #include "celix_err.h" +#include "jansson_ei.h" #include "malloc_ei.h" +#include "string_ei.h" #include +#include class JsonSerializerErrorInjectionTestSuite : public ::testing::Test { public: JsonSerializerErrorInjectionTestSuite() = default; ~JsonSerializerErrorInjectionTestSuite() override { + celix_ei_expect_json_array_size(nullptr, 0, 0); + celix_ei_expect_strdup(nullptr, 0, nullptr); celix_ei_expect_calloc(nullptr, 0, nullptr); celix_err_resetErrors(); } @@ -38,7 +43,6 @@ TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { dyn_type *type; void *inst; - //simple string type = nullptr; inst = nullptr; rc = dynType_parseWithStr("t", nullptr, nullptr, &type); @@ -50,4 +54,42 @@ TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { EXPECT_STREQ("Error cannot deserialize json. Input is '\"hello\"'", celix_err_popLastError()); EXPECT_STREQ("Error allocating memory for type 't'", celix_err_popLastError()); dynType_destroy(type); + + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr("t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"("hello")"; + celix_ei_expect_strdup((void*) dynType_text_allocAndInit, 0, nullptr); + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '\"hello\"'", celix_err_popLastError()); + EXPECT_STREQ("Cannot allocate memory for string", celix_err_popLastError()); + dynType_destroy(type); + + + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr("[t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"(["hello", "world"])"; + celix_ei_expect_json_array_size((void*)jsonSerializer_deserializeJson, 3, (size_t)UINT32_MAX+1); + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '[\"hello\", \"world\"]'", celix_err_popLastError()); + EXPECT_STREQ("Error array size(4294967296) too large", celix_err_popLastError()); + dynType_destroy(type); + + + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr("[t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"(["hello", "world"])"; + celix_ei_expect_calloc((void*) dynType_sequence_alloc, 0, nullptr); + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '[\"hello\", \"world\"]'", celix_err_popLastError()); + EXPECT_STREQ("Error allocating memory for seq buf", celix_err_popLastError()); + dynType_destroy(type); } \ No newline at end of file diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index 0d924f71e..a41c99dae 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -569,6 +569,15 @@ static void parseTests() { celix_err_printErrors(stderr, nullptr, nullptr); dynType_destroy(type); + // parse complex from non-object + rc = dynType_parseWithStr("{t a}", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"(["a"])"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_EQ(1, rc); + celix_err_printErrors(stderr, nullptr, nullptr); + dynType_destroy(type); + //simple string rc = dynType_parseWithStr("t", nullptr, nullptr, &type); ASSERT_EQ(0, rc); diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 0ad3e330d..92fb4fe5f 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -590,7 +590,7 @@ int dynType_sequence_alloc(const dyn_type* type, void* inst, uint32_t cap) { seq->buf = calloc(cap, size); if (seq->buf == NULL) { seq->cap = 0; - celix_err_pushf("Error allocating memory for buf"); + celix_err_pushf("Error allocating memory for seq buf"); return MEM_ERROR; } seq->cap = cap; @@ -613,7 +613,7 @@ int dynType_sequence_reserve(const dyn_type* type, void* inst, uint32_t cap) { seq->buf = realloc(seq->buf, (size_t)(cap * size)); if (seq->buf == NULL) { seq->cap = 0; - celix_err_pushf("Error allocating memory for buf"); + celix_err_pushf("Error allocating memory for seq buf"); return MEM_ERROR; } memset(seq->buf+seq->cap*size, 0, (cap-seq->cap)*size); diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index da9953669..a3e4a7eac 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -188,8 +188,11 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) } break; case '{' : - if (status == OK) { + if (json_is_object(val)) { status = jsonSerializer_parseObject(type, val, loc); + } else { + status = ERROR; + celix_err_pushf("Expected json object type got '%i'", json_typeof(val)); } break; case '*' : @@ -202,7 +205,7 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) case 'P' : default : status = ERROR; - celix_err_pushf("Error provided type '%c' not supported for JSON\n", dynType_descriptorType(type)); + celix_err_pushf("Error provided type '%c' not supported for JSON\n", c); break; } @@ -214,21 +217,23 @@ static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void int status = OK; size_t size = json_array_size(array); - status = dynType_sequence_alloc(seq, seqLoc, (int) size); + if (size > UINT32_MAX) { + celix_err_pushf("Error array size(%zu) too large", size); + return ERROR; + } + if ((status = dynType_sequence_alloc(seq, seqLoc, (uint32_t) size)) != OK) { + return status; + } - if (status == OK) { - const dyn_type* itemType = dynType_sequence_itemType(seq); - size_t index; - json_t* val; - json_array_foreach(array, index, val) { - void* valLoc = NULL; - status = dynType_sequence_increaseLengthAndReturnLastLoc(seq, seqLoc, &valLoc); - if (status == OK) { - status = jsonSerializer_parseAny(itemType, valLoc, val); - if (status != OK) { - break; - } - } + const dyn_type* itemType = dynType_sequence_itemType(seq); + size_t index; + json_t* val; + json_array_foreach(array, index, val) { + void* valLoc = NULL; + (void)dynType_sequence_increaseLengthAndReturnLastLoc(seq, seqLoc, &valLoc); + status = jsonSerializer_parseAny(itemType, valLoc, val); + if (status != OK) { + break; } } diff --git a/libs/error_injector/CMakeLists.txt b/libs/error_injector/CMakeLists.txt index 958d99c94..0d5080458 100644 --- a/libs/error_injector/CMakeLists.txt +++ b/libs/error_injector/CMakeLists.txt @@ -37,7 +37,13 @@ add_subdirectory(sys_shm) add_subdirectory(socket) add_subdirectory(pthread) add_subdirectory(unistd) + celix_subproject(ERROR_INJECTOR_MDNSRESPONDER "Option to enable building the mdnsresponder error injector" OFF) if (ERROR_INJECTOR_MDNSRESPONDER) add_subdirectory(mdnsresponder) endif () + +celix_subproject(ERROR_INJECTOR_JANSSON "Option to enable building the jansson error injector", OFF) +if (ERROR_INJECTOR_JANSSON) + add_subdirectory(jansson) +endif () diff --git a/libs/error_injector/jansson/CMakeLists.txt b/libs/error_injector/jansson/CMakeLists.txt new file mode 100644 index 000000000..73fec4035 --- /dev/null +++ b/libs/error_injector/jansson/CMakeLists.txt @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +find_package(jansson REQUIRED) + +add_library(jansson_ei STATIC src/jansson_ei.cc) + +target_include_directories(jansson_ei PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(jansson_ei PUBLIC Celix::error_injector jansson::jansson) + +target_link_options(jansson_ei INTERFACE + LINKER:--wrap,json_array_size + ) +add_library(Celix::jansson_ei ALIAS jansson_ei) diff --git a/libs/error_injector/jansson/include/jansson_ei.h b/libs/error_injector/jansson/include/jansson_ei.h new file mode 100644 index 000000000..a33693719 --- /dev/null +++ b/libs/error_injector/jansson/include/jansson_ei.h @@ -0,0 +1,33 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ +#ifndef CELIX_JANSSON_EI_H +#define CELIX_JANSSON_EI_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "celix_error_injector.h" + +CELIX_EI_DECLARE(json_array_size, size_t); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_JANSSON_EI_H diff --git a/libs/error_injector/jansson/src/jansson_ei.cc b/libs/error_injector/jansson/src/jansson_ei.cc new file mode 100644 index 000000000..df3a8ed02 --- /dev/null +++ b/libs/error_injector/jansson/src/jansson_ei.cc @@ -0,0 +1,31 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#include "jansson_ei.h" + +extern "C" { + +size_t __real_json_array_size(const json_t *array); +CELIX_EI_DEFINE(json_array_size, size_t) +size_t __wrap_json_array_size(const json_t *array) { + CELIX_EI_IMPL(json_array_size); + return __real_json_array_size(array); +} + +} \ No newline at end of file From b61b6bd4e01dec4f40ef9d466f509f882e4f9b8e Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 16 Jan 2024 23:07:23 +0800 Subject: [PATCH 30/62] Fix unit test error in TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError). --- libs/dfi/src/json_serializer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index a3e4a7eac..1cd622df2 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -173,7 +173,7 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) if (json_is_null(val)) { //nop } else if (json_is_string(val)) { - dynType_text_allocAndInit(type, loc, json_string_value(val)); + status = dynType_text_allocAndInit(type, loc, json_string_value(val)); } else { status = ERROR; celix_err_pushf("Expected json string type got %i", json_typeof(val)); From 8e38c03f6fb9d12b47902ebd737216abc9abcd31 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 16 Jan 2024 23:11:03 +0800 Subject: [PATCH 31/62] Fix compilation error linux-build-apt CI. --- libs/error_injector/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/error_injector/CMakeLists.txt b/libs/error_injector/CMakeLists.txt index 0d5080458..8e84dde5c 100644 --- a/libs/error_injector/CMakeLists.txt +++ b/libs/error_injector/CMakeLists.txt @@ -43,7 +43,7 @@ if (ERROR_INJECTOR_MDNSRESPONDER) add_subdirectory(mdnsresponder) endif () -celix_subproject(ERROR_INJECTOR_JANSSON "Option to enable building the jansson error injector", OFF) +celix_subproject(ERROR_INJECTOR_JANSSON "Option to enable building the jansson error injector", ON) if (ERROR_INJECTOR_JANSSON) add_subdirectory(jansson) endif () From 58589ade6574a3185d7ae6901136a66b3405da28 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 17 Jan 2024 11:23:01 +0800 Subject: [PATCH 32/62] Fix compilation error linux-build-apt CI. --- libs/error_injector/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/error_injector/CMakeLists.txt b/libs/error_injector/CMakeLists.txt index 8e84dde5c..76a9f99f7 100644 --- a/libs/error_injector/CMakeLists.txt +++ b/libs/error_injector/CMakeLists.txt @@ -43,7 +43,7 @@ if (ERROR_INJECTOR_MDNSRESPONDER) add_subdirectory(mdnsresponder) endif () -celix_subproject(ERROR_INJECTOR_JANSSON "Option to enable building the jansson error injector", ON) +celix_subproject(ERROR_INJECTOR_JANSSON "Option to enable building the jansson error injector" ON) if (ERROR_INJECTOR_JANSSON) add_subdirectory(jansson) endif () From 3f039d3e21dd219c90bdd2738b318257f3f8259f Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 17 Jan 2024 12:23:22 +0800 Subject: [PATCH 33/62] Add option to control whether to install Find modules defined by Celix, and skip it in conan build. --- CMakeLists.txt | 5 ++++- conanfile.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4228d32ef..f0aa5aa5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,10 @@ install(EXPORT celix NAMESPACE Celix:: DESTINATION share/celix/cmake FILE Target install_celix_targets(celix NAMESPACE Celix:: DESTINATION share/celix/cmake FILE CelixTargets COMPONENT cmake) #install celix cmake modules -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/ DESTINATION share/celix/cmake/Modules) +option(INSTALL_FIND_MODULES "Whether to install Find modules defined by Celix" ON) +if (INSTALL_FIND_MODULES) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/ DESTINATION share/celix/cmake/Modules) +endif () install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_celix/ DESTINATION share/celix/cmake/cmake_celix) file(GENERATE diff --git a/conanfile.py b/conanfile.py index 10dffeb3e..60e05f719 100644 --- a/conanfile.py +++ b/conanfile.py @@ -47,6 +47,7 @@ class CelixConan(ConanFile): "enable_address_sanitizer": False, "enable_undefined_sanitizer": False, "enable_thread_sanitizer": False, + "install_find_modules": False, "build_all": False, "build_http_admin": False, "build_log_service": False, From f6a42e85307f3de558521a1a4e228621f3b5376a Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 17 Jan 2024 12:50:51 +0800 Subject: [PATCH 34/62] Remove unnecessary find_dependency from CelixDeps.cmake.in. --- cmake/CelixDeps.cmake.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/CelixDeps.cmake.in b/cmake/CelixDeps.cmake.in index 538153630..6567d959d 100644 --- a/cmake/CelixDeps.cmake.in +++ b/cmake/CelixDeps.cmake.in @@ -10,7 +10,6 @@ $<$>:find_dependency(CURL)> $<$>:find_dependency(CURL)> $<$>:find_dependency(CURL)> $<$>:find_dependency(CURL)> -$<$>:find_dependency(RapidJSON)> $<$>:find_dependency(LibXml2)> $<$>:find_dependency(LibXml2)> $<$>:find_dependency(ZeroMQ)> From e1dd1b880a1857757e5b409c50dd9b4faf610670 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 17 Jan 2024 21:30:01 +0800 Subject: [PATCH 35/62] Improve dyn_type deserialization. 1. Reject JSON object missing members. 2. Reject pointer to pointer. 3. Support nullptr for text and pointer type. --- libs/dfi/gtest/src/json_rpc_tests.cpp | 108 ------------------- libs/dfi/gtest/src/json_serializer_tests.cpp | 93 +++++++++++----- libs/dfi/src/dyn_type_common.c | 2 +- libs/dfi/src/dyn_type_common.h | 2 +- libs/dfi/src/json_serializer.c | 58 +++++----- 5 files changed, 97 insertions(+), 166 deletions(-) diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index b98d9961b..91b6d9315 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -775,110 +775,6 @@ extern "C" { ASSERT_EQ(0, rc); free(result); - dynInterface_destroy(intf); - } - - enum example6_enum{ - v1 = 1, - v2 = 2, - }; - - struct tst_CptData { - double d; - char *t; - struct tst_seq s; - enum example6_enum e; - }; - - struct tst_serv_example6 { - void *handle; - int (*cpt)(void *, struct tst_CptData *input, struct tst_CptData *output); - }; - - void testRequestBackwardCompatibility(void) { - dyn_interface_type *intf = nullptr; - FILE *desc = fopen("descriptors/example6.descriptor", "r"); - ASSERT_TRUE(desc != nullptr); - int rc = dynInterface_parse(desc, &intf); - ASSERT_EQ(0, rc); - fclose(desc); - - - char *result = nullptr; - - tst_serv_example6 serv {nullptr, nullptr}; - - serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData *)->int { - EXPECT_EQ(input->d , 0.0); - EXPECT_EQ(input->t , nullptr); - EXPECT_EQ(input->s.len , 0); - EXPECT_EQ(input->s.cap , 0); - EXPECT_EQ(input->s.buf , nullptr); - EXPECT_EQ(input->e , 0); - return 0; - }; - rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": [{}]})", &result); - ASSERT_EQ(0, rc); - free(result); - - serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData *)->int { - EXPECT_EQ(input->d , 1.0); - EXPECT_EQ(input->t , nullptr); - EXPECT_EQ(input->s.len , 0); - EXPECT_EQ(input->s.cap , 0); - EXPECT_EQ(input->s.buf , nullptr); - EXPECT_EQ(input->e , 0); - return 0; - }; - rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": [{"d":1.0}]})", &result); - ASSERT_EQ(0, rc); - free(result); - - serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData *)->int { - EXPECT_EQ(input->d , 1.0); - EXPECT_STREQ(input->t , "hello compatibility"); - EXPECT_EQ(input->s.len , 0); - EXPECT_EQ(input->s.cap , 0); - EXPECT_EQ(input->s.buf , nullptr); - EXPECT_EQ(input->e , 0); - return 0; - }; - rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": [{"d":1.0, "t":"hello compatibility"}]})", &result); - ASSERT_EQ(0, rc); - free(result); - - serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData *)->int { - EXPECT_EQ(input->d , 1.0); - EXPECT_STREQ(input->t , "hello compatibility"); - EXPECT_EQ(input->s.len , 3); - EXPECT_EQ(input->s.cap , 3); - EXPECT_EQ(input->s.buf[0] , 1.0); - EXPECT_EQ(input->s.buf[1] , 2.0); - EXPECT_EQ(input->s.buf[2] , 3.0); - EXPECT_EQ(input->e , 0); - return 0; - }; - rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": [{"d":1.0, "t":"hello compatibility", "s":[1.0,2.0,3.0]}]})", &result); - ASSERT_EQ(0, rc); - free(result); - - - serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData *)->int { - EXPECT_EQ(input->d , 1.0); - EXPECT_STREQ(input->t , "hello compatibility"); - EXPECT_EQ(input->s.len , 3); - EXPECT_EQ(input->s.cap , 3); - EXPECT_EQ(input->s.buf[0] , 1.0); - EXPECT_EQ(input->s.buf[1] , 2.0); - EXPECT_EQ(input->s.buf[2] , 3.0); - EXPECT_EQ(input->e , v2); - return 0; - }; - rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": [{"d":1.0, "t":"hello compatibility", "s":[1.0,2.0,3.0], "e":"v2"}]})", &result); - ASSERT_EQ(0, rc); - free(result); - - dynInterface_destroy(intf); } } @@ -983,8 +879,4 @@ TEST_F(JsonRpcTests, callTestChar) { TEST_F(JsonRpcTests, callTestConstChar) { callTestConstChar(); -} - -TEST_F(JsonRpcTests, testRequestBackwardCompatibility) { - testRequestBackwardCompatibility(); } \ No newline at end of file diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index a41c99dae..abf82b800 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -162,18 +162,24 @@ static const char *example5_input = "{ \ \"age\" : 44 \ },\ \"left\" : {\ + \"left\" : null,\ + \"right\" : null,\ \"value\" : {\ \"name\" : \"Victor\",\ \"age\" : 400 \ }\ - }\ + },\ + \"right\" : null\ },\ \"right\" : {\ + \"left\" : null,\ + \"right\" : null,\ \"value\" : {\ \"name\" : \"Peter\", \ \"age\" : 55 \ }\ - }\ + },\ + \"value\" : null\ }\ }"; @@ -212,6 +218,8 @@ static void check_example5(void *data) { ASSERT_EQ(55, ex->head->right->value->age); ASSERT_TRUE(ex->head->right->left == nullptr); ASSERT_TRUE(ex->head->right->right == nullptr); + + ASSERT_TRUE(ex->head->value == nullptr); } /*********** example 6 ************************/ @@ -270,25 +278,6 @@ const char *example8_input = "{ \ \"e\" : 32 \ }"; -struct example8 { - bool a; - unsigned char b; - uint32_t c; - uint64_t d; - int e; - void* f; -}; - -static void check_example8(void *data) { - auto ex = static_cast(data); - ASSERT_EQ(true,ex->a); - ASSERT_EQ(4,ex->b); - ASSERT_EQ(8,ex->c); - //error on mac ASSERT_EQ(16,ex->d); - ASSERT_TRUE(16 == ex->d); - ASSERT_EQ(32,ex->e); -} - /*********** example 9 ************************/ const char *example9_descriptor = "{It#OK=0;#NOK=1;#MAYBE=2;E id name result}"; @@ -443,14 +432,15 @@ static void parseTests() { dynType_free(type, inst); dynType_destroy(type); + // missing object member "f" type = nullptr; inst = nullptr; rc = dynType_parseWithStr(example8_descriptor, nullptr, nullptr, &type); ASSERT_EQ(0, rc); rc = jsonSerializer_deserialize(type, example8_input, strlen(example8_input), &inst); - ASSERT_EQ(0, rc); - check_example8(inst); - dynType_free(type, inst); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '{ \"a\" : true, \"b\" : 4, \"c\" : 8, \"d\" : 16, \"e\" : 32 }'", + celix_err_popLastError()); dynType_destroy(type); type = nullptr; @@ -506,12 +496,14 @@ static void parseTests() { celix_err_printErrors(stderr, nullptr, nullptr); dynType_destroy(type); - //pointer type mismatch + //double pointer unsupported rc = dynType_parseWithStr("{*t a}", nullptr, nullptr, &type); ASSERT_EQ(0, rc); - inputStr = R"({"a":1.0})"; + inputStr = R"({"a":"hello world"})"; rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); ASSERT_EQ(1, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '{\"a\":\"hello world\"}'", celix_err_popLastError()); + EXPECT_STREQ("Error cannot deserialize pointer to pointer", celix_err_popLastError()); celix_err_printErrors(stderr, nullptr, nullptr); dynType_destroy(type); @@ -560,13 +552,13 @@ static void parseTests() { celix_err_printErrors(stderr, nullptr, nullptr); dynType_destroy(type); - // unknown member + // extra member ("b") is allowed rc = dynType_parseWithStr("{t a}", nullptr, nullptr, &type); ASSERT_EQ(0, rc); inputStr = R"({"a":"hello", "b":"world"})"; rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); - ASSERT_EQ(1, rc); - celix_err_printErrors(stderr, nullptr, nullptr); + ASSERT_EQ(0, rc); + dynType_free(type, inst); dynType_destroy(type); // parse complex from non-object @@ -587,6 +579,48 @@ static void parseTests() { EXPECT_STREQ("hello", *(char**)inst); dynType_free(type, inst); dynType_destroy(type); + + //null string + rc = dynType_parseWithStr("t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"(null)"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_EQ(0, rc); + EXPECT_EQ(nullptr, *(char**)inst); + dynType_free(type, inst); + dynType_destroy(type); + + // double* + rc = dynType_parseWithStr("*D", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"(0.1)"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_EQ(0, rc); + EXPECT_EQ(0.1, **(double**)inst); + dynType_free(type, inst); + dynType_destroy(type); + + // null double* + rc = dynType_parseWithStr("*D", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"(null)"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + ASSERT_EQ(0, rc); + EXPECT_EQ(nullptr, *(double**)inst); + dynType_free(type, inst); + dynType_destroy(type); + + // unnamed fields + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr("{DD}", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + inputStr = R"({"a":1.0, "b":2.0})"; + rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error cannot deserialize json. Input is '{\"a\":1.0, \"b\":2.0}'", celix_err_popLastError()); + EXPECT_STREQ("Unamed field unsupported", celix_err_popLastError()); + dynType_destroy(type); } /*********** write example 1 ************************/ @@ -800,6 +834,7 @@ class JsonSerializerTests : public ::testing::Test { JsonSerializerTests() { } ~JsonSerializerTests() override { + celix_err_resetErrors(); } }; diff --git a/libs/dfi/src/dyn_type_common.c b/libs/dfi/src/dyn_type_common.c index 885da8064..64c2e8349 100644 --- a/libs/dfi/src/dyn_type_common.c +++ b/libs/dfi/src/dyn_type_common.c @@ -51,7 +51,7 @@ dyn_type* dynType_findType(dyn_type *type, char *name) { return NULL; } -ffi_type * dynType_ffiType(dyn_type * type) { +ffi_type* dynType_ffiType(const dyn_type * type) { while (type->type == DYN_TYPE_REF) { type = type->ref.ref; } diff --git a/libs/dfi/src/dyn_type_common.h b/libs/dfi/src/dyn_type_common.h index 34e5b4a9d..314cb2dd1 100644 --- a/libs/dfi/src/dyn_type_common.h +++ b/libs/dfi/src/dyn_type_common.h @@ -59,7 +59,7 @@ struct _dyn_type { }; dyn_type* dynType_findType(dyn_type* type, char* name); -ffi_type* dynType_ffiType(dyn_type* type); +ffi_type* dynType_ffiType(const dyn_type* type); #ifdef __cplusplus } diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index 1cd622df2..6c97d625e 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -30,7 +30,6 @@ static int jsonSerializer_createType(const dyn_type* type, json_t* object, void** result); static int jsonSerializer_parseObject(const dyn_type* type, json_t* object, void* inst); -static int jsonSerializer_parseObjectMember(const dyn_type* type, const char* name, json_t* val, void* inst); static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void* seqLoc); static int jsonSerializer_parseAny(const dyn_type* type, void* input, json_t* val); static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out); @@ -86,36 +85,35 @@ static int jsonSerializer_createType(const dyn_type* type, json_t* val, void** r static int jsonSerializer_parseObject(const dyn_type* type, json_t* object, void* inst) { assert(object != NULL); - int status = 0; + int status = OK; json_t* value; - const char* key; - - json_object_foreach(object, key, value) { - status = jsonSerializer_parseObjectMember(type, key, value, inst); + const struct complex_type_entries_head* entries = dynType_complex_entries(type); + struct complex_type_entry* entry = NULL; + int index = 0; + void* valp = NULL; + const dyn_type* valType = NULL; + TAILQ_FOREACH(entry, entries, entries) { + if (entry->name == NULL) { + celix_err_push("Unamed field unsupported"); + return ERROR; + } + value = json_object_get(object, entry->name); + if (value == NULL) { + celix_err_pushf("Missing object member %s", entry->name); + return ERROR; + } + valp = dynType_complex_valLocAt(type, index, inst); + valType = dynType_complex_dynTypeAt(type, index); + status = jsonSerializer_parseAny(valType, valp, value); if (status != OK) { break; } + index++; } return status; } -static int jsonSerializer_parseObjectMember(const dyn_type* type, const char* name, json_t* val, void* inst) { - void* valp = NULL; - const dyn_type* valType = NULL; - - int index = dynType_complex_indexForName(type, name); - if (index < 0) { - celix_err_pushf("Cannot find index for member '%s'", name); - return ERROR; - } - - valp = dynType_complex_valLocAt(type, index, inst); - valType = dynType_complex_dynTypeAt(type, index); - - return jsonSerializer_parseAny(valType, valp, val); -} - static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) { int status = OK; @@ -160,9 +158,7 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) *(uint64_t*)loc = (uint64_t) json_integer_value(val); break; case 'E' : - if (json_is_null(val)) { - //nop - } else if (json_is_string(val)){ + if (json_is_string(val)){ status = jsonSerializer_parseEnum(type, json_string_value(val), loc); } else { status = ERROR; @@ -171,7 +167,7 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) break; case 't' : if (json_is_null(val)) { - //nop + // NULL string is allowed } else if (json_is_string(val)) { status = dynType_text_allocAndInit(type, loc, json_string_value(val)); } else { @@ -197,7 +193,15 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) break; case '*' : subType = dynType_typedPointer_getTypedType(type); - status = jsonSerializer_createType(subType, val, (void **) loc); + if (dynType_ffiType(subType) != &ffi_type_pointer) { + // NULL pointer is allowed + if (!json_is_null(val)) { + status = jsonSerializer_createType(subType, val, (void **) loc); + } + } else { + status = ERROR; + celix_err_pushf("Error cannot deserialize pointer to pointer"); + } break; case 'l': status = jsonSerializer_parseAny(type->ref.ref, loc, val); From 927926c845392d686ab8a021732ca4e8c556b5dd Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 18 Jan 2024 14:27:22 +0800 Subject: [PATCH 36/62] Remove unnecessary recursions caused by type reference by name. --- libs/dfi/src/dyn_type.c | 7 +++---- libs/dfi/src/json_serializer.c | 24 +++++++++++------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 92fb4fe5f..d9ce0206f 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -629,11 +629,10 @@ static void dynType_deepFree(const dyn_type* type, void* loc, bool alsoDeleteSel if (loc != NULL) { const dyn_type* subType = NULL; char* text = NULL; + while (type->type == DYN_TYPE_REF) { + type = type->ref.ref; + } switch (type->type) { - case DYN_TYPE_REF: - //NOTE: do not recursively forward asloDeleteSelf, because this is already handled in this function) - dynType_deepFree(type->ref.ref, loc, false); - break; case DYN_TYPE_COMPLEX : dynType_freeComplexType(type, loc); break; diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index 6c97d625e..c271a0277 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -61,7 +61,11 @@ int jsonSerializer_deserialize(const dyn_type* type, const char* input, size_t l } int jsonSerializer_deserializeJson(const dyn_type* type, json_t* input, void** out) { - return jsonSerializer_createType(type, input, out); + const dyn_type* real = type; + while (real->type == DYN_TYPE_REF) { + real = real->ref.ref; + } + return jsonSerializer_createType(real, input, out); } static int jsonSerializer_createType(const dyn_type* type, json_t* val, void** result) { @@ -203,10 +207,7 @@ static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) celix_err_pushf("Error cannot deserialize pointer to pointer"); } break; - case 'l': - status = jsonSerializer_parseAny(type->ref.ref, loc, val); - break; - case 'P' : + //case 'P' : default : status = ERROR; celix_err_pushf("Error provided type '%c' not supported for JSON\n", c); @@ -273,7 +274,11 @@ static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, } int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t** out) { - return jsonSerializer_writeAny(type, (void*)input /*TODO update static function to take const void**/, out); + const dyn_type* real = type; + while (real->type == DYN_TYPE_REF) { + real = real->ref.ref; + } + return jsonSerializer_writeAny(real, (void*)input /*TODO update static function to take const void**/, out); } static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **out) { @@ -363,13 +368,6 @@ static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **o case '[' : status = jsonSerializer_writeSequence(type, input, &val); break; - case 'P' : - status = ERROR; - celix_err_pushf("Untyped pointer not supported for serialization."); - break; - case 'l': - status = jsonSerializer_writeAny(type->ref.ref, input, out); - break; default : celix_err_pushf("Unsupported descriptor '%c'", descriptor); status = ERROR; From 782325e6c876943f5d861357fe2bc700022c4b3b Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 19 Jan 2024 15:39:46 +0800 Subject: [PATCH 37/62] Improve dyn_type serialization. 1. Support nullptr for pointer type. 2. Reject pointer to pointer. 3. Apply const qualifiers when possible. 4. More robust error handling and thorough test coverage. --- libs/dfi/gtest/src/dyn_type_tests.cpp | 7 + .../dfi/gtest/src/json_serializer_ei_tests.cc | 81 ++++++- libs/dfi/gtest/src/json_serializer_tests.cpp | 43 +++- libs/dfi/include/dyn_type.h | 2 +- libs/dfi/src/dyn_type.c | 4 +- libs/dfi/src/json_serializer.c | 212 +++++++----------- libs/error_injector/jansson/CMakeLists.txt | 11 +- .../jansson/include/jansson_ei.h | 6 + libs/error_injector/jansson/src/jansson_ei.cc | 46 ++++ 9 files changed, 278 insertions(+), 134 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index b714aa7ae..961a24112 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -170,13 +170,20 @@ TEST_F(DynTypeTests, AssignTest1) { int32_t val1 = 2; int32_t val2 = 4; int32_t val3 = 8; + EXPECT_EQ(0, dynType_complex_indexForName(type, "a")); dynType_complex_setValueAt(type, 0, &inst, &val1); ASSERT_EQ(2, inst.a); + + EXPECT_EQ(1, dynType_complex_indexForName(type, "b")); dynType_complex_setValueAt(type, 1, &inst, &val2); ASSERT_EQ(4, inst.b); + + EXPECT_EQ(2, dynType_complex_indexForName(type, "c")); dynType_complex_setValueAt(type, 2, &inst, &val3); ASSERT_EQ(8, inst.c); + EXPECT_EQ(-1, dynType_complex_indexForName(type, nullptr)); + EXPECT_EQ(-1, dynType_complex_indexForName(type, "none")); dynType_destroy(type); } diff --git a/libs/dfi/gtest/src/json_serializer_ei_tests.cc b/libs/dfi/gtest/src/json_serializer_ei_tests.cc index c1a538166..4c7e9feba 100644 --- a/libs/dfi/gtest/src/json_serializer_ei_tests.cc +++ b/libs/dfi/gtest/src/json_serializer_ei_tests.cc @@ -31,6 +31,12 @@ class JsonSerializerErrorInjectionTestSuite : public ::testing::Test { public: JsonSerializerErrorInjectionTestSuite() = default; ~JsonSerializerErrorInjectionTestSuite() override { + celix_ei_expect_json_integer(nullptr, 0, nullptr); + celix_ei_expect_json_array_append_new(nullptr, 0, 0); + celix_ei_expect_json_array(nullptr, 0, nullptr); + celix_ei_expect_json_object_set_new(nullptr, 0, 0); + celix_ei_expect_json_object(nullptr, 0, nullptr); + celix_ei_expect_json_dumps(nullptr, 0, nullptr); celix_ei_expect_json_array_size(nullptr, 0, 0); celix_ei_expect_strdup(nullptr, 0, nullptr); celix_ei_expect_calloc(nullptr, 0, nullptr); @@ -38,7 +44,7 @@ class JsonSerializerErrorInjectionTestSuite : public ::testing::Test { } }; -TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { +TEST_F(JsonSerializerErrorInjectionTestSuite, DeserilizationError) { int rc; dyn_type *type; void *inst; @@ -92,4 +98,75 @@ TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { EXPECT_STREQ("Error cannot deserialize json. Input is '[\"hello\", \"world\"]'", celix_err_popLastError()); EXPECT_STREQ("Error allocating memory for seq buf", celix_err_popLastError()); dynType_destroy(type); -} \ No newline at end of file +} + +struct test_struct { + double a; + double b; +}; + +struct test_seq { + uint32_t cap; + uint32_t len; + double* buf; +}; + +TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { + dyn_type *type = nullptr; + char *result = nullptr; + int rc = dynType_parseWithStr(R"(#v1=1;#v2=2;E)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + enum { + v1 = 1, + v2 = 2 + }enumVal = v2; + celix_ei_expect_json_dumps((void*)jsonSerializer_serialize, 0, nullptr); + rc = jsonSerializer_serialize(type, &enumVal, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + test_struct ex {1.0, 2.0}; + rc = dynType_parseWithStr(R"({DD a b})", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_object((void*)jsonSerializer_serialize, 3, nullptr); + rc = jsonSerializer_serialize(type, &ex, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + rc = dynType_parseWithStr(R"({DD a b})", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_object_set_new((void*)jsonSerializer_serialize, 3, -1); + rc = jsonSerializer_serialize(type, &ex, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + double arr[1] = {1.0}; + test_seq seq {1, 1, arr}; + rc = dynType_parseWithStr(R"([D)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_array((void*)jsonSerializer_serialize, 3, nullptr); + rc = jsonSerializer_serialize(type, &seq, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + rc = dynType_parseWithStr(R"([D)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_array_append_new((void*)jsonSerializer_serialize, 3, -1); + rc = jsonSerializer_serialize(type, &seq, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + int32_t intVal = 12345; + rc = dynType_parseWithStr(R"(I)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_integer((void*)jsonSerializer_serialize, 2, nullptr); + rc = jsonSerializer_serialize(type, &intVal, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); +} diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index abf82b800..449ab6fe0 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -34,6 +34,8 @@ extern "C" { #include "json_serializer.h" #include "celix_err.h" +#include + /*********** example 1 ************************/ /** struct type ******************************/ const char *example1_descriptor = "{DJISF a b c d e}"; @@ -408,7 +410,7 @@ static void parseTests() { ASSERT_EQ(0, rc); rc = jsonSerializer_deserialize(type, example5_input, strlen(example5_input), &inst); ASSERT_EQ(0, rc); - check_example5(inst); + check_example5(inst); dynType_free(type, inst); dynType_destroy(type); @@ -874,3 +876,42 @@ TEST_F(JsonSerializerTests, WriteComplexFailed) { TEST_F(JsonSerializerTests, WriteEnumFailed) { writeEnumFailed(); } + +TEST_F(JsonSerializerTests, WriteDoublePointer) { + dyn_type *type; + int rc; + double val = 1.0; + double* valp = &val; + double** valpp = &valp; + char *result = nullptr; + + rc = dynType_parseWithStr("**D", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + rc = jsonSerializer_serialize(type, &valpp, &result); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error cannot serialize pointer to pointer", celix_err_popLastError()); + dynType_destroy(type); +} + +TEST_F(JsonSerializerTests, SerializationDeserilizationTest) { + dyn_type *type; + void *inst; + int rc; + + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr(example5_descriptor, nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + json_auto_t* root = json_loads(example5_input, JSON_DECODE_ANY, NULL); + ASSERT_NE(nullptr, root); + rc = jsonSerializer_deserializeJson(type, root, &inst); + ASSERT_EQ(0, rc); + check_example5(inst); + + json_auto_t* result = nullptr; + rc = jsonSerializer_serializeJson(type, inst, &result); + ASSERT_EQ(0, rc); + EXPECT_TRUE(json_equal(root, result)); + dynType_free(type, inst); + dynType_destroy(type); +} diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 7ae474719..4e17386d5 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -311,7 +311,7 @@ CELIX_DFI_EXPORT int dynType_sequence_reserve(const dyn_type* type, void* inst, * @return 0 if successful. * @retval 1 if the index is out of bounds. */ -CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, uint32_t index, void** valLoc); +CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, const void* seqLoc, uint32_t index, void** valLoc); /** * @brief Increase the length of the sequence by one and return the value location for the last element. diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index d9ce0206f..af950efdb 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -690,9 +690,9 @@ uint32_t dynType_sequence_length(const void *seqLoc) { return seq->len; } -int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, uint32_t index, void** out) { +int dynType_sequence_locForIndex(const dyn_type* type, const void* seqLoc, uint32_t index, void** out) { assert(type->type == DYN_TYPE_SEQUENCE); - struct generic_sequence* seq = seqLoc; + const struct generic_sequence* seq = seqLoc; size_t itemSize = dynType_size(type->sequence.itemType); diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index c271a0277..f6f964f9b 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -20,7 +20,6 @@ #include "json_serializer.h" #include "dyn_type.h" #include "dyn_type_common.h" -#include "dyn_interface.h" #include "celix_err.h" #include @@ -34,9 +33,9 @@ static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void static int jsonSerializer_parseAny(const dyn_type* type, void* input, json_t* val); static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out); -static int jsonSerializer_writeAny(const dyn_type* type, void* input, json_t** val); -static int jsonSerializer_writeComplex(const dyn_type* type, void* input, json_t** val); -static int jsonSerializer_writeSequence(const dyn_type* type, void* input, json_t** out); +static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** val); +static int jsonSerializer_writeComplex(const dyn_type* type, const void* input, json_t** val); +static int jsonSerializer_writeSequence(const dyn_type* type, const void* input, json_t** out); static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t** out); @@ -246,17 +245,13 @@ static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void } int jsonSerializer_serialize(const dyn_type* type, const void* input, char** output) { - int status = OK; - - json_t* root = NULL; - status = jsonSerializer_serializeJson(type, input, &root); - - if (status == OK) { - *output = json_dumps(root, JSON_COMPACT | JSON_ENCODE_ANY); - json_decref(root); + int status; + json_auto_t* root = NULL; + if ((status = jsonSerializer_serializeJson(type, input, &root)) != OK) { + return status; } - - return status; + *output = json_dumps(root, JSON_COMPACT | JSON_ENCODE_ANY); + return *output != NULL ? OK : ERROR; } static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out) { @@ -278,89 +273,72 @@ int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t while (real->type == DYN_TYPE_REF) { real = real->ref.ref; } - return jsonSerializer_writeAny(real, (void*)input /*TODO update static function to take const void**/, out); + return jsonSerializer_writeAny(real, input, out); } -static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **out) { +static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** out) { int status = OK; int descriptor = dynType_descriptorType(type); - json_t *val = NULL; - const dyn_type *subType = NULL; - - bool *z; //Z - float *f; //F - double *d; //D - char *b; //B - int *n; //N - int16_t *s; //S - int32_t *i; //I - int32_t *e; //E - int64_t *l; //J - uint8_t *ub; //b - uint16_t *us; //s - uint32_t *ui; //i - uint64_t *ul; //j + json_auto_t* val = NULL; + const dyn_type* subType = NULL; switch (descriptor) { case 'Z' : - z = input; - val = json_boolean((bool)*z); + val = json_boolean(*(const bool*)input); break; case 'B' : - b = input; - val = json_integer((json_int_t)*b); + val = json_integer((json_int_t)*(const char*)input); break; case 'S' : - s = input; - val = json_integer((json_int_t)*s); + val = json_integer((json_int_t)*(const int16_t*)input); break; case 'I' : - i = input; - val = json_integer((json_int_t)*i); + val = json_integer((json_int_t)*(const int32_t*)input); break; case 'J' : - l = input; - val = json_integer((json_int_t)*l); + val = json_integer((json_int_t)*(const int64_t*)input); break; case 'b' : - ub = input; - val = json_integer((json_int_t)*ub); + val = json_integer((json_int_t)*(const uint8_t*)input); break; case 's' : - us = input; - val = json_integer((json_int_t)*us); + val = json_integer((json_int_t)*(const uint16_t*)input); break; case 'i' : - ui = input; - val = json_integer((json_int_t)*ui); + val = json_integer((json_int_t)*(const uint32_t *)input); break; case 'j' : - ul = input; - val = json_integer((json_int_t)*ul); + val = json_integer((json_int_t)*(const uint64_t*)input); break; case 'N' : - n = input; - val = json_integer((json_int_t)*n); + val = json_integer((json_int_t)*(const int*)input); break; case 'F' : - f = input; - val = json_real((double) *f); + val = json_real((double) *(const float*)input); break; case 'D' : - d = input; - val = json_real(*d); + val = json_real(*(const double*)input); break; case 't' : val = json_string(*(const char **) input); break; case 'E': - e = input; - status = jsonSerializer_writeEnum(type, *e, &val); + status = jsonSerializer_writeEnum(type, *(const int32_t*)input, &val); break; case '*' : subType = dynType_typedPointer_getTypedType(type); - status = jsonSerializer_writeAny(subType, *(void **)input, &val); + if (dynType_ffiType(subType) != &ffi_type_pointer) { + const void* inputValue = *(const void**)input; + if (inputValue) { + status = jsonSerializer_writeAny(subType, inputValue, &val); + } else { + val = json_null(); + } + } else { + status = ERROR; + celix_err_pushf("Error cannot serialize pointer to pointer"); + } break; case '{' : status = jsonSerializer_writeComplex(type, input, &val); @@ -373,96 +351,76 @@ static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **o status = ERROR; break; } - - if (status == OK && val != NULL) { - *out = val; + if (status != OK) { + return status; } - - return status; + *out = celix_steal_ptr(val); + return *out != NULL ? OK : ERROR; } -static int jsonSerializer_writeSequence(const dyn_type *type, void *input, json_t **out) { +static int jsonSerializer_writeSequence(const dyn_type* type, const void* input, json_t** out) { assert(dynType_type(type) == DYN_TYPE_SEQUENCE); - int status = OK; - json_t *array = json_array(); - const dyn_type *itemType = dynType_sequence_itemType(type); + json_auto_t* array = json_array(); + if (array == NULL) { + return ERROR; + } + const dyn_type* itemType = dynType_sequence_itemType(type); uint32_t len = dynType_sequence_length(input); - uint32_t i = 0; - void *itemLoc = NULL; - json_t *item = NULL; - for (i = 0; i < len; i += 1) { - item = NULL; - status = dynType_sequence_locForIndex(type, input, i, &itemLoc); - if (status == OK) { - status = jsonSerializer_writeAny(itemType, itemLoc, &item); - if (status == OK) { - json_array_append(array, item); - json_decref(item); - } + for (uint32_t i = 0; i < len; i += 1) { + int status = OK; + void* itemLoc = NULL; + json_t* item = NULL; + if ((status = dynType_sequence_locForIndex(type, input, i, &itemLoc)) != OK) { + celix_err_push("Cannot serialize invalid sequence"); + return status; } - - if (status != OK) { - break; + if ((status = jsonSerializer_writeAny(itemType, itemLoc, &item)) != OK) { + return status; + } + if ((json_array_append_new(array, item)) != 0) { + return ERROR; } } - if (status == OK && array != NULL) { - *out = array; - } else { - *out = NULL; - json_decref(array); - } - - return status; + *out = celix_steal_ptr(array); + return OK; } -static int jsonSerializer_writeComplex(const dyn_type *type, void *input, json_t **out) { +static int jsonSerializer_writeComplex(const dyn_type* type, const void* input, json_t** out) { assert(dynType_type(type) == DYN_TYPE_COMPLEX); - int status = OK; - json_t *val = json_object(); - struct complex_type_entry *entry = NULL; - const struct complex_type_entries_head *entries = dynType_complex_entries(type); - int index = -1; + json_auto_t* val = json_object(); + if (val == NULL) { + return ERROR; + } + struct complex_type_entry* entry = NULL; + const struct complex_type_entries_head* entries = dynType_complex_entries(type); + int index = 0; TAILQ_FOREACH(entry, entries, entries) { - void *subLoc = NULL; - json_t *subVal = NULL; + int status; + void* subLoc = NULL; + json_t* subVal = NULL; const dyn_type* subType = NULL; - index = dynType_complex_indexForName(type, entry->name); - if (index < 0) { - celix_err_pushf("Cannot find index for member '%s'", entry->name); - status = ERROR; - } - if(status == OK){ - subLoc = dynType_complex_valLocAt(type, index, input); - } - if (status == OK) { - subType = dynType_complex_dynTypeAt(type, index); - } - if (status == OK) { - status = jsonSerializer_writeAny(subType, subLoc, &subVal); + if (entry->name == NULL) { + celix_err_push("Unamed field unsupported"); + return ERROR; } - if (status == OK) { - json_object_set(val, entry->name, subVal); - json_decref(subVal); + subLoc = dynType_complex_valLocAt(type, index, (void*)input); + subType = dynType_complex_dynTypeAt(type, index); + if ((status = jsonSerializer_writeAny(subType, subLoc, &subVal)) != OK) { + return status; } - - if (status != OK) { - break; + if (json_object_set_new(val, entry->name, subVal) != 0) { + return ERROR; } + index++; } - if (status == OK && val != NULL) { - *out = val; - } else { - *out = NULL; - json_decref(val); - } - - return status; + *out = celix_steal_ptr(val); + return OK; } static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t **out) { @@ -476,7 +434,7 @@ static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, js TAILQ_FOREACH(entry, &type->metaProperties, entries) { if (0 == strcmp(enum_value_str, entry->value)) { *out = json_string((const char*)entry->name); - return OK; + return *out != NULL ? OK : ERROR; } } diff --git a/libs/error_injector/jansson/CMakeLists.txt b/libs/error_injector/jansson/CMakeLists.txt index 73fec4035..9365037ac 100644 --- a/libs/error_injector/jansson/CMakeLists.txt +++ b/libs/error_injector/jansson/CMakeLists.txt @@ -20,9 +20,18 @@ find_package(jansson REQUIRED) add_library(jansson_ei STATIC src/jansson_ei.cc) target_include_directories(jansson_ei PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) -target_link_libraries(jansson_ei PUBLIC Celix::error_injector jansson::jansson) +target_link_libraries(jansson_ei + PUBLIC Celix::error_injector jansson::jansson + PRIVATE Celix::utils #for celix_cleanup.h +) target_link_options(jansson_ei INTERFACE LINKER:--wrap,json_array_size + LINKER:--wrap,json_dumps + LINKER:--wrap,json_object + LINKER:--wrap,json_object_set_new + LINKER:--wrap,json_array + LINKER:--wrap,json_array_append_new + LINKER:--wrap,json_integer ) add_library(Celix::jansson_ei ALIAS jansson_ei) diff --git a/libs/error_injector/jansson/include/jansson_ei.h b/libs/error_injector/jansson/include/jansson_ei.h index a33693719..504638529 100644 --- a/libs/error_injector/jansson/include/jansson_ei.h +++ b/libs/error_injector/jansson/include/jansson_ei.h @@ -26,6 +26,12 @@ extern "C" { #include "celix_error_injector.h" CELIX_EI_DECLARE(json_array_size, size_t); +CELIX_EI_DECLARE(json_dumps, char*); +CELIX_EI_DECLARE(json_object, json_t*); +CELIX_EI_DECLARE(json_object_set_new, int); +CELIX_EI_DECLARE(json_array, json_t*); +CELIX_EI_DECLARE(json_array_append_new, int); +CELIX_EI_DECLARE(json_integer, json_t*); #ifdef __cplusplus } diff --git a/libs/error_injector/jansson/src/jansson_ei.cc b/libs/error_injector/jansson/src/jansson_ei.cc index df3a8ed02..cca486d7e 100644 --- a/libs/error_injector/jansson/src/jansson_ei.cc +++ b/libs/error_injector/jansson/src/jansson_ei.cc @@ -18,6 +18,8 @@ */ #include "jansson_ei.h" +#include "celix_cleanup.h" +#include extern "C" { @@ -28,4 +30,48 @@ size_t __wrap_json_array_size(const json_t *array) { return __real_json_array_size(array); } +char* __real_json_dumps(const json_t *json, size_t flags); +CELIX_EI_DEFINE(json_dumps, char*) +char* __wrap_json_dumps(const json_t *json, size_t flags) { + CELIX_EI_IMPL(json_dumps); + return __real_json_dumps(json, flags); +} + +json_t* __real_json_object(void); +CELIX_EI_DEFINE(json_object, json_t*) +json_t* __wrap_json_object(void) { + CELIX_EI_IMPL(json_object); + return __real_json_object(); +} + +int __real_json_object_set_new(json_t *object, const char *key, json_t *value); +CELIX_EI_DEFINE(json_object_set_new, int) +int __wrap_json_object_set_new(json_t *object, const char *key, json_t *value) { + json_auto_t* val = value; + CELIX_EI_IMPL(json_object_set_new); + return __real_json_object_set_new(object, key, celix_steal_ptr(val)); +} + +json_t* __real_json_array(void); +CELIX_EI_DEFINE(json_array, json_t*) +json_t* __wrap_json_array(void) { + CELIX_EI_IMPL(json_array); + return __real_json_array(); +} + +int __real_json_array_append_new(json_t *array, json_t *value); +CELIX_EI_DEFINE(json_array_append_new, int) +int __wrap_json_array_append_new(json_t *array, json_t *value) { + json_auto_t *val = value; + CELIX_EI_IMPL(json_array_append_new); + return __real_json_array_append_new(array, celix_steal_ptr(val)); +} + +json_t* __real_json_integer(json_int_t value); +CELIX_EI_DEFINE(json_integer, json_t*) +json_t* __wrap_json_integer(json_int_t value) { + CELIX_EI_IMPL(json_integer); + return __real_json_integer(value); +} + } \ No newline at end of file From 7f1391d4c8f6a1b12a4ff7e5574107193be1e634 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 19 Jan 2024 15:56:57 +0800 Subject: [PATCH 38/62] Add more serialization tests. --- libs/dfi/gtest/src/json_serializer_tests.cpp | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index 449ab6fe0..3c88d40dc 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -869,10 +869,41 @@ TEST_F(JsonSerializerTests, WriteSequenceFailed) { writeSequenceFailed(); } +TEST_F(JsonSerializerTests, WriteSequenceFailed2) { + dyn_type* type = nullptr; + char* result = nullptr; + int rc = dynType_parseWithStr(R"([**D)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + double** input[] = {nullptr}; + struct { + uint32_t cap; + uint32_t len; + double*** buf; + }seq{1,1,input}; + rc = jsonSerializer_serialize(type, &seq, &result); + ASSERT_EQ(1, rc); + celix_err_printErrors(stderr, nullptr, nullptr); + dynType_destroy(type); +} + TEST_F(JsonSerializerTests, WriteComplexFailed) { writeComplexFailed(); } +TEST_F(JsonSerializerTests, WriteComplexFailed2) { + dyn_type *type = nullptr; + char *result = nullptr; + int rc = dynType_parseWithStr(R"({**D a})", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + struct { + double** a; + }input{nullptr}; + rc = jsonSerializer_serialize(type, &input, &result); + ASSERT_EQ(1, rc); + celix_err_printErrors(stderr, nullptr, nullptr); + dynType_destroy(type); +} + TEST_F(JsonSerializerTests, WriteEnumFailed) { writeEnumFailed(); } From 6fffb96b660cac7a77a55b3bfa829d9b4aa8f724 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sat, 20 Jan 2024 15:20:04 +0800 Subject: [PATCH 39/62] Support nullptr when serialize text. --- libs/dfi/gtest/src/json_serializer_tests.cpp | 40 ++++++++++++++++++++ libs/dfi/src/json_serializer.c | 6 ++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index 3c88d40dc..20bdf7253 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -946,3 +946,43 @@ TEST_F(JsonSerializerTests, SerializationDeserilizationTest) { dynType_free(type, inst); dynType_destroy(type); } + +TEST_F(JsonSerializerTests, SerializationDeserilizationNullStringTest) { + dyn_type *type; + void *inst; + int rc; + rc = dynType_parseWithStr("t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + json_auto_t* root = json_null(); + rc = jsonSerializer_deserializeJson(type, root, &inst); + ASSERT_EQ(0, rc); + EXPECT_EQ(nullptr, *(char**)inst); + + json_auto_t* result = nullptr; + rc = jsonSerializer_serializeJson(type, inst, &result); + ASSERT_EQ(0, rc); + EXPECT_TRUE(json_equal(root, result)); + dynType_free(type, inst); + dynType_destroy(type); + +} + +TEST_F(JsonSerializerTests, SerializationDeserilizationStringTest) { + dyn_type *type; + void *inst; + int rc; + rc = dynType_parseWithStr("t", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + json_auto_t* root = json_loads(R"("hello")", JSON_DECODE_ANY, NULL); + ASSERT_NE(nullptr, root); + rc = jsonSerializer_deserializeJson(type, root, &inst); + ASSERT_EQ(0, rc); + EXPECT_STREQ("hello", *(char**)inst); + + json_auto_t* result = nullptr; + rc = jsonSerializer_serializeJson(type, inst, &result); + ASSERT_EQ(0, rc); + EXPECT_TRUE(json_equal(root, result)); + dynType_free(type, inst); + dynType_destroy(type); +} diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index f6f964f9b..d98451fe1 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -320,9 +320,11 @@ static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json case 'D' : val = json_real(*(const double*)input); break; - case 't' : - val = json_string(*(const char **) input); + case 't' : { + const char *strValue = *(const char **) input; + val = (strValue != NULL) ? json_string(strValue) : json_null(); break; + } case 'E': status = jsonSerializer_writeEnum(type, *(const int32_t*)input, &val); break; From 1220311cf33f212eeacdb8f57c3ed7d4175fad6d Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 22 Jan 2024 11:47:58 +0800 Subject: [PATCH 40/62] Optimize dyn_function's arguments handling. 1. Removed unused argName. 2. Add dynFunction_arguments. 3. Avoid list iteration in dynFunction_nrOfArguments. --- libs/dfi/gtest/src/dyn_example_functions.c | 4 +++ libs/dfi/gtest/src/dyn_example_functions.h | 2 ++ libs/dfi/gtest/src/dyn_function_ei_tests.cc | 7 ------ libs/dfi/gtest/src/dyn_function_tests.cpp | 27 +++++++++++++++++++++ libs/dfi/include/dyn_function.h | 15 ++++++++++++ libs/dfi/src/dyn_function.c | 21 ++++------------ libs/dfi/src/dyn_function_common.h | 23 ++++++------------ 7 files changed, 60 insertions(+), 39 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_example_functions.c b/libs/dfi/gtest/src/dyn_example_functions.c index 091382a18..3f95c482c 100644 --- a/libs/dfi/gtest/src/dyn_example_functions.c +++ b/libs/dfi/gtest/src/dyn_example_functions.c @@ -46,3 +46,7 @@ void example5Func(const char *s1, char *s2) { assert(strncmp("s1", s1, 5) == 0); assert(strncmp("s2", s2, 5) == 0); } + +int32_t example6Func() { + return 1234; +} \ No newline at end of file diff --git a/libs/dfi/gtest/src/dyn_example_functions.h b/libs/dfi/gtest/src/dyn_example_functions.h index 41700467b..8d50e4e30 100644 --- a/libs/dfi/gtest/src/dyn_example_functions.h +++ b/libs/dfi/gtest/src/dyn_example_functions.h @@ -50,6 +50,8 @@ void example4Func(struct tst_seq seq); #define EXAMPLE5_DESCRIPTOR "example(#const=true;tt)V" void example5Func(const char *s1, char *s2); +#define EXAMPLE6_DESCRIPTOR "example()I" +int32_t example6Func(); #ifdef __cplusplus diff --git a/libs/dfi/gtest/src/dyn_function_ei_tests.cc b/libs/dfi/gtest/src/dyn_function_ei_tests.cc index 1327c5f8c..86d5df180 100644 --- a/libs/dfi/gtest/src/dyn_function_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_function_ei_tests.cc @@ -68,13 +68,6 @@ TEST_F(DynFunctionErrorInjectionTestSuite, ParseError) { result += strerror(ENOMEM); EXPECT_STREQ(result.c_str(), celix_err_popLastError()); - celix_ei_expect_asprintf((void*) dynFunction_parse, 1, -1, 3); - rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); - EXPECT_NE(0, rc); - EXPECT_STREQ("Error parsing descriptor", celix_err_popLastError()); - EXPECT_STREQ("Error allocating argument name", celix_err_popLastError()); - - celix_ei_expect_calloc((void*)dynFunction_parse, 1, nullptr, 3); rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); EXPECT_NE(0, rc); diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 4dcb28f13..529289007 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -48,6 +48,13 @@ TEST_F(DynFunctionTests, DynFuncTest1) { rc = dynFunction_parseWithStr(EXAMPLE1_DESCRIPTOR, nullptr, &dynFunc); ASSERT_EQ(0, rc); EXPECT_TRUE(dynFunction_hasReturn(dynFunc)); + EXPECT_EQ(3, dynFunction_nrOfArguments(dynFunc)); + auto args = dynFunction_arguments(dynFunc); + dyn_function_argument_type* arg = NULL; + TAILQ_FOREACH(arg, args, entries) { + EXPECT_EQ(DYN_FUNCTION_ARGUMENT_META__STD, arg->argumentMeta); + EXPECT_EQ('I', dynType_descriptorType(arg->type)); + } ffi_sarg rVal = 0; int32_t a = 2; @@ -190,6 +197,7 @@ TEST_F(DynFunctionTests, DynFuncTest4) { rc = dynFunction_parseWithStr(EXAMPLE4_DESCRIPTOR, nullptr, &dynFunc); ASSERT_EQ(0, rc); EXPECT_FALSE(dynFunction_hasReturn(dynFunc)); + EXPECT_EQ(1, dynFunction_nrOfArguments(dynFunc)); double buf[4]; buf[0] = 1.1; @@ -237,6 +245,25 @@ TEST_F(DynFunctionTests, DynFuncTest5) { EXPECT_TRUE(func_test5()); } +TEST_F(DynFunctionTests, DynFuncTest6) { + dyn_function_type *dynFunc = nullptr; + void (*fp)(void) = (void(*)(void)) example6Func; + int rc; + + rc = dynFunction_parseWithStr(EXAMPLE6_DESCRIPTOR, nullptr, &dynFunc); + ASSERT_EQ(0, rc); + EXPECT_TRUE(dynFunction_hasReturn(dynFunc)); + EXPECT_EQ(0, dynFunction_nrOfArguments(dynFunc)); + auto args = dynFunction_arguments(dynFunc); + EXPECT_TRUE(TAILQ_EMPTY(args)); + + ffi_sarg rVal = 0; + rc = dynFunction_call(dynFunc, fp, &rVal, nullptr); + dynFunction_destroy(dynFunc); + EXPECT_EQ(0, rc); + EXPECT_EQ(1234, rVal); +} + TEST_F(DynFunctionTests, InvalidDynFuncTest) { dyn_function_type *dynFunc = nullptr; diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index 3f8002840..72d992781 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -52,6 +52,15 @@ enum dyn_function_argument_meta { DYN_FUNCTION_ARGUMENT_META__OUTPUT = 3 }; +typedef struct _dyn_function_argument_type dyn_function_argument_type; +TAILQ_HEAD(dyn_function_arguments_head,_dyn_function_argument_type); +struct _dyn_function_argument_type { + int index; + enum dyn_function_argument_meta argumentMeta; + dyn_type* type; + TAILQ_ENTRY(_dyn_function_argument_type) entries; +}; + /** * @brief Creates a dyn_function_type according to the given function descriptor stream. * @@ -107,6 +116,12 @@ CELIX_DFI_EXPORT const dyn_type* dynFunction_argumentTypeForIndex(const dyn_func */ CELIX_DFI_EXPORT enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(const dyn_function_type* dynFunc, int argumentNr); +/** + * @brief Returns the argument list for the given dynamic function type instance. + * @note It always returns valid list. + */ +CELIX_DFI_EXPORT const struct dyn_function_arguments_head* dynFunction_arguments(const dyn_function_type* dynFunc); + /** * @brief Returns the return value type for the given dynamic function type instance. * @param[in] dynFunc The dynamic type instance for function. diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index a68f4b016..ee23681db 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -107,18 +107,12 @@ static int dynFunction_parseDescriptor(dyn_function_type* dynFunc, FILE* descrip ungetc(nextChar, descriptor); celix_autoptr(dyn_type) type = NULL; - celix_autofree char* argName = NULL; dyn_function_argument_type* arg = NULL; if ((status = dynType_parse(descriptor, NULL, dynFunc->refTypes, &type)) != OK) { return status; } - if (asprintf(&argName, "arg%04i", index) == -1) { - celix_err_pushf("Error allocating argument name"); - return MEM_ERROR; - } - arg = calloc(1, sizeof(*arg)); if (arg == NULL) { celix_err_pushf("Error allocating arg"); @@ -126,7 +120,6 @@ static int dynFunction_parseDescriptor(dyn_function_type* dynFunc, FILE* descrip } arg->index = index++; arg->type = celix_steal_ptr(type); - arg->name = celix_steal_ptr(argName); TAILQ_INSERT_TAIL(&dynFunc->arguments, arg, entries); @@ -154,6 +147,9 @@ enum dyn_function_argument_meta dynFunction_argumentMetaForIndex(const dyn_funct return result; } +const struct dyn_function_arguments_head* dynFunction_arguments(const dyn_function_type* dynFunc) { + return &dynFunc->arguments; +} static int dynFunction_initCif(dyn_function_type* dynFunc) { unsigned int nargs = 0; @@ -200,9 +196,6 @@ void dynFunction_destroy(dyn_function_type* dynFunc) { dyn_function_argument_type* tmp = NULL; entry = TAILQ_FIRST(&dynFunc->arguments); while (entry != NULL) { - if (entry->name != NULL) { - free(entry->name); - } dynType_destroy(entry->type); tmp = entry; entry = TAILQ_NEXT(entry, entries); @@ -256,12 +249,8 @@ int dynFunction_getFnPointer(const dyn_function_type* dynFunc, void (**fn)(void) } int dynFunction_nrOfArguments(const dyn_function_type* dynFunc) { - int count = 0; - dyn_function_argument_type *entry = NULL; - TAILQ_FOREACH(entry, &dynFunc->arguments, entries) { - count += 1; - } - return count; + dyn_function_argument_type* last = TAILQ_LAST(&dynFunc->arguments, dyn_function_arguments_head); + return last == NULL ? 0 : (last->index+1); } const dyn_type* dynFunction_argumentTypeForIndex(const dyn_function_type* dynFunc, int argumentNr) { diff --git a/libs/dfi/src/dyn_function_common.h b/libs/dfi/src/dyn_function_common.h index 8d96f7cb8..68c2a844a 100644 --- a/libs/dfi/src/dyn_function_common.h +++ b/libs/dfi/src/dyn_function_common.h @@ -33,27 +33,18 @@ extern "C" { #endif struct _dyn_function_type { - char *name; - struct types_head *refTypes; //NOTE not owned - TAILQ_HEAD(,_dyn_function_argument_type) arguments; - ffi_type **ffiArguments; - dyn_type *funcReturn; + char* name; + struct types_head* refTypes; //NOTE not owned + struct dyn_function_arguments_head arguments; + ffi_type** ffiArguments; + dyn_type* funcReturn; ffi_cif cif; //closure part - ffi_closure *ffiClosure; + ffi_closure* ffiClosure; void (*fn)(void); void *userData; - void (*bind)(void *userData, void *args[], void *ret); -}; - -typedef struct _dyn_function_argument_type dyn_function_argument_type; -struct _dyn_function_argument_type { - int index; - char *name; - enum dyn_function_argument_meta argumentMeta; - dyn_type *type; - TAILQ_ENTRY(_dyn_function_argument_type) entries; + void (*bind)(void* userData, void* args[], void* ret); }; #ifdef __cplusplus From 74f1243d2990e5b6bb95d8823092b0d719fbb2ed Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 22 Jan 2024 12:33:26 +0800 Subject: [PATCH 41/62] Avoid list iteration in dynInterface_nrOfMethods. --- .../dfi/gtest/descriptors/example6.descriptor | 6 ++--- libs/dfi/gtest/src/dyn_interface_tests.cpp | 24 +++++++++++++++++++ libs/dfi/src/dyn_interface.c | 8 ++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/libs/dfi/gtest/descriptors/example6.descriptor b/libs/dfi/gtest/descriptors/example6.descriptor index 4ce4e9357..5f9a06b63 100644 --- a/libs/dfi/gtest/descriptors/example6.descriptor +++ b/libs/dfi/gtest/descriptors/example6.descriptor @@ -1,9 +1,9 @@ :header type=interface -name=example6 +name=calculator version=1.0.0 :annotations +classname=org.example.Calculator :types -CptData={Dt[D#v1=1;#v2=2;E d t s e} +StatsResult={DDD[D average min max input} :methods -compatibility=cpt(#am=handle;PLCptData;#am=out;*LCptData;)N diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index a340eb3d1..efd08dbfe 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -258,3 +258,27 @@ TEST_F(DynInterfaceTests, testInvalid) { testInvalid(); } + +TEST_F(DynInterfaceTests, testEmptyMethod) { + int status = 0; + dyn_interface_type *dynIntf = NULL; + FILE *desc = fopen("descriptors/example6.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_EQ(0, status); + fclose(desc); + + const char *name = dynInterface_getName(dynIntf); + ASSERT_STREQ("calculator", name); + + checkInterfaceVersion(dynIntf,"1.0.0"); + + const struct methods_head* list = dynInterface_methods(dynIntf); + ASSERT_TRUE(list != NULL); + + int count = dynInterface_nrOfMethods(dynIntf); + ASSERT_EQ(0, count); + + dynInterface_destroy(dynIntf); +} + diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 819532705..1e95e2ae3 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -184,10 +184,6 @@ const struct methods_head* dynInterface_methods(const dyn_interface_type* intf) } int dynInterface_nrOfMethods(const dyn_interface_type* intf) { - int count = 0; - struct method_entry* entry = NULL; - TAILQ_FOREACH(entry, &intf->methods, entries) { - count +=1; - } - return count; + struct method_entry* last = TAILQ_LAST(&intf->methods, methods_head); + return last == NULL ? 0 : (last->index+1); } From 6223856ecfb5384c24eecb6de507093114b3ba9a Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 22 Jan 2024 18:27:19 +0800 Subject: [PATCH 42/62] Add dynType_realType for code deduplication. --- libs/dfi/include/dyn_type.h | 5 +++ libs/dfi/src/dyn_type.c | 57 +++++++++++----------------------- libs/dfi/src/dyn_type_common.c | 5 +-- libs/dfi/src/json_serializer.c | 12 ++----- 4 files changed, 26 insertions(+), 53 deletions(-) diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 4e17386d5..8d6039e91 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -218,6 +218,11 @@ CELIX_DFI_EXPORT size_t dynType_size(const dyn_type* type); */ CELIX_DFI_EXPORT int dynType_type(const dyn_type* type); +/** + * Returns the real (non-reference) type of the dyn type. + */ +CELIX_DFI_EXPORT const dyn_type* dynType_realType(const dyn_type* type); + /** * Returns the char identifier of the dyn type type. * E.g. 'D' for a double, or '{' for a complex type. diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index af950efdb..b2019f9eb 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -497,11 +497,8 @@ static void dynType_clearTypedPointer(dyn_type* type) { } int dynType_alloc(const dyn_type* type, void** bufLoc) { - const dyn_type* current = type; + const dyn_type* current = dynType_realType(type); - while (current->type == DYN_TYPE_REF) { - current = current->ref.ref; - } void* inst = calloc(1, current->ffiType->size); if (inst == NULL) { celix_err_pushf("Error allocating memory for type '%c'", current->descriptor); @@ -534,11 +531,7 @@ int dynType_complex_indexForName(const dyn_type* type, const char* name) { const dyn_type* dynType_complex_dynTypeAt(const dyn_type* type, int index) { assert(type->type == DYN_TYPE_COMPLEX); assert(index >= 0); - dyn_type* sub = type->complex.types[index]; - while (sub->type == DYN_TYPE_REF) { - sub = sub->ref.ref; - } - return sub; + return dynType_realType(type->complex.types[index]); } int dynType_complex_setValueAt(const dyn_type* type, int index, void* start, const void* in) { @@ -629,9 +622,7 @@ static void dynType_deepFree(const dyn_type* type, void* loc, bool alsoDeleteSel if (loc != NULL) { const dyn_type* subType = NULL; char* text = NULL; - while (type->type == DYN_TYPE_REF) { - type = type->ref.ref; - } + type = dynType_realType(type); switch (type->type) { case DYN_TYPE_COMPLEX : dynType_freeComplexType(type, loc); @@ -727,11 +718,7 @@ int dynType_sequence_increaseLengthAndReturnLastLoc(const dyn_type* type, void* const dyn_type* dynType_sequence_itemType(const dyn_type* type) { assert(type->type == DYN_TYPE_SEQUENCE); - dyn_type *itemType = type->sequence.itemType; - while (itemType->type == DYN_TYPE_REF) { - itemType = itemType->ref.ref; - } - return itemType; + return dynType_realType(type->sequence.itemType); } void dynType_simple_setValue(const dyn_type *type, void *inst, const void *in) { @@ -834,10 +821,7 @@ static unsigned short dynType_getOffset(const dyn_type* type, int index) { } size_t dynType_size(const dyn_type* type) { - const dyn_type* rType = type; - while (rType->type == DYN_TYPE_REF) { - rType = type->ref.ref; - } + const dyn_type* rType = dynType_realType(type); return rType->ffiType->size; } @@ -845,13 +829,18 @@ int dynType_type(const dyn_type* type) { return type->type; } +const dyn_type* dynType_realType(const dyn_type* type) { + const dyn_type* real = type; + while (real->type == DYN_TYPE_REF) { + real= real->ref.ref; + } + return real; + +} + const dyn_type* dynType_typedPointer_getTypedType(const dyn_type* type) { assert(type->type == DYN_TYPE_TYPED_POINTER); - dyn_type* typedType = type->typedPointer.typedType; - while (typedType->type == DYN_TYPE_REF) { - typedType = typedType->ref.ref; - } - return typedType; + return dynType_realType(type->typedPointer.typedType); } @@ -886,10 +875,7 @@ static void dynType_printDepth(int depth, FILE *stream) { } static void dynType_printAny(const char* name, const dyn_type* type, int depth, FILE *stream) { - const dyn_type* toPrint = type; - while (toPrint->type == DYN_TYPE_REF) { - toPrint = toPrint->ref.ref; - } + const dyn_type* toPrint = dynType_realType(type); name = (name != NULL) ? name : "(unnamed)"; switch(toPrint->type) { case DYN_TYPE_COMPLEX : @@ -998,10 +984,7 @@ static void dynType_printText(const char* name, const dyn_type* type, int depth, static void dynType_printTypes(const dyn_type* type, FILE* stream) { if (type->type == DYN_TYPE_REF) { - const dyn_type* real = type->ref.ref; - while (real->type == DYN_TYPE_REF) { - real = real->ref.ref; - } + const dyn_type* real = dynType_realType(type->ref.ref); dyn_type* parent = type->parent; struct type_entry* pentry = NULL; while (parent != NULL) { @@ -1016,11 +999,7 @@ static void dynType_printTypes(const dyn_type* type, FILE* stream) { struct type_entry* entry = NULL; TAILQ_FOREACH(entry, &type->nestedTypesHead, entries) { - dyn_type* toPrint = entry->type; - while (toPrint->type == DYN_TYPE_REF) { - toPrint = toPrint->ref.ref; - } - + const dyn_type* toPrint = dynType_realType(entry->type); switch(toPrint->type) { case DYN_TYPE_COMPLEX : dynType_printComplexType(toPrint, stream); diff --git a/libs/dfi/src/dyn_type_common.c b/libs/dfi/src/dyn_type_common.c index 64c2e8349..96ea84d11 100644 --- a/libs/dfi/src/dyn_type_common.c +++ b/libs/dfi/src/dyn_type_common.c @@ -52,9 +52,6 @@ dyn_type* dynType_findType(dyn_type *type, char *name) { } ffi_type* dynType_ffiType(const dyn_type * type) { - while (type->type == DYN_TYPE_REF) { - type = type->ref.ref; - } - return type->ffiType; + return dynType_realType(type)->ffiType; } diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index d98451fe1..ee8fa7a42 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -60,11 +60,7 @@ int jsonSerializer_deserialize(const dyn_type* type, const char* input, size_t l } int jsonSerializer_deserializeJson(const dyn_type* type, json_t* input, void** out) { - const dyn_type* real = type; - while (real->type == DYN_TYPE_REF) { - real = real->ref.ref; - } - return jsonSerializer_createType(real, input, out); + return jsonSerializer_createType(dynType_realType(type), input, out); } static int jsonSerializer_createType(const dyn_type* type, json_t* val, void** result) { @@ -269,11 +265,7 @@ static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, } int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t** out) { - const dyn_type* real = type; - while (real->type == DYN_TYPE_REF) { - real = real->ref.ref; - } - return jsonSerializer_writeAny(real, input, out); + return jsonSerializer_writeAny(dynType_realType(type), input, out); } static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** out) { From ba398d32f96dad3ca0f17e7ae9ff4aae97812e47 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 22 Jan 2024 20:38:24 +0800 Subject: [PATCH 43/62] Remove unnecessary calloc and fix crash caused by pointer reference in jsonRpc_call. --- .../dfi/gtest/descriptors/example7.descriptor | 13 + libs/dfi/gtest/src/json_rpc_tests.cpp | 56 ++ libs/dfi/include/json_rpc.h | 6 +- libs/dfi/src/json_rpc.c | 526 +++++++++--------- 4 files changed, 324 insertions(+), 277 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/example7.descriptor diff --git a/libs/dfi/gtest/descriptors/example7.descriptor b/libs/dfi/gtest/descriptors/example7.descriptor new file mode 100644 index 000000000..6729be3af --- /dev/null +++ b/libs/dfi/gtest/descriptors/example7.descriptor @@ -0,0 +1,13 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(#am=handle;PDD#am=pre;TOut=*D;lOut;)N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;TOut=*LStatsResult;;lOut;)N diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 91b6d9315..c2dc1cc6f 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -336,11 +336,27 @@ extern "C" { tst_serv serv {nullptr, addFailed, nullptr, nullptr, nullptr}; rc = jsonRpc_call(intf, &serv, R"({)", &result); + EXPECT_STREQ("Got json error: string or '}' expected near end of file", celix_err_popLastError()); ASSERT_EQ(1, rc); rc = jsonRpc_call(intf, &serv, R"({"a": [1.0,2.0]})", &result); + EXPECT_STREQ("Error getting method signature", celix_err_popLastError()); ASSERT_EQ(1, rc); + //request missing argument + rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;"})", &result); + EXPECT_STREQ("Error getting arguments array", celix_err_popLastError()); + ASSERT_EQ(1, rc); + + //request non-array argument + rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": "hello"})", &result); + EXPECT_STREQ("Error getting arguments array", celix_err_popLastError()); + ASSERT_EQ(1, rc); + + // argument number mismatch + //rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": []})", &result); + //ASSERT_EQ(1, rc); + //request argument type mismatch rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": [1.0]})", &result); ASSERT_EQ(1, rc); @@ -480,6 +496,7 @@ extern "C" { rc = jsonRpc_call(intf, &serv, R"({"m":"unknown", "a": [1.0,2.0]})", &result); ASSERT_EQ(1, rc); + EXPECT_STREQ("Cannot find method with sig 'unknown'", celix_err_popLastError()); dynInterface_destroy(intf); } @@ -785,6 +802,7 @@ class JsonRpcTests : public ::testing::Test { } ~JsonRpcTests() override { + celix_err_resetErrors(); } }; @@ -817,6 +835,25 @@ TEST_F(JsonRpcTests, handleTestOut) { handleTestOut(); } +TEST_F(JsonRpcTests, callPreReference) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/example7.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, add, nullptr, nullptr, nullptr}; + + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DD)D", "a": [1.0,2.0]})", &result); + ASSERT_EQ(0, rc); + ASSERT_TRUE(strstr(result, "3.0") != nullptr); + + free(result); + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, callPre) { callTestPreAllocated(); } @@ -829,6 +866,25 @@ TEST_F(JsonRpcTests, callOut) { callTestOutput(); } +TEST_F(JsonRpcTests, callOutReference) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/example7.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, stats}; + + rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": [[1.0,2.0]]})", &result); + ASSERT_EQ(0, rc); + ASSERT_TRUE(strstr(result, "1.5") != nullptr); + + free(result); + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, callTestInvalidRequest) { callTestInvalidRequest(); } diff --git a/libs/dfi/include/json_rpc.h b/libs/dfi/include/json_rpc.h index cdbf3fc33..77e8c434e 100644 --- a/libs/dfi/include/json_rpc.h +++ b/libs/dfi/include/json_rpc.h @@ -44,7 +44,7 @@ extern "C" { * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, char **out); +CELIX_DFI_EXPORT int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* request, char** out); /** * @brief Prepare a JSON-RPC request for a given function. @@ -60,7 +60,7 @@ CELIX_DFI_EXPORT int jsonRpc_call(dyn_interface_type *intf, void *service, const * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonRpc_prepareInvokeRequest(dyn_function_type *func, const char *id, void *args[], char **out); +CELIX_DFI_EXPORT int jsonRpc_prepareInvokeRequest(const dyn_function_type* func, const char* id, void* args[], char** out); /** * @brief Handle a JSON-RPC reply for a given function. @@ -74,7 +74,7 @@ CELIX_DFI_EXPORT int jsonRpc_prepareInvokeRequest(dyn_function_type *func, const * @return 0 if successful, otherwise 1. * */ -CELIX_DFI_EXPORT int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[], int *rsErrno); +CELIX_DFI_EXPORT int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* args[], int* rsErrno); #ifdef __cplusplus } diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 541cd9c3c..08d1a4aa4 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -26,7 +26,6 @@ #include "celix_err.h" #include -#include #include #include #include @@ -37,289 +36,268 @@ static int ERROR = 1; typedef void (*gen_func_type)(void); struct generic_service_layout { - void *handle; - gen_func_type methods[]; + void* handle; + gen_func_type methods[]; }; -int jsonRpc_call(dyn_interface_type *intf, void *service, const char *request, char **out) { - int status = OK; - - const dyn_type* returnType = NULL; - - json_error_t error; - json_t *js_request = json_loads(request, 0, &error); - json_t *arguments = NULL; - const char *sig; - if (js_request) { - if (json_unpack(js_request, "{s:s}", "m", &sig) != 0) { - celix_err_pushf("Got json error '%s'\n", error.text); - json_decref(js_request); - return ERROR; - } else { - arguments = json_object_get(js_request, "a"); - } - } else { - celix_err_pushf("Got json error '%s' for '%s'\n", error.text, request); - 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; - } - } - - if (method == NULL) { - status = ERROR; +int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* request, char** out) { + int status = OK; + + json_error_t error; + json_auto_t* js_request = json_loads(request, 0, &error); + if (js_request == NULL) { + celix_err_pushf("Got json error: %s", error.text); + return ERROR; + } + json_t* arguments = NULL; + const char* sig; + if (json_unpack(js_request, "{s:s}", "m", &sig) != 0) { + celix_err_push("Error getting method signature"); + return ERROR; + } + arguments = json_object_get(js_request, "a"); + if (arguments == NULL || !json_is_array(arguments)) { + celix_err_push("Error getting arguments array"); + 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; + } + } + + if (method == NULL) { celix_err_pushf("Cannot find method with sig '%s'", sig); - } - else if (status == OK) { - returnType = dynFunction_returnType(method->dynFunc); - } - - void (*fp)(void) = NULL; - void *handle = NULL; - if (status == OK) { - struct generic_service_layout *serv = service; - handle = serv->handle; - fp = serv->methods[method->index]; - } - - dyn_function_type *func = NULL; - int nrOfArgs = 0; - if (status == OK) { - nrOfArgs = dynFunction_nrOfArguments(entry->dynFunc); - func = entry->dynFunc; - } - - void *args[nrOfArgs]; - - json_t *value = NULL; - - int i; - int index = 0; - - void *ptr = NULL; - void *ptrToPtr = &ptr; - - //setup and deserialize input - for (i = 0; i < nrOfArgs; ++i) { - const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { - value = json_array_get(arguments, index++); - void *outPtr = NULL; + 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); + void* args[nrOfArgs]; + + json_t* value = NULL; + + int i; + int index = 0; + + void* ptr = NULL; + void* ptrToPtr = &ptr; + void* instPtr = NULL; + + //setup and deserialize input + for (i = 0; i < nrOfArgs; ++i) { + const dyn_type* argType = dynType_realType(dynFunction_argumentTypeForIndex(func, i)); + enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); + if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { + value = json_array_get(arguments, index++); + void* outPtr = NULL; status = jsonSerializer_deserializeJson(argType, value, &outPtr); args[i] = outPtr; - } else if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - void **instPtr = calloc(1, sizeof(void*)); - void *inst = NULL; - const dyn_type *subType = dynType_typedPointer_getTypedType(argType); + } else if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + void* inst = NULL; + const dyn_type *subType = dynType_typedPointer_getTypedType(argType); dynType_alloc(subType, &inst); - *instPtr = inst; - args[i] = instPtr; - } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - args[i] = &ptrToPtr; - } else if (meta == DYN_FUNCTION_ARGUMENT_META__HANDLE) { - args[i] = &handle; - } - - if (status != OK) { - break; - } - } - json_decref(js_request); - - if (status == OK) { - if (dynType_descriptorType(returnType) != 'N') { - //NOTE To be able to handle exception only N as returnType is supported - celix_err_pushf("Only interface methods with a native int are supported. Found type '%c'", (char)dynType_descriptorType(returnType)); - status = ERROR; - } - } - - ffi_sarg returnVal = 1; - - if (status == OK) { - status = dynFunction_call(func, fp, (void *) &returnVal, args); - } - - int funcCallStatus = (int)returnVal; + instPtr = inst; + args[i] = &instPtr; + } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { + args[i] = &ptrToPtr; + } else if (meta == DYN_FUNCTION_ARGUMENT_META__HANDLE) { + args[i] = &handle; + } + + if (status != OK) { + break; + } + } + json_decref(celix_steal_ptr(js_request)); + + ffi_sarg returnVal = 1; + + if (status == OK) { + status = dynFunction_call(func, fp, (void *) &returnVal, args); + } + + int funcCallStatus = (int)returnVal; //free input args - json_t *jsonResult = NULL; - for(i = 0; i < nrOfArgs; ++i) { - const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { - if (dynType_descriptorType(argType) == 't') { - const char* isConst = dynType_getMetaInfo(argType, "const"); - if (isConst != NULL && strncmp("true", isConst, 5) == 0) { - dynType_free(argType, args[i]); - } else { + json_t* jsonResult = NULL; + for(i = 0; i < nrOfArgs; ++i) { + const dyn_type* argType = dynFunction_argumentTypeForIndex(func, i); + enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); + if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { + if (dynType_descriptorType(argType) == 't') { + const char* isConst = dynType_getMetaInfo(argType, "const"); + if (isConst != NULL && strncmp("true", isConst, 5) == 0) { + dynType_free(argType, args[i]); + } else { //char* -> callee is now owner, no free for char seq needed //will free the actual pointer free(args[i]); - } - } else { + } + } else { dynType_free(argType, args[i]); } - } - } - - //serialize and free output - for (i = 0; i < nrOfArgs; i += 1) { - const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - if (funcCallStatus == 0 && status == OK) { - status = jsonSerializer_serializeJson(argType, args[i], &jsonResult); - } - const dyn_type* subType = dynType_typedPointer_getTypedType(argType); - void **ptrToInst = (void**)args[i]; - dynType_free(subType, *ptrToInst); - free(ptrToInst); - } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - if (funcCallStatus == 0 && ptr != NULL) { - const dyn_type *typedType = NULL; - if (status == OK) { + } + } + + //serialize and free output + for (i = 0; i < nrOfArgs; i += 1) { + const dyn_type* argType = dynType_realType(dynFunction_argumentTypeForIndex(func, i)); + enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); + if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + if (funcCallStatus == 0 && status == OK) { + status = jsonSerializer_serializeJson(argType, args[i], &jsonResult); + } + const dyn_type* subType = dynType_typedPointer_getTypedType(argType); + void** ptrToInst = (void**)args[i]; + dynType_free(subType, *ptrToInst); + } 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) { + } + 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); - } - } - - } - } - - if (status != OK) { - break; - } - } - - char *response = NULL; - if (status == OK) { - json_t *payload = json_object(); - if (funcCallStatus == 0) { - if (jsonResult == NULL) { - //ignore -> no result - } else { + } + + if(status == OK){ + status = jsonSerializer_serializeJson(typedTypedType, ptr, &jsonResult); + } + + if (status == OK) { + dynType_free(typedTypedType, ptr); + } + } + + } + } + + if (status != OK) { + break; + } + } + + char* response = NULL; + if (status == OK) { + json_t* payload = json_object(); + if (funcCallStatus == 0) { + if (jsonResult == NULL) { + //ignore -> no result + } else { json_object_set_new_nocheck(payload, "r", jsonResult); - } - } else { + } + } else { json_object_set_new_nocheck(payload, "e", json_integer(funcCallStatus)); - } - response = json_dumps(payload, JSON_COMPACT | JSON_ENCODE_ANY);//Should use JSON_COMPACT, it can reduce the size of the JSON string. - json_decref(payload); - } - - if (status == OK) { - *out = response; - } else { - free(response); - } - - return status; + } + //use JSON_COMPACT to reduce the size of the JSON string. + response = json_dumps(payload, JSON_COMPACT | JSON_ENCODE_ANY); + json_decref(payload); + } + + if (status == OK) { + *out = response; + } else { + free(response); + } + + return status; } -int jsonRpc_prepareInvokeRequest(dyn_function_type *func, const char *id, void *args[], char **out) { - int status = OK; +int jsonRpc_prepareInvokeRequest(const dyn_function_type* func, const char* id, void* args[], char** out) { + int status = OK; - json_t *invoke = json_object(); + json_t* invoke = json_object(); json_object_set_new_nocheck(invoke, "m", json_string(id)); - json_t *arguments = json_array(); + json_t* arguments = json_array(); json_object_set_new_nocheck(invoke, "a", arguments); - int i; - int nrOfArgs = dynFunction_nrOfArguments(func); - for (i = 0; i < nrOfArgs; i +=1) { - const dyn_type *type = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { - json_t *val = NULL; + int i; + int nrOfArgs = dynFunction_nrOfArguments(func); + for (i = 0; i < nrOfArgs; i +=1) { + const dyn_type* type = dynFunction_argumentTypeForIndex(func, i); + enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); + if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { + json_t* val = NULL; - int rc = jsonSerializer_serializeJson(type, args[i], &val); + int rc = jsonSerializer_serializeJson(type, args[i], &val); if (dynType_descriptorType(type) == 't') { - const char *metaArgument = dynType_getMetaInfo(type, "const"); + const char* metaArgument = dynType_getMetaInfo(type, "const"); if (metaArgument != NULL && strncmp("true", metaArgument, 5) == 0) { //const char * as input -> nop } else { - char **str = args[i]; + char** str = args[i]; free(*str); //char * as input -> got ownership -> free it. } } - if (rc == 0) { - json_array_append_new(arguments, val); - } else { + if (rc == 0) { + json_array_append_new(arguments, val); + } else { celix_err_pushf("Failed to serialize args for function '%s'\n", id); - status = ERROR; - break; - } - } else { - //skip handle / output types - } - } - - char *invokeStr = json_dumps(invoke, JSON_COMPACT | JSON_ENCODE_ANY);//Should use JSON_COMPACT, it can reduce the size of the JSON string. - json_decref(invoke); - - if (status == OK) { - *out = invokeStr; - } else { + status = ERROR; + break; + } + } else { + //skip handle / output types + } + } + + char* invokeStr = json_dumps(invoke, JSON_COMPACT | JSON_ENCODE_ANY);//Should use JSON_COMPACT, it can reduce the size of the JSON string. + json_decref(invoke); + + if (status == OK) { + *out = invokeStr; + } else { *out = NULL; free(invokeStr); } - return status; + return status; } -int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[], int *rsErrno) { - int status = OK; +int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* args[], int* rsErrno) { + int status = OK; - json_error_t error; - json_t *replyJson = json_loads(reply, JSON_DECODE_ANY, &error); - if (replyJson == NULL) { - status = ERROR; + json_error_t error; + json_t* replyJson = json_loads(reply, JSON_DECODE_ANY, &error); + if (replyJson == NULL) { + status = ERROR; celix_err_pushf("Error parsing json '%s', got error '%s'", reply, error.text); - } + } - json_t *result = NULL; - json_t *rsError = NULL; + json_t* result = NULL; + json_t* rsError = NULL; bool replyHasError = false; - if (status == OK) { - *rsErrno = 0; - result = json_object_get(replyJson, "r"); - if (result == NULL) { - rsError = json_object_get(replyJson, "e"); - if(rsError != NULL) { - //get the invocation error of remote service function - *rsErrno = (int)json_integer_value(rsError); + if (status == OK) { + *rsErrno = 0; + result = json_object_get(replyJson, "r"); + if (result == NULL) { + rsError = json_object_get(replyJson, "e"); + if(rsError != NULL) { + //get the invocation error of remote service function + *rsErrno = (int)json_integer_value(rsError); replyHasError = true; - } - } - } + } + } + } int nrOfOutputArgs = 0; int nrOfArgs = dynFunction_nrOfArguments(func); @@ -340,55 +318,55 @@ int jsonRpc_handleReply(dyn_function_type *func, const char *reply, void *args[] } } - if (status == OK && !replyHasError) { + if (status == OK && !replyHasError) { int i; - for (i = 0; i < nrOfArgs; i += 1) { - const dyn_type *argType = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - void *tmp = NULL; - void **out = (void **) args[i]; - size_t size = 0; + for (i = 0; i < nrOfArgs; i += 1) { + const dyn_type* argType = dynFunction_argumentTypeForIndex(func, i); + enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); + if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + void* tmp = NULL; + void** out = (void **) args[i]; + size_t size = 0; if (dynType_descriptorType(argType) == 't') { - status = jsonSerializer_deserializeJson(argType, result, &tmp); - if (tmp != NULL) { - size = strnlen(((char *) *(char**) tmp), 1024 * 1024); - memcpy(*out, *(void**) tmp, size); - } - } else { + status = jsonSerializer_deserializeJson(argType, result, &tmp); + if (tmp != NULL) { + size = strnlen(((char *) *(char**) tmp), 1024 * 1024); + memcpy(*out, *(void**) tmp, size); + } + } else { argType = dynType_typedPointer_getTypedType(argType); - status = jsonSerializer_deserializeJson(argType, result, &tmp); - if (tmp != NULL) { - size = dynType_size(argType); - memcpy(*out, tmp, size); - } - } + status = jsonSerializer_deserializeJson(argType, result, &tmp); + if (tmp != NULL) { + size = dynType_size(argType); + memcpy(*out, tmp, size); + } + } - dynType_free(argType, tmp); - } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - const dyn_type *subType = dynType_typedPointer_getTypedType(argType); + dynType_free(argType, tmp); + } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { + const dyn_type* subType = dynType_typedPointer_getTypedType(argType); if (dynType_descriptorType(subType) == 't') { - char ***out = (char ***) args[i]; - char **ptrToString = NULL; + char*** out = (char ***) args[i]; + char** ptrToString = NULL; status = jsonSerializer_deserializeJson(subType, result, (void**)&ptrToString); - char *s CELIX_UNUSED = *ptrToString; //note for debug + char* s CELIX_UNUSED = *ptrToString; //note for debug free(ptrToString); **out = (void*)s; } else { - const dyn_type *subSubType = dynType_typedPointer_getTypedType(subType); - void ***out = (void ***) args[i]; - status = jsonSerializer_deserializeJson(subSubType, result, *out); - } - } else { - //skip - } - } - } + const dyn_type* subSubType = dynType_typedPointer_getTypedType(subType); + void*** out = (void ***) args[i]; + status = jsonSerializer_deserializeJson(subSubType, result, *out); + } + } else { + //skip + } + } + } - json_decref(replyJson); + json_decref(replyJson); - return status; + return status; } From 797e97552262d185433c51377af3f39d2b86967b Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 23 Jan 2024 13:20:52 +0800 Subject: [PATCH 44/62] Add some type checking to dyn_function. --- libs/dfi/gtest/src/dyn_function_tests.cpp | 10 ++++++++++ libs/dfi/src/dyn_function.c | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 529289007..6546fa5a0 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -291,3 +291,13 @@ TEST_F(DynFunctionTests, InvalidDynFuncTest) { EXPECT_STREQ("Error missing ')'", celix_err_popLastError()); } +TEST_F(DynFunctionTests, WrongArgumentMetaTest) { + dyn_function_type *dynFunc = nullptr; + int rc1 = dynFunction_parseWithStr("example(#am=handle;tt)N", nullptr, &dynFunc); + EXPECT_NE(0, rc1); + EXPECT_STREQ("Error 'handle' is only allowed for untyped pointer not 't'", celix_err_popLastError()); + + int rc2 = dynFunction_parseWithStr("example(#am=handle;THandle=P;lHandle;t)N", nullptr, &dynFunc); + EXPECT_EQ(0, rc2); + dynFunction_destroy(dynFunc); +} diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index ee23681db..55229e4da 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -59,10 +59,15 @@ int dynFunction_parse(FILE* descriptor, struct types_head* refTypes, dyn_functio dyn_function_argument_type* arg = NULL; TAILQ_FOREACH(arg, &dynFunc->arguments, entries) { + const dyn_type* real = dynType_realType(arg->type); const char* meta = dynType_getMetaInfo(arg->type, "am"); if (meta == NULL) { arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; } else if (strcmp(meta, "handle") == 0) { + if (dynType_descriptorType(real) != 'P') { + celix_err_pushf("Error 'handle' is only allowed for untyped pointer not '%c'", dynType_descriptorType(real)); + return PARSE_ERROR; + } arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__HANDLE; } else if (strcmp(meta, "pre") == 0) { arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT; From 862493731c1baceb81ed8cd0e457a00e9667987f Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 23 Jan 2024 13:26:35 +0800 Subject: [PATCH 45/62] Update codecov-action. (cherry picked from commit 9b4501d2b14c473023b10bbe68bd98de45a8517e) --- .github/workflows/coverage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 76601a2e0..be94b289a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -67,7 +67,7 @@ jobs: source deactivate_conanrun.sh lcx="lcov --output-file=coverage.info " && for i in `find . -name "*.info.cleaned"`; do lcx+=" --add-tracefile=$i"; done && $lcx - name: Codecov - uses: codecov/codecov-action@e156083f13aff6830c92fc5faa23505779fbf649 + uses: codecov/codecov-action@v3 with: - file: build/coverage.info + files: build/coverage.info name: codecov-celix From e34eb00b6ea2212504662dbc7bbff206bb33931e Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 25 Jan 2024 18:43:05 +0800 Subject: [PATCH 46/62] #723 Add triviality test for dynType so that definitions of dfi argument types can be given. 1. A standard argument can be of any serializable type. 2. `am=pre` argument should be pointer to trivial types. 3. `am=out` argument should be pointer to text or double pointer to serializable types. 4. `am=handle` argument should be untyped pointer. --- libs/dfi/gtest/src/dyn_function_tests.cpp | 18 +++++++ libs/dfi/gtest/src/dyn_type_tests.cpp | 65 +++++++++++++++++++++++ libs/dfi/gtest/src/json_rpc_tests.cpp | 24 --------- libs/dfi/include/dyn_function.h | 6 ++- libs/dfi/include/dyn_type.h | 10 ++++ libs/dfi/src/dyn_function.c | 20 +++++++ libs/dfi/src/dyn_type.c | 27 ++++++++++ libs/dfi/src/json_rpc.c | 18 ++----- 8 files changed, 149 insertions(+), 39 deletions(-) diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 6546fa5a0..09328b8b7 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -300,4 +300,22 @@ TEST_F(DynFunctionTests, WrongArgumentMetaTest) { int rc2 = dynFunction_parseWithStr("example(#am=handle;THandle=P;lHandle;t)N", nullptr, &dynFunc); EXPECT_EQ(0, rc2); dynFunction_destroy(dynFunc); + + int rc3 = dynFunction_parseWithStr("example(#am=handle;P#am=out;t)N", nullptr, &dynFunc); + EXPECT_NE(0, rc3); + EXPECT_STREQ("Error 'out' is only allowed for typed pointer not 't'", celix_err_popLastError()); + + int rc4 = dynFunction_parseWithStr("example(#am=handle;P#am=out;*D)N", nullptr, &dynFunc); + EXPECT_NE(0, rc4); + EXPECT_STREQ("Error 'out' is only allowed for pointer to text or typed pointer not to 'D'", celix_err_popLastError()); + + // #am=pre argument is not allowed for non pointer types + int rc5 = dynFunction_parseWithStr("example(#am=pre;I)N", nullptr, &dynFunc); + EXPECT_NE(0, rc5); + EXPECT_STREQ("Error 'pre' is only allowed for typed pointer not 'I'", celix_err_popLastError()); + + // #am=pre argument is not allowed for pointer to nontrivial types + int rc6 = dynFunction_parseWithStr("example(#am=pre;**D)N", nullptr, &dynFunc); + EXPECT_NE(0, rc6); + EXPECT_STREQ("Error 'pre' is only allowed for pointer to trivial types not non-trivial '*'", celix_err_popLastError()); } diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index 961a24112..701dae86a 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -573,4 +573,69 @@ TEST_F(DynTypeTests, ParseSequenceFailed) { int rc = 0; rc = dynType_parseWithStr("Tval={DD a b};Titem={Jtlval;DDJ a text val c d e};**[Lite;", NULL, NULL, &type); ASSERT_NE(0, rc); +} + +TEST_F(DynTypeTests, TrivialityTesT) { + dyn_type *type = NULL; + int rc = 0; + + // non pointer simple type is trivial + rc = dynType_parseWithStr("I", NULL, NULL, &type); + ASSERT_EQ(0, rc); + ASSERT_TRUE(dynType_isTrivial(type)); + dynType_destroy(type); + + // untyped pointer is non-trivial + rc = dynType_parseWithStr("P", NULL, NULL, &type); + ASSERT_EQ(0, rc); + ASSERT_FALSE(dynType_isTrivial(type)); + dynType_destroy(type); + + // reference to non-pointer simple type is trivial + rc = dynType_parseWithStr("Ttype=I;ltype;", NULL, NULL, &type); + ASSERT_EQ(0, rc); + ASSERT_TRUE(dynType_isTrivial(type)); + dynType_destroy(type); + + // typed pointer is non-trivial + rc = dynType_parseWithStr("*D", NULL, NULL, &type); + ASSERT_EQ(0, rc); + ASSERT_FALSE(dynType_isTrivial(type)); + dynType_destroy(type); + + // sequence type is non-trivial + rc = dynType_parseWithStr("[I", NULL, NULL, &type); + ASSERT_EQ(0, rc); + EXPECT_FALSE(dynType_isTrivial(type)); + dynType_destroy(type); + + // text is non-trivial + rc = dynType_parseWithStr("t", NULL, NULL, &type); + ASSERT_EQ(0, rc); + EXPECT_FALSE(dynType_isTrivial(type)); + dynType_destroy(type); + + // a complex consisting of non-pointer scalars is trivial + rc = dynType_parseWithStr("{II a b}", NULL, NULL, &type); + ASSERT_EQ(0, rc); + EXPECT_TRUE(dynType_isTrivial(type)); + dynType_destroy(type); + + // a complex having a pointer scalar is non-trivial + rc = dynType_parseWithStr("{IP a b}", NULL, NULL, &type); + ASSERT_EQ(0, rc); + EXPECT_FALSE(dynType_isTrivial(type)); + dynType_destroy(type); + + // a complex consisting of non-pointer scalar and trivial complex is trivial + rc = dynType_parseWithStr("{II{II b c}}", NULL, NULL, &type); + ASSERT_EQ(0, rc); + EXPECT_TRUE(dynType_isTrivial(type)); + dynType_destroy(type); + + // a complex consisting of non-pointer scalar and non-trivial complex is non-trivial + rc = dynType_parseWithStr("{II{IP b c}}", NULL, NULL, &type); + ASSERT_EQ(0, rc); + EXPECT_FALSE(dynType_isTrivial(type)); + dynType_destroy(type); } \ 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 c2dc1cc6f..44daee2e7 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -732,26 +732,6 @@ extern "C" { dynInterface_destroy(intf); } - void handleTestPreChar(void) { - dyn_function_type *dynFunc = nullptr; - int rc = dynFunction_parseWithStr("getName(#am=handle;P#am=pre;t)N", nullptr, &dynFunc); - ASSERT_EQ(0, rc); - - const char *reply = "{\"r\":\"this is a test pre string\"}"; - char result[32] = {0}; - void *out = result; - void *args[2]; - args[0] = nullptr; - args[1] = &out; - int rsErrno = 0; - rc = jsonRpc_handleReply(dynFunc, reply, args, &rsErrno); - ASSERT_EQ(0, rc); - ASSERT_EQ(0, rsErrno); - ASSERT_STREQ("this is a test pre string", result); - - dynFunction_destroy(dynFunc); - } - void callTestChar(void) { dyn_interface_type *intf = nullptr; FILE *desc = fopen("descriptors/example4.descriptor", "r"); @@ -905,10 +885,6 @@ TEST_F(JsonRpcTests, handleOutChar) { handleTestOutChar(); } -TEST_F(JsonRpcTests, handlePreChar) { - handleTestPreChar(); -} - TEST_F(JsonRpcTests, handleReplyError) { handleTestReplyError(); } diff --git a/libs/dfi/include/dyn_function.h b/libs/dfi/include/dyn_function.h index 72d992781..35e15d2df 100644 --- a/libs/dfi/include/dyn_function.h +++ b/libs/dfi/include/dyn_function.h @@ -34,8 +34,10 @@ 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 - * am=out #output pointer + * 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 + * + * Without meta info the argument is considered to be a standard argument, which can be of any serializable type. * * text argument (t) can also be annotated to be considered const string. * Normally a text argument will be handled as char*, meaning that the callee is expected to take of ownership. diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 8d6039e91..20c8c6504 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -371,6 +371,16 @@ CELIX_DFI_EXPORT int dynType_text_allocAndInit(const dyn_type* type, void* textL */ CELIX_DFI_EXPORT void dynType_simple_setValue(const dyn_type* type, void* inst, const void* in); +/** + * @brief Test whether the given type is trivial. + * A trivial type does NOT involve any pointer type, and thus can always be safely shallow-copied. + * Any non-pointer simple type is trivial, while typed/untyped pointer is non-trivial. + * A sequence is non-trivial since it contains a pointer to the buffer. + * Text is nontrivial. + * A complex is trivial iff each field is trivial. + */ +CELIX_DFI_EXPORT bool dynType_isTrivial(const dyn_type* type); + #ifdef __cplusplus } #endif diff --git a/libs/dfi/src/dyn_function.c b/libs/dfi/src/dyn_function.c index 55229e4da..3bf27d826 100644 --- a/libs/dfi/src/dyn_function.c +++ b/libs/dfi/src/dyn_function.c @@ -70,8 +70,28 @@ int dynFunction_parse(FILE* descriptor, struct types_head* refTypes, dyn_functio } arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__HANDLE; } else if (strcmp(meta, "pre") == 0) { + if (dynType_type(real) != DYN_TYPE_TYPED_POINTER) { + celix_err_pushf("Error 'pre' is only allowed for typed pointer not '%c'", dynType_descriptorType(real)); + return PARSE_ERROR; + } + const dyn_type* sub = dynType_typedPointer_getTypedType(real); + if (!dynType_isTrivial(sub)) { + celix_err_pushf("Error 'pre' is only allowed for pointer to trivial types not non-trivial '%c'", dynType_descriptorType(sub)); + return PARSE_ERROR; + } arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT; } else if (strcmp(meta, "out") == 0) { + if (dynType_type(real) != DYN_TYPE_TYPED_POINTER) { + celix_err_pushf("Error 'out' is only allowed for typed pointer not '%c'", dynType_descriptorType(real)); + return PARSE_ERROR; + } + const dyn_type* sub = dynType_typedPointer_getTypedType(real); + int subType = dynType_type(sub); + if (subType != DYN_TYPE_TEXT && subType != DYN_TYPE_TYPED_POINTER) { + celix_err_pushf("Error 'out' is only allowed for pointer to text or typed pointer not to '%c'", + dynType_descriptorType(sub)); + return PARSE_ERROR; + } arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__OUTPUT; } else { arg->argumentMeta = DYN_FUNCTION_ARGUMENT_META__STD; diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index b2019f9eb..c7059dd1d 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -1045,3 +1045,30 @@ static void dynType_printSimpleType(const dyn_type *type, FILE *stream) { fprintf(stream, "type '%s': simple type, size is %zu, alignment is %i, descriptor is '%c'\n", type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); } +bool dynType_isTrivial(const dyn_type* type) { + const dyn_type* real = dynType_realType(type); + switch (real->type) { + case DYN_TYPE_COMPLEX : { + struct complex_type_entry* entry = NULL; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + if (!dynType_isTrivial(entry->type)) { + return false; + } + } + return true; + } + case DYN_TYPE_SEQUENCE : + return false; + case DYN_TYPE_TYPED_POINTER : + return false; + case DYN_TYPE_TEXT: + return false; + case DYN_TYPE_SIMPLE: + return type->descriptor != 'P'; +//LCOV_EXCL_START + default : + assert(0 && "Unexpected type."); +//LCOV_EXCL_STOP + } +} + diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 08d1a4aa4..f5f0bee66 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -328,19 +328,11 @@ int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* void** out = (void **) args[i]; size_t size = 0; - if (dynType_descriptorType(argType) == 't') { - status = jsonSerializer_deserializeJson(argType, result, &tmp); - if (tmp != NULL) { - size = strnlen(((char *) *(char**) tmp), 1024 * 1024); - memcpy(*out, *(void**) tmp, size); - } - } else { - argType = dynType_typedPointer_getTypedType(argType); - status = jsonSerializer_deserializeJson(argType, result, &tmp); - if (tmp != NULL) { - size = dynType_size(argType); - memcpy(*out, tmp, size); - } + argType = dynType_typedPointer_getTypedType(argType); + status = jsonSerializer_deserializeJson(argType, result, &tmp); + if (tmp != NULL) { + size = dynType_size(argType); + memcpy(*out, tmp, size); } dynType_free(argType, tmp); From 823c131b2fd9805a1976e5ca4e7fbb913399cd19 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 25 Jan 2024 18:46:40 +0800 Subject: [PATCH 47/62] Fix compilation error. --- libs/dfi/src/dyn_type.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index c7059dd1d..7d956cf50 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -1068,6 +1068,7 @@ bool dynType_isTrivial(const dyn_type* type) { //LCOV_EXCL_START default : assert(0 && "Unexpected type."); + return false; //LCOV_EXCL_STOP } } From aebe97502129ac350afaf0f37d53578c26f833c2 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 26 Jan 2024 11:15:43 +0800 Subject: [PATCH 48/62] Merge triviality test for dynType into parsing. --- libs/dfi/src/dyn_type.c | 35 ++++++++++------------------------ libs/dfi/src/dyn_type_common.h | 2 ++ 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 7d956cf50..6a15179a2 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -226,6 +226,7 @@ static int dynType_parseMetaInfo(FILE* stream, dyn_type* type) { static int dynType_parseText(FILE* stream, dyn_type* type) { type->type = DYN_TYPE_TEXT; type->descriptor = 't'; + type->trivial = false; type->ffiType = &ffi_type_pointer; return OK; } @@ -233,6 +234,7 @@ static int dynType_parseText(FILE* stream, dyn_type* type) { static int dynType_parseEnum(FILE* stream, dyn_type* type) { type->ffiType = &ffi_type_sint32; type->descriptor = 'E'; + type->trivial = true; type->type = DYN_TYPE_SIMPLE; return OK; } @@ -242,6 +244,7 @@ static int dynType_parseComplex(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_COMPLEX; type->descriptor = '{'; + type->trivial = true; type->ffiType = &type->complex.structType; TAILQ_INIT(&type->complex.entriesHead); @@ -260,6 +263,9 @@ static int dynType_parseComplex(FILE* stream, dyn_type* type) { return MEM_ERROR; } entry->type = celix_steal_ptr(subType); + if (!dynType_isTrivial(entry->type)) { + type->trivial = false; + } TAILQ_INSERT_TAIL(&type->complex.entriesHead, entry, entries); nbEntries += 1; c = fgetc(stream); @@ -339,6 +345,7 @@ static int dynType_parseReference(FILE* stream, dyn_type* type) { int status; type->type = DYN_TYPE_TYPED_POINTER; type->descriptor = '*'; + type->trivial = false; type->ffiType = &ffi_type_pointer; type->typedPointer.typedType = NULL; @@ -400,6 +407,7 @@ static int dynType_parseSimple(int c, dyn_type* type) { } type->type = DYN_TYPE_SIMPLE; type->descriptor = c; + type->trivial = c != 'P'; type->ffiType = ffiType; return OK; } @@ -408,6 +416,7 @@ static int dynType_parseTypedPointer(FILE* stream, dyn_type* type) { int status = OK; type->type = DYN_TYPE_TYPED_POINTER; type->descriptor = '*'; + type->trivial = false; type->ffiType = &ffi_type_pointer; status = dynType_parseWithStreamOfName(stream, NULL, type, NULL, &type->typedPointer.typedType, dynType_parseAny); @@ -1046,30 +1055,6 @@ static void dynType_printSimpleType(const dyn_type *type, FILE *stream) { } bool dynType_isTrivial(const dyn_type* type) { - const dyn_type* real = dynType_realType(type); - switch (real->type) { - case DYN_TYPE_COMPLEX : { - struct complex_type_entry* entry = NULL; - TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { - if (!dynType_isTrivial(entry->type)) { - return false; - } - } - return true; - } - case DYN_TYPE_SEQUENCE : - return false; - case DYN_TYPE_TYPED_POINTER : - return false; - case DYN_TYPE_TEXT: - return false; - case DYN_TYPE_SIMPLE: - return type->descriptor != 'P'; -//LCOV_EXCL_START - default : - assert(0 && "Unexpected type."); - return false; -//LCOV_EXCL_STOP - } + return dynType_realType(type)->trivial; } diff --git a/libs/dfi/src/dyn_type_common.h b/libs/dfi/src/dyn_type_common.h index 314cb2dd1..aa5cc2aa4 100644 --- a/libs/dfi/src/dyn_type_common.h +++ b/libs/dfi/src/dyn_type_common.h @@ -24,6 +24,7 @@ #include "dyn_type.h" #include +#include #ifdef __cplusplus @@ -33,6 +34,7 @@ extern "C" { struct _dyn_type { char* name; char descriptor; + bool trivial; int type; ffi_type* ffiType; dyn_type* parent; From 28db6c37fd035b399f4f277f44c7c681441630f9 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 26 Jan 2024 15:29:13 +0800 Subject: [PATCH 49/62] Perform more strict checking on interface method. 1. The first method argument must be of handle type. 2. Only one output argument (either am=pre or am=out) is allowed. 3. Output argument (if any) must be the last argument. --- .../invalids/invalidMetaType.descriptor | 2 +- .../invalids/methodMissingHandle.descriptor | 13 +++ .../methodWithoutArguments.descriptor | 13 +++ .../invalids/multiOutArgs.descriptor | 1 - .../invalids/multiPreOutArgs.descriptor | 8 ++ .../invalids/outArgAtWrongPosition.descriptor | 8 ++ libs/dfi/gtest/src/dyn_interface_tests.cpp | 69 ++++++++++++++ libs/dfi/gtest/src/json_rpc_tests.cpp | 95 ------------------- libs/dfi/src/dyn_interface.c | 28 +++++- libs/dfi/src/json_rpc.c | 7 -- 10 files changed, 139 insertions(+), 105 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/invalids/methodMissingHandle.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/methodWithoutArguments.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/multiPreOutArgs.descriptor create mode 100644 libs/dfi/gtest/descriptors/invalids/outArgAtWrongPosition.descriptor diff --git a/libs/dfi/gtest/descriptors/invalids/invalidMetaType.descriptor b/libs/dfi/gtest/descriptors/invalids/invalidMetaType.descriptor index 2d17c4799..365c6dded 100644 --- a/libs/dfi/gtest/descriptors/invalids/invalidMetaType.descriptor +++ b/libs/dfi/gtest/descriptors/invalids/invalidMetaType.descriptor @@ -5,4 +5,4 @@ version=1.0.0 :annotations classname=org.example.Calculator :methods -add(DD)D=add(#am=invalid;PDD#am=pre;*D)N +add(DD)D=add(#am=handle;P#am=invalid;PDD#am=pre;*D)N diff --git a/libs/dfi/gtest/descriptors/invalids/methodMissingHandle.descriptor b/libs/dfi/gtest/descriptors/invalids/methodMissingHandle.descriptor new file mode 100644 index 000000000..50ac049d7 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/methodMissingHandle.descriptor @@ -0,0 +1,13 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(DD#am=pre;*D)N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/methodWithoutArguments.descriptor b/libs/dfi/gtest/descriptors/invalids/methodWithoutArguments.descriptor new file mode 100644 index 000000000..a83b54b51 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/methodWithoutArguments.descriptor @@ -0,0 +1,13 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add()N +sub(DD)D=sub(#am=handle;PDD*#am=pre;D)N +sqrt(D)D=sqrt(#am=handle;PD*#am=pre;D)N +stats([D)LStatsResult;=stats(#am=handle;P[D#am=out;*LStatsResult;)N diff --git a/libs/dfi/gtest/descriptors/invalids/multiOutArgs.descriptor b/libs/dfi/gtest/descriptors/invalids/multiOutArgs.descriptor index 4a7831ac3..be5d618ff 100644 --- a/libs/dfi/gtest/descriptors/invalids/multiOutArgs.descriptor +++ b/libs/dfi/gtest/descriptors/invalids/multiOutArgs.descriptor @@ -5,5 +5,4 @@ version=1.0.0 :annotations :types :methods -multiPreOut(V)Di=multiPreOut(#am=handle;P#am=pre;*D#am=pre;*i)N multiOut(V)Di=multiOut(#am=handle;P#am=out;**D#am=out;**i)N diff --git a/libs/dfi/gtest/descriptors/invalids/multiPreOutArgs.descriptor b/libs/dfi/gtest/descriptors/invalids/multiPreOutArgs.descriptor new file mode 100644 index 000000000..a34940988 --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/multiPreOutArgs.descriptor @@ -0,0 +1,8 @@ +:header +type=interface +name=example5 +version=1.0.0 +:annotations +:types +:methods +multiPreOut(V)Di=multiPreOut(#am=handle;P#am=pre;*D#am=pre;*i)N diff --git a/libs/dfi/gtest/descriptors/invalids/outArgAtWrongPosition.descriptor b/libs/dfi/gtest/descriptors/invalids/outArgAtWrongPosition.descriptor new file mode 100644 index 000000000..19205067c --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/outArgAtWrongPosition.descriptor @@ -0,0 +1,8 @@ +:header +type=interface +name=example5 +version=1.0.0 +:annotations +:types +:methods +outWrongPos(V)Di=outWrongPos(#am=handle;P#am=out;**i**D)N diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index efd08dbfe..88263d3f4 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -31,6 +31,7 @@ extern "C" { #include "dyn_common.h" #include "dyn_interface.h" +#include "celix_err.h" #include "celix_version.h" static void checkInterfaceVersion(dyn_interface_type* dynIntf, const char* v) { @@ -100,6 +101,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of a space at the end of the name fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid header */ desc = fopen("descriptors/invalids/invalidHeader.descriptor", "r"); @@ -107,6 +109,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing name value fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Header without Version */ desc = fopen("descriptors/invalids/noVersion.descriptor", "r"); @@ -114,6 +117,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing version field in header section fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Header without Type */ desc = fopen("descriptors/invalids/noType.descriptor", "r"); @@ -121,6 +125,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing type field in header section fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Header without Name */ desc = fopen("descriptors/invalids/noName.descriptor", "r"); @@ -128,6 +133,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing name field in header section fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid Annotations Section */ desc = fopen("descriptors/invalids/invalidInterfaceAnnotations.descriptor", "r"); @@ -135,6 +141,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing name field in annotations section fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid section */ desc = fopen("descriptors/invalids/invalidSection.descriptor", "r"); @@ -142,13 +149,62 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of unknown section type fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid return type */ desc = fopen("descriptors/invalids/invalidMethodReturnType.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of invalid return type (D instead of N) + EXPECT_STREQ("Parse Error. Got return type 'D' rather than 'N' (native int) for method add(DD)D (0)", celix_err_popLastError()); fclose(desc); desc=NULL; + celix_err_resetErrors(); + + /* Method without arguments */ + desc = fopen("descriptors/invalids/methodWithoutArguments.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + EXPECT_STREQ("Parse Error. The first argument must be handle for method add(DD)D (0)", celix_err_popLastError()); + fclose(desc); desc=NULL; + celix_err_resetErrors(); + + /* Method missing handle */ + desc = fopen("descriptors/invalids/methodMissingHandle.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + EXPECT_STREQ("Parse Error. The first argument must be handle for method add(DD)D (0)", celix_err_popLastError()); + fclose(desc); desc=NULL; + celix_err_resetErrors(); + + /* Method with multiple PreOut arguments */ + desc = fopen("descriptors/invalids/multiPreOutArgs.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + EXPECT_STREQ("Parse Error. Only one output argument is allowed for method multiPreOut(V)Di (0)", celix_err_popLastError()); + fclose(desc); desc=NULL; + celix_err_resetErrors(); + + /* Method with multiple Out arguments */ + desc = fopen("descriptors/invalids/multiOutArgs.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + EXPECT_STREQ("Parse Error. Only one output argument is allowed for method multiOut(V)Di (0)", celix_err_popLastError()); + fclose(desc); desc=NULL; + celix_err_resetErrors(); + + /* Method with an Out argument at wrong position */ + desc = fopen("descriptors/invalids/outArgAtWrongPosition.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + EXPECT_STREQ("Parse Error. Output argument is only allowed as the last argument for method outWrongPos(V)Di (0)", + celix_err_popLastError()); + fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid method section */ desc = fopen("descriptors/invalids/invalidMethod.descriptor", "r"); @@ -156,6 +212,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of space at the end of the method fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid method section missing method id */ desc = fopen("descriptors/invalids/invalidMethodMissingId.descriptor", "r"); @@ -163,6 +220,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid method section missing equality */ desc = fopen("descriptors/invalids/invalidMethodMissingEquality.descriptor", "r"); @@ -170,6 +228,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid method section missing function name */ desc = fopen("descriptors/invalids/invalidMethodMissingFunctionName.descriptor", "r"); @@ -177,6 +236,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid type */ desc = fopen("descriptors/invalids/invalidType.descriptor", "r"); @@ -184,6 +244,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of space at the end of the type fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid types section missing type name */ desc = fopen("descriptors/invalids/noTypeName.descriptor", "r"); @@ -191,6 +252,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing type name fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid types section missing equality */ desc = fopen("descriptors/invalids/invalidTypeMissingEquality.descriptor", "r"); @@ -198,6 +260,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); //Test fails because of missing equality fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid types section with unrecognized simple type */ desc = fopen("descriptors/invalids/invalidTypeUnrecognizedSimpleType.descriptor", "r"); @@ -205,6 +268,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_NE(0, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); /* Invalid metatype in method description */ desc = fopen("descriptors/invalids/invalidMetaType.descriptor", "r"); @@ -213,6 +277,7 @@ extern "C" { dynInterface_destroy(dynIntf); ASSERT_EQ(0, status); //Invalid meta type doesn't generate errors, just warnings fclose(desc); desc=NULL; dynIntf=NULL; + celix_err_resetErrors(); /* Invalid version section */ desc = fopen("descriptors/invalids/invalidVersion.descriptor", "r"); @@ -220,6 +285,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); /* garbage descriptor */ desc = fopen("descriptors/invalids/garbage.descriptor", "r"); @@ -227,6 +293,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); /* invalid extra section */ desc = fopen("descriptors/invalids/invalidExtraSection.descriptor", "r"); @@ -234,6 +301,7 @@ extern "C" { status = dynInterface_parse(desc, &dynIntf); ASSERT_EQ(1, status); fclose(desc); desc=NULL; + celix_err_resetErrors(); } } @@ -242,6 +310,7 @@ class DynInterfaceTests : public ::testing::Test { DynInterfaceTests() { } ~DynInterfaceTests() override { + celix_err_resetErrors(); } }; diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 44daee2e7..0b8d2fee3 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -584,93 +584,6 @@ extern "C" { dynInterface_destroy(intf); } - //Current only support one out argument - void handleTestMultiPreOut(void) { - dyn_interface_type *intf = nullptr; - FILE *desc = fopen("descriptors/invalids/multiOutArgs.descriptor", "r"); - ASSERT_TRUE(desc != nullptr); - int rc = dynInterface_parse(desc, &intf); - 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), "multiPreOut") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); - - const char *reply = R"({"r":2.0})"; - - void *args[3]; - args[0] = nullptr; - args[1] = nullptr; - args[2] = nullptr; - - double result1 = 0; - void *out = &result1; - args[1] = &out; - int result2 = 0; - void *out2 = &result2; - args[2] = &out2; - - int rsErrno = 0; - rc = jsonRpc_handleReply(func, reply, args, &rsErrno); - ASSERT_NE(0, rc); - celix_err_printErrors(stderr, nullptr, nullptr); - - dynInterface_destroy(intf); - } - - //Current only support one out argument - void handleTestMultiOut(void) { - dyn_interface_type *intf = nullptr; - FILE *desc = fopen("descriptors/invalids/multiOutArgs.descriptor", "r"); - ASSERT_TRUE(desc != nullptr); - int rc = dynInterface_parse(desc, &intf); - 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), "multiOut") == 0) { - func = entry->dynFunc; - break; - } - } - ASSERT_TRUE(func != nullptr); - - const char *reply = R"({"r":2.0})"; - - void *args[3]; - args[0] = nullptr; - args[1] = nullptr; - args[2] = nullptr; - - double *result1 = nullptr; - void *out = &result1; - args[1] = &out; - int *result2 = nullptr; - void *out2 = &result2; - args[2] = &out2; - - int rsErrno = 0; - rc = jsonRpc_handleReply(func, reply, args, &rsErrno); - ASSERT_NE(0, rc); - celix_err_printErrors(stderr, nullptr, nullptr); - - dynInterface_destroy(intf); - } - - - - void callTestOutChar(void) { dyn_interface_type *intf = nullptr; FILE *desc = fopen("descriptors/example4.descriptor", "r"); @@ -897,14 +810,6 @@ TEST_F(JsonRpcTests, handleTestAction) { handleTestAction(); } -TEST_F(JsonRpcTests, handleTestMultiPreOut) { - handleTestMultiPreOut(); -} - -TEST_F(JsonRpcTests, handleTestMultiOut) { - handleTestMultiOut(); -} - TEST_F(JsonRpcTests, callTestChar) { callTestChar(); } diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 1e95e2ae3..4908d5382 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -69,9 +69,35 @@ static int dynInterface_checkInterface(dyn_interface_type* intf) { const dyn_type* type = dynFunction_returnType(mEntry->dynFunc); int descriptor = dynType_descriptorType(type); if (descriptor != 'N') { - celix_err_pushf("Parse Error. Only method with a return type 'N' (native int) are supported. Got return type '%c'\n", descriptor); + celix_err_pushf("Parse Error. Got return type '%c' rather than 'N' (native int) for method %s (%d)", + descriptor, mEntry->id, mEntry->index); return ERROR; } + const struct dyn_function_arguments_head* args = dynFunction_arguments(mEntry->dynFunc); + dyn_function_argument_type* argEntry = TAILQ_FIRST(args); + if (argEntry == NULL || argEntry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__HANDLE) { + celix_err_pushf("Parse Error. The first argument must be handle for method %s (%d)", mEntry->id, mEntry->index); + return ERROR; + } + size_t nbOfOutputArgs = 0; + for (argEntry = TAILQ_NEXT(argEntry, entries); argEntry != NULL; argEntry = TAILQ_NEXT(argEntry, entries)) { + if (argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT || + argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + nbOfOutputArgs += 1; + } + } + if (nbOfOutputArgs > 1) { + celix_err_pushf("Parse Error. Only one output argument is allowed for method %s (%d)", mEntry->id, mEntry->index); + return ERROR; + } else if (nbOfOutputArgs > 0) { + argEntry = TAILQ_LAST(args, dyn_function_arguments_head); + if (argEntry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__OUTPUT && + argEntry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + celix_err_pushf("Parse Error. Output argument is only allowed as the last argument for method %s (%d)", + mEntry->id, mEntry->index); + return ERROR; + } + } } return OK; diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index f5f0bee66..6fafa7904 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -299,17 +299,10 @@ int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* } } - int nrOfOutputArgs = 0; int nrOfArgs = dynFunction_nrOfArguments(func); for (int j = 0; j < nrOfArgs; ++j) { enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, j); if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT || meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - nrOfOutputArgs += 1; - if (nrOfOutputArgs > 1) { - status = ERROR; - celix_err_pushf("Only one output argument is supported"); - break; - } if (result == NULL && !replyHasError) { status = ERROR; celix_err_pushf("Expected result in reply. got '%s'", reply); From 56abd7d5b9c8a6eac40c5bad131732c4510a23a5 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 26 Jan 2024 18:09:05 +0800 Subject: [PATCH 50/62] Optimize jsonRpc_handleReply. 1. Apply early return to make error handling dead simple. 2. Remove unnecessary argument list iterations. 3. Protect against user-provided nullptr. 4. Deal with invalid text output. --- libs/dfi/gtest/src/json_rpc_tests.cpp | 93 ++++++++++++++++++++- libs/dfi/src/json_rpc.c | 115 ++++++++++++-------------- 2 files changed, 143 insertions(+), 65 deletions(-) diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index 0b8d2fee3..f74fffdfc 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -150,7 +150,7 @@ extern "C" { rc = jsonRpc_handleReply(dynFunc, reply, args, &rsErrno); ASSERT_EQ(0, rc); ASSERT_EQ(0, rsErrno); - //ASSERT_EQ(2.2, result); + EXPECT_DOUBLE_EQ(2.2, result); dynFunction_destroy(dynFunc); } @@ -720,6 +720,24 @@ TEST_F(JsonRpcTests, handleTestPre) { handleTestPre(); } +TEST_F(JsonRpcTests, handleTestNullPre) { + dyn_function_type *dynFunc = nullptr; + int rc = dynFunction_parseWithStr("add(#am=handle;PDD#am=pre;*D)N", nullptr, &dynFunc); + ASSERT_EQ(0, rc); + + const char *reply = "{\"r\":2.2}"; + double *out = NULL; + void *args[4]; + args[3] = &out; + int rsErrno = 0; + rc = jsonRpc_handleReply(dynFunc, reply, args, &rsErrno); + ASSERT_EQ(0, rc); + ASSERT_EQ(0, rsErrno); + //ASSERT_EQ(2.2, result); + + dynFunction_destroy(dynFunc); +} + TEST_F(JsonRpcTests, handleTestInvalidReply) { handleTestInvalidReply(); } @@ -728,6 +746,42 @@ TEST_F(JsonRpcTests, handleTestOut) { handleTestOut(); } +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); + + 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); + + const char *reply = R"({"r":{"input":[1.0,2.0],"max":2.0,"average":1.5,"min":1.0}})"; + + void *args[3]; + args[0] = nullptr; + args[1] = nullptr; + args[2] = nullptr; + + void *out = nullptr; + args[2] = &out; + + int rsErrno = 0; + rc = jsonRpc_handleReply(func, reply, args, &rsErrno); + ASSERT_EQ(0, rc); + ASSERT_EQ(0, rsErrno); + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, callPreReference) { dyn_interface_type *intf = nullptr; FILE *desc = fopen("descriptors/example7.descriptor", "r"); @@ -798,6 +852,43 @@ TEST_F(JsonRpcTests, handleOutChar) { handleTestOutChar(); } +TEST_F(JsonRpcTests, handleInvalidOutChar) { + + 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); + + 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); + + const char *reply = R"({"r": 12345})"; + char *result = nullptr; + void *out = &result; + + void *args[2]; + args[0] = nullptr; + args[1] = &out; + + if (func != nullptr) { // Check needed just to satisfy Coverity + int rsErrno = 0; + int status = jsonRpc_handleReply(func, reply, args, &rsErrno); + EXPECT_NE(0, status); + } + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, handleReplyError) { handleTestReplyError(); } diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 6fafa7904..3a04f7a5c 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -277,81 +277,68 @@ int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* int status = OK; json_error_t error; - json_t* replyJson = json_loads(reply, JSON_DECODE_ANY, &error); + json_auto_t* replyJson = json_loads(reply, JSON_DECODE_ANY, &error); if (replyJson == NULL) { - status = ERROR; celix_err_pushf("Error parsing json '%s', got error '%s'", reply, error.text); + return ERROR; } json_t* result = NULL; json_t* rsError = NULL; - bool replyHasError = false; - if (status == OK) { - *rsErrno = 0; - result = json_object_get(replyJson, "r"); - if (result == NULL) { - rsError = json_object_get(replyJson, "e"); - if(rsError != NULL) { - //get the invocation error of remote service function - *rsErrno = (int)json_integer_value(rsError); - replyHasError = true; - } - } + *rsErrno = 0; + + const struct dyn_function_arguments_head* arguments = dynFunction_arguments(func); + dyn_function_argument_type* last = TAILQ_LAST(arguments, dyn_function_arguments_head); + const dyn_type* argType = last->type; + enum dyn_function_argument_meta meta = last->argumentMeta; + rsError = json_object_get(replyJson, "e"); + if(rsError != NULL) { + //get the invocation error of remote service function + *rsErrno = (int) json_integer_value(rsError); + return OK; } - - int nrOfArgs = dynFunction_nrOfArguments(func); - for (int j = 0; j < nrOfArgs; ++j) { - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, j); - if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT || meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - if (result == NULL && !replyHasError) { - status = ERROR; - celix_err_pushf("Expected result in reply. got '%s'", reply); - break; - } - } + if (meta != DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT && meta != DYN_FUNCTION_ARGUMENT_META__OUTPUT) { + return OK; } - - if (status == OK && !replyHasError) { - int i; - for (i = 0; i < nrOfArgs; i += 1) { - const dyn_type* argType = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - void* tmp = NULL; - void** out = (void **) args[i]; - size_t size = 0; - - argType = dynType_typedPointer_getTypedType(argType); - status = jsonSerializer_deserializeJson(argType, result, &tmp); - if (tmp != NULL) { - size = dynType_size(argType); - memcpy(*out, tmp, size); - } - - dynType_free(argType, tmp); - } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - const dyn_type* subType = dynType_typedPointer_getTypedType(argType); - - if (dynType_descriptorType(subType) == 't') { - char*** out = (char ***) args[i]; - char** ptrToString = NULL; - status = jsonSerializer_deserializeJson(subType, result, (void**)&ptrToString); - char* s CELIX_UNUSED = *ptrToString; //note for debug - free(ptrToString); - **out = (void*)s; - } else { - const dyn_type* subSubType = dynType_typedPointer_getTypedType(subType); - void*** out = (void ***) args[i]; - status = jsonSerializer_deserializeJson(subSubType, result, *out); - } - } else { - //skip + result = json_object_get(replyJson, "r"); + if (result == NULL) { + celix_err_pushf("Expected result in reply. got '%s'", reply); + return ERROR; + } + void** lastArg = (void **) args[last->index]; + if (*lastArg == NULL) { + // caller provides nullptr, no need to deserialize + return OK; + } + if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + void* tmp = NULL; + void** out = lastArg; + size_t size = 0; + + argType = dynType_typedPointer_getTypedType(argType); + status = jsonSerializer_deserializeJson(argType, result, &tmp); + if (tmp != NULL) { + size = dynType_size(argType); + memcpy(*out, tmp, size); + dynType_free(argType, tmp); + } + } else { + const dyn_type* subType = dynType_typedPointer_getTypedType(argType); + + if (dynType_descriptorType(subType) == 't') { + char*** out = (char ***) lastArg; + char** ptrToString = NULL; + status = jsonSerializer_deserializeJson(subType, result, (void**)&ptrToString); + if (ptrToString != NULL) { + **out = (void*)*ptrToString; + free(ptrToString); } + } else { + const dyn_type* subSubType = dynType_typedPointer_getTypedType(subType); + void*** out = (void ***) lastArg; + status = jsonSerializer_deserializeJson(subSubType, result, *out); } } - - json_decref(replyJson); - return status; } From d833f70b1cb0ac193469e11250f59a863e9567eb Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 26 Jan 2024 21:39:59 +0800 Subject: [PATCH 51/62] Fix issue caused by reference type in jsonRpc_handleReply and optimize jsonRpc_prepareInvokeRequest. 1. Remove unnecessary argument list iteration by removing usages of dynFunction_argumentTypeForIndex and dynFunction_argumentMetaForIndex. 2. Add more tests using error injection. --- libs/dfi/gtest/CMakeLists.txt | 1 + libs/dfi/gtest/src/json_rpc_ei_tests.cc | 77 +++++++++++++++++++ libs/dfi/src/json_rpc.c | 77 +++++++++---------- libs/error_injector/jansson/CMakeLists.txt | 1 + .../jansson/include/jansson_ei.h | 1 + libs/error_injector/jansson/src/jansson_ei.cc | 6 ++ 6 files changed, 122 insertions(+), 41 deletions(-) create mode 100644 libs/dfi/gtest/src/json_rpc_ei_tests.cc diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 030c2a2c5..f55b751a3 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -46,6 +46,7 @@ if (EI_TESTS) src/dyn_message_ei_tests.cc src/dyn_function_ei_tests.cc src/json_serializer_ei_tests.cc + src/json_rpc_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut diff --git a/libs/dfi/gtest/src/json_rpc_ei_tests.cc b/libs/dfi/gtest/src/json_rpc_ei_tests.cc new file mode 100644 index 000000000..46b90b09d --- /dev/null +++ b/libs/dfi/gtest/src/json_rpc_ei_tests.cc @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "json_rpc.h" +#include "dyn_function.h" +#include "celix_err.h" +#include "jansson_ei.h" + +#include + +class JsonRpcErrorInjectionTestSuite : public ::testing::Test { +public: + JsonRpcErrorInjectionTestSuite() { + + } + ~JsonRpcErrorInjectionTestSuite() override { + celix_ei_expect_json_dumps(nullptr, 0, nullptr); + celix_ei_expect_json_array_append_new(nullptr, 0, -1); + celix_ei_expect_json_string(nullptr, 0, nullptr); + celix_ei_expect_json_array(nullptr, 0, nullptr); + celix_err_resetErrors(); + } +}; + +TEST_F(JsonRpcErrorInjectionTestSuite, prepareErrorTest) { + dyn_function_type *dynFunc = nullptr; + int rc = dynFunction_parseWithStr("add(#am=handle;PDD#am=pre;*D)N", nullptr, &dynFunc); + ASSERT_EQ(0, rc); + + char *result = nullptr; + + void *handle = nullptr; + double arg1 = 1.0; + double arg2 = 2.0; + + void *args[4]; + args[0] = &handle; + args[1] = &arg1; + args[2] = &arg2; + + celix_ei_expect_json_array((void*)jsonRpc_prepareInvokeRequest, 0, nullptr); + rc = jsonRpc_prepareInvokeRequest(dynFunc, "add", args, &result); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error adding arguments array for 'add'", celix_err_popLastError()); + + celix_ei_expect_json_string((void*)jsonRpc_prepareInvokeRequest, 0, nullptr); + rc = jsonRpc_prepareInvokeRequest(dynFunc, "add", args, &result); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error setting method name 'add'", celix_err_popLastError()); + + celix_ei_expect_json_array_append_new((void*)jsonRpc_prepareInvokeRequest, 0, -1); + rc = jsonRpc_prepareInvokeRequest(dynFunc, "add", args, &result); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error adding argument (1) for 'add'", celix_err_popLastError()); + + celix_ei_expect_json_dumps((void*)jsonRpc_prepareInvokeRequest, 0, nullptr); + rc = jsonRpc_prepareInvokeRequest(dynFunc, "add", args, &result); + ASSERT_NE(0, rc); + + dynFunction_destroy(dynFunc); +} \ No newline at end of file diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 3a04f7a5c..b02c2dd33 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -219,58 +219,53 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ } int jsonRpc_prepareInvokeRequest(const dyn_function_type* func, const char* id, void* args[], char** out) { - int status = OK; - - - json_t* invoke = json_object(); - json_object_set_new_nocheck(invoke, "m", json_string(id)); + json_auto_t* invoke = json_object(); + // each method must have a non-null id + if (json_object_set_new_nocheck(invoke, "m", json_string(id)) != 0) { + celix_err_pushf("Error setting method name '%s'", id); + return ERROR; + } - json_t* arguments = json_array(); - json_object_set_new_nocheck(invoke, "a", arguments); + json_auto_t* arguments = json_array(); + if (json_object_set_nocheck(invoke, "a", arguments) != 0) { + celix_err_pushf("Error adding arguments array for '%s'", id); + return ERROR; + } - int i; - int nrOfArgs = dynFunction_nrOfArguments(func); - for (i = 0; i < nrOfArgs; i +=1) { - const dyn_type* type = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); + const struct dyn_function_arguments_head* dynArgs = dynFunction_arguments(func); + dyn_function_argument_type* entry = NULL; + TAILQ_FOREACH(entry, dynArgs, entries) { + const dyn_type* type = dynType_realType(entry->type); + enum dyn_function_argument_meta meta = entry->argumentMeta; if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { json_t* val = NULL; - int rc = jsonSerializer_serializeJson(type, args[i], &val); + int rc = jsonSerializer_serializeJson(type, args[entry->index], &val); + if (rc != 0) { + celix_err_pushf("Failed to serialize args for function '%s'\n", id); + return ERROR; + } if (dynType_descriptorType(type) == 't') { const char* metaArgument = dynType_getMetaInfo(type, "const"); - if (metaArgument != NULL && strncmp("true", metaArgument, 5) == 0) { + if (metaArgument != NULL && strcmp("true", metaArgument) == 0) { //const char * as input -> nop } else { - char** str = args[i]; + char** str = args[entry->index]; free(*str); //char * as input -> got ownership -> free it. } } - - if (rc == 0) { - json_array_append_new(arguments, val); - } else { - celix_err_pushf("Failed to serialize args for function '%s'\n", id); - status = ERROR; - break; + if (json_array_append_new(arguments, val) != 0) { + celix_err_pushf("Error adding argument (%d) for '%s'", entry->index, id); + return ERROR; } - } else { - //skip handle / output types } } - char* invokeStr = json_dumps(invoke, JSON_COMPACT | JSON_ENCODE_ANY);//Should use JSON_COMPACT, it can reduce the size of the JSON string. - json_decref(invoke); - - if (status == OK) { - *out = invokeStr; - } else { - *out = NULL; - free(invokeStr); - } - - return status; + //use JSON_COMPACT to reduce the size of the JSON string. + char* invokeStr = json_dumps(invoke, JSON_COMPACT | JSON_ENCODE_ANY); + *out = invokeStr; + return *out != NULL ? OK : ERROR; } int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* args[], int* rsErrno) { @@ -289,10 +284,10 @@ int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* const struct dyn_function_arguments_head* arguments = dynFunction_arguments(func); dyn_function_argument_type* last = TAILQ_LAST(arguments, dyn_function_arguments_head); - const dyn_type* argType = last->type; + const dyn_type* argType = dynType_realType(last->type); enum dyn_function_argument_meta meta = last->argumentMeta; rsError = json_object_get(replyJson, "e"); - if(rsError != NULL) { + if (rsError != NULL) { //get the invocation error of remote service function *rsErrno = (int) json_integer_value(rsError); return OK; @@ -315,12 +310,12 @@ int jsonRpc_handleReply(const dyn_function_type* func, const char* reply, void* void** out = lastArg; size_t size = 0; - argType = dynType_typedPointer_getTypedType(argType); - status = jsonSerializer_deserializeJson(argType, result, &tmp); + const dyn_type* subType = dynType_typedPointer_getTypedType(argType); + status = jsonSerializer_deserializeJson(subType, result, &tmp); if (tmp != NULL) { - size = dynType_size(argType); + size = dynType_size(subType); memcpy(*out, tmp, size); - dynType_free(argType, tmp); + dynType_free(subType, tmp); } } else { const dyn_type* subType = dynType_typedPointer_getTypedType(argType); diff --git a/libs/error_injector/jansson/CMakeLists.txt b/libs/error_injector/jansson/CMakeLists.txt index 9365037ac..d325c1a2f 100644 --- a/libs/error_injector/jansson/CMakeLists.txt +++ b/libs/error_injector/jansson/CMakeLists.txt @@ -33,5 +33,6 @@ target_link_options(jansson_ei INTERFACE LINKER:--wrap,json_array LINKER:--wrap,json_array_append_new LINKER:--wrap,json_integer + LINKER:--wrap,json_string ) add_library(Celix::jansson_ei ALIAS jansson_ei) diff --git a/libs/error_injector/jansson/include/jansson_ei.h b/libs/error_injector/jansson/include/jansson_ei.h index 504638529..dd91168ea 100644 --- a/libs/error_injector/jansson/include/jansson_ei.h +++ b/libs/error_injector/jansson/include/jansson_ei.h @@ -32,6 +32,7 @@ CELIX_EI_DECLARE(json_object_set_new, int); CELIX_EI_DECLARE(json_array, json_t*); CELIX_EI_DECLARE(json_array_append_new, int); CELIX_EI_DECLARE(json_integer, json_t*); +CELIX_EI_DECLARE(json_string, json_t*); #ifdef __cplusplus } diff --git a/libs/error_injector/jansson/src/jansson_ei.cc b/libs/error_injector/jansson/src/jansson_ei.cc index cca486d7e..1cf4756b3 100644 --- a/libs/error_injector/jansson/src/jansson_ei.cc +++ b/libs/error_injector/jansson/src/jansson_ei.cc @@ -74,4 +74,10 @@ json_t* __wrap_json_integer(json_int_t value) { return __real_json_integer(value); } +json_t* __real_json_string(const char *value); +CELIX_EI_DEFINE(json_string, json_t*) +json_t* __wrap_json_string(const char *value) { + CELIX_EI_IMPL(json_string); + return __real_json_string(value); +} } \ No newline at end of file From c39b58144a449d713f3826dfd220d0f01bbfa8fe Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 26 Jan 2024 21:51:21 +0800 Subject: [PATCH 52/62] Fix an issue caused by text reference in jsonRpc_prepareInvokeRequest. --- libs/dfi/src/json_rpc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index b02c2dd33..2833045c3 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -247,7 +247,8 @@ int jsonRpc_prepareInvokeRequest(const dyn_function_type* func, const char* id, } if (dynType_descriptorType(type) == 't') { - const char* metaArgument = dynType_getMetaInfo(type, "const"); + // we need to get meta info from the original type, which could be a reference, rather than the real type + const char* metaArgument = dynType_getMetaInfo(entry->type, "const"); if (metaArgument != NULL && strcmp("true", metaArgument) == 0) { //const char * as input -> nop } else { From 94903a2fc81b79984baebc7ac5ad79b7e08647e8 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sat, 27 Jan 2024 22:17:52 +0800 Subject: [PATCH 53/62] Optimize jsonRpc_call. 1. Add support for nullptr result for `am=out` parameter. 2. Fix memory leaks when `am=out` result fails to serialize. 3. Extract dynInterface_findMethod. --- libs/dfi/gtest/src/dyn_interface_tests.cpp | 18 ++ libs/dfi/gtest/src/json_rpc_tests.cpp | 225 ++++++++++++--------- libs/dfi/include/dyn_function.h | 2 +- libs/dfi/include/dyn_interface.h | 10 + libs/dfi/src/dyn_interface.c | 10 + libs/dfi/src/json_rpc.c | 69 +++---- 6 files changed, 195 insertions(+), 139 deletions(-) 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); + } } } From 4301a9823c76e34cce9f3d66651dbd0f7eb44fb8 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sat, 27 Jan 2024 22:27:52 +0800 Subject: [PATCH 54/62] Add test for dynFunction_getName. --- libs/dfi/gtest/src/dyn_function_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/dfi/gtest/src/dyn_function_tests.cpp b/libs/dfi/gtest/src/dyn_function_tests.cpp index 09328b8b7..d4e302469 100644 --- a/libs/dfi/gtest/src/dyn_function_tests.cpp +++ b/libs/dfi/gtest/src/dyn_function_tests.cpp @@ -49,6 +49,7 @@ TEST_F(DynFunctionTests, DynFuncTest1) { ASSERT_EQ(0, rc); EXPECT_TRUE(dynFunction_hasReturn(dynFunc)); EXPECT_EQ(3, dynFunction_nrOfArguments(dynFunc)); + EXPECT_STREQ("example", dynFunction_getName(dynFunc)); auto args = dynFunction_arguments(dynFunc); dyn_function_argument_type* arg = NULL; TAILQ_FOREACH(arg, args, entries) { From bfef295a49e7b5765462f564120424755d019cdd Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 28 Jan 2024 14:54:38 +0800 Subject: [PATCH 55/62] Enhance checking in dynInterface. For any method, there must be exactly one handle argument, that is, the first one. --- .../methodWithMultipleHandles.descriptor | 10 +++++ libs/dfi/gtest/src/dyn_interface_tests.cpp | 13 ++++++- libs/dfi/src/dyn_interface.c | 37 +++++++++---------- 3 files changed, 39 insertions(+), 21 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/invalids/methodWithMultipleHandles.descriptor diff --git a/libs/dfi/gtest/descriptors/invalids/methodWithMultipleHandles.descriptor b/libs/dfi/gtest/descriptors/invalids/methodWithMultipleHandles.descriptor new file mode 100644 index 000000000..1bbf1abfb --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/methodWithMultipleHandles.descriptor @@ -0,0 +1,10 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DD)D=add(#am=handle;P#am=handle;PDD#am=pre;*D)N diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index 4adda8d5d..5de1e8bd1 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -178,12 +178,21 @@ extern "C" { fclose(desc); desc=NULL; celix_err_resetErrors(); + /* Method with multiple handles */ + desc = fopen("descriptors/invalids/methodWithMultipleHandles.descriptor", "r"); + assert(desc != NULL); + status = dynInterface_parse(desc, &dynIntf); + ASSERT_NE(0, status); + EXPECT_STREQ("Parse Error. Handle argument is only allowed as the first argument for method add(DD)D (0)", celix_err_popLastError()); + fclose(desc); desc=NULL; + celix_err_resetErrors(); + /* Method with multiple PreOut arguments */ desc = fopen("descriptors/invalids/multiPreOutArgs.descriptor", "r"); assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); ASSERT_NE(0, status); - EXPECT_STREQ("Parse Error. Only one output argument is allowed for method multiPreOut(V)Di (0)", celix_err_popLastError()); + EXPECT_STREQ("Parse Error. Output argument is only allowed as the last argument for method multiPreOut(V)Di (0)", celix_err_popLastError()); fclose(desc); desc=NULL; celix_err_resetErrors(); @@ -192,7 +201,7 @@ extern "C" { assert(desc != NULL); status = dynInterface_parse(desc, &dynIntf); ASSERT_NE(0, status); - EXPECT_STREQ("Parse Error. Only one output argument is allowed for method multiOut(V)Di (0)", celix_err_popLastError()); + EXPECT_STREQ("Parse Error. Output argument is only allowed as the last argument for method multiOut(V)Di (0)", celix_err_popLastError()); fclose(desc); desc=NULL; celix_err_resetErrors(); diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index dfec535c2..5bbd78bf4 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -74,28 +74,27 @@ static int dynInterface_checkInterface(dyn_interface_type* intf) { return ERROR; } const struct dyn_function_arguments_head* args = dynFunction_arguments(mEntry->dynFunc); - dyn_function_argument_type* argEntry = TAILQ_FIRST(args); - if (argEntry == NULL || argEntry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__HANDLE) { + const dyn_function_argument_type* first = TAILQ_FIRST(args); + const dyn_function_argument_type* last = TAILQ_LAST(args, dyn_function_arguments_head); + if (first == NULL || first->argumentMeta != DYN_FUNCTION_ARGUMENT_META__HANDLE) { celix_err_pushf("Parse Error. The first argument must be handle for method %s (%d)", mEntry->id, mEntry->index); return ERROR; } - size_t nbOfOutputArgs = 0; - for (argEntry = TAILQ_NEXT(argEntry, entries); argEntry != NULL; argEntry = TAILQ_NEXT(argEntry, entries)) { - if (argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT || - argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - nbOfOutputArgs += 1; - } - } - if (nbOfOutputArgs > 1) { - celix_err_pushf("Parse Error. Only one output argument is allowed for method %s (%d)", mEntry->id, mEntry->index); - return ERROR; - } else if (nbOfOutputArgs > 0) { - argEntry = TAILQ_LAST(args, dyn_function_arguments_head); - if (argEntry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__OUTPUT && - argEntry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - celix_err_pushf("Parse Error. Output argument is only allowed as the last argument for method %s (%d)", - mEntry->id, mEntry->index); - return ERROR; + const dyn_function_argument_type* argEntry = NULL; + TAILQ_FOREACH(argEntry, args, entries) { + if (argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__HANDLE) { + if (argEntry != first) { + celix_err_pushf("Parse Error. Handle argument is only allowed as the first argument for method %s (%d)", + mEntry->id, mEntry->index); + return ERROR; + } + } else if (argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT || + argEntry->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + if (argEntry != last) { + celix_err_pushf("Parse Error. Output argument is only allowed as the last argument for method %s (%d)", + mEntry->id, mEntry->index); + return ERROR; + } } } } From e3120aef6be44d804bb3b49baadd9d72447fba2c Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 28 Jan 2024 21:44:44 +0800 Subject: [PATCH 56/62] Optimize jsonRpc_call. 1. Limit max number of arguments to CELIX_JSON_RPC_MAX_ARGS. 2. Apply early return error handling. 3. Simplify arguments manipulation and check for argument number mismatch. 4. More error injection tests. --- libs/dfi/gtest/CMakeLists.txt | 2 + .../invalids/methodWithTooManyArgs.descriptor | 10 + libs/dfi/gtest/src/json_rpc_ei_tests.cc | 112 +++++++++ libs/dfi/gtest/src/json_rpc_test.c | 66 ++++++ libs/dfi/gtest/src/json_rpc_test.h | 48 ++++ libs/dfi/gtest/src/json_rpc_tests.cpp | 114 ++++------ libs/dfi/src/json_rpc.c | 213 +++++++++--------- libs/error_injector/jansson/CMakeLists.txt | 1 + .../jansson/include/jansson_ei.h | 1 + libs/error_injector/jansson/src/jansson_ei.cc | 30 ++- 10 files changed, 416 insertions(+), 181 deletions(-) create mode 100644 libs/dfi/gtest/descriptors/invalids/methodWithTooManyArgs.descriptor create mode 100644 libs/dfi/gtest/src/json_rpc_test.c create mode 100644 libs/dfi/gtest/src/json_rpc_test.h diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index f55b751a3..7e23890f9 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(test_dfi src/json_serializer_tests.cpp src/json_rpc_tests.cpp src/dyn_common_tests.cc + src/json_rpc_test.c ) @@ -47,6 +48,7 @@ if (EI_TESTS) src/dyn_function_ei_tests.cc src/json_serializer_ei_tests.cc src/json_rpc_ei_tests.cc + src/json_rpc_test.c ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut diff --git a/libs/dfi/gtest/descriptors/invalids/methodWithTooManyArgs.descriptor b/libs/dfi/gtest/descriptors/invalids/methodWithTooManyArgs.descriptor new file mode 100644 index 000000000..a89a5b89e --- /dev/null +++ b/libs/dfi/gtest/descriptors/invalids/methodWithTooManyArgs.descriptor @@ -0,0 +1,10 @@ +:header +type=interface +name=calculator +version=1.0.0 +:annotations +classname=org.example.Calculator +:types +StatsResult={DDD[D average min max input} +:methods +add(DDDDDDDDDDDDDDD)D=add(#am=handle;PDDDDDDDDDDDDDDD#am=pre;*D)N diff --git a/libs/dfi/gtest/src/json_rpc_ei_tests.cc b/libs/dfi/gtest/src/json_rpc_ei_tests.cc index 46b90b09d..dd0851fc4 100644 --- a/libs/dfi/gtest/src/json_rpc_ei_tests.cc +++ b/libs/dfi/gtest/src/json_rpc_ei_tests.cc @@ -18,9 +18,13 @@ */ #include "json_rpc.h" +#include "json_rpc_test.h" +#include "json_serializer.h" +#include "dyn_type.h" #include "dyn_function.h" #include "celix_err.h" #include "jansson_ei.h" +#include "malloc_ei.h" #include @@ -30,6 +34,10 @@ class JsonRpcErrorInjectionTestSuite : public ::testing::Test { } ~JsonRpcErrorInjectionTestSuite() override { + celix_ei_expect_json_dumps(nullptr, 0, nullptr); + celix_ei_expect_json_object(nullptr, 0, nullptr); + celix_ei_expect_json_real(nullptr, 0, nullptr); + celix_ei_expect_calloc(nullptr, 0, nullptr); celix_ei_expect_json_dumps(nullptr, 0, nullptr); celix_ei_expect_json_array_append_new(nullptr, 0, -1); celix_ei_expect_json_string(nullptr, 0, nullptr); @@ -74,4 +82,108 @@ TEST_F(JsonRpcErrorInjectionTestSuite, prepareErrorTest) { ASSERT_NE(0, rc); dynFunction_destroy(dynFunc); +} + +struct tst_serv { + void *handle; + int (*add)(void *, double, double, double *); + int (*sub)(void *, double, double, double *); + int (*sqrt)(void *, double, double *); + int (*stats)(void *, struct tst_seq, struct tst_StatsResult **); +}; + +TEST_F(JsonRpcErrorInjectionTestSuite, preallocationFailureTest) { + 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, add, nullptr, nullptr, nullptr}; + + celix_ei_expect_calloc((void*)dynType_alloc, 0, nullptr); + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DD)D", "a": [1.0,2.0]})", &result); + ASSERT_NE(0, rc); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Error allocating memory for pre-allocated output argument of add(DD)D", celix_err_popLastError()); + EXPECT_STREQ("Error allocating memory for type 'D'", celix_err_popLastError()); + dynInterface_destroy(intf); +} + +TEST_F(JsonRpcErrorInjectionTestSuite, preOutParamterSerializationFailureTest) { + 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, add, nullptr, nullptr, nullptr}; + + celix_ei_expect_json_real((void*)jsonSerializer_serializeJson, 2, nullptr); + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DD)D", "a": [1.0,2.0]})", &result); + ASSERT_NE(0, rc); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Error serializing result for add(DD)D", celix_err_popLastError()); + dynInterface_destroy(intf); +} + +TEST_F(JsonRpcErrorInjectionTestSuite, outParamterSerializationFailureTest) { + 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, stats}; + + celix_ei_expect_json_object((void*)jsonSerializer_serializeJson, 3, nullptr); + rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": [[1.0,2.0]]})", &result); + ASSERT_NE(0, rc); + EXPECT_STREQ("Error serializing result for stats([D)LStatsResult;", celix_err_popLastError()); + + free(result); + dynInterface_destroy(intf); +} + +TEST_F(JsonRpcErrorInjectionTestSuite, responsePayloadGenerationErrorTest) { + 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, add, nullptr, nullptr, nullptr}; + + celix_ei_expect_json_object((void*)jsonRpc_call, 0, nullptr); + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DD)D", "a": [1.0,2.0]})", &result); + ASSERT_NE(0, rc); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Error generating response payload for add(DD)D", celix_err_popLastError()); + dynInterface_destroy(intf); +} + +TEST_F(JsonRpcErrorInjectionTestSuite, responseRenderingErrorTest) { + 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, add, nullptr, nullptr, nullptr}; + + celix_ei_expect_json_dumps((void*)jsonRpc_call, 0, nullptr); + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DD)D", "a": [1.0,2.0]})", &result); + ASSERT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynInterface_destroy(intf); } \ No newline at end of file diff --git a/libs/dfi/gtest/src/json_rpc_test.c b/libs/dfi/gtest/src/json_rpc_test.c new file mode 100644 index 000000000..0219b9bb3 --- /dev/null +++ b/libs/dfi/gtest/src/json_rpc_test.c @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "json_rpc_test.h" +#include +#include +#include +#include +#include + +int add(void*, double a, double b, double *result) { + *result = a + b; + return 0; +} + +int stats(void*, struct tst_seq input, struct tst_StatsResult **out) { + assert(out != NULL); + assert(*out == NULL); + double total = 0.0; + unsigned int count = 0; + double max = DBL_MIN; + double min = DBL_MAX; + + unsigned int i; + for (i = 0; i max) { + max = input.buf[i]; + } + if (input.buf[i] < min) { + min = input.buf[i]; + } + } + + struct tst_StatsResult* result = (struct tst_StatsResult *)(calloc(1, sizeof(struct tst_StatsResult))); + if(count>0) { + result->average = total / count; + } + result->min = min; + result->max = max; + double* buf = (double *)(calloc(input.len, sizeof(double))); + memcpy(buf, input.buf, input.len * sizeof(double)); + result->input.len = input.len; + result->input.cap = input.len; + result->input.buf = buf; + + *out = result; + return 0; +} diff --git a/libs/dfi/gtest/src/json_rpc_test.h b/libs/dfi/gtest/src/json_rpc_test.h new file mode 100644 index 000000000..2e8deef44 --- /dev/null +++ b/libs/dfi/gtest/src/json_rpc_test.h @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef CELIX_JSON_RPC_TEST_H +#define CELIX_JSON_RPC_TEST_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct tst_seq { + uint32_t cap; + uint32_t len; + double *buf; +}; + + +//StatsResult={DDD[D average min max input} +struct tst_StatsResult { + double average; + double min; + double max; + struct tst_seq input; +}; + +int add(void*, double a, double b, double *result); +int stats(void*, struct tst_seq input, struct tst_StatsResult **out); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_JSON_RPC_TEST_H diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp b/libs/dfi/gtest/src/json_rpc_tests.cpp index aa92c5326..260695a91 100644 --- a/libs/dfi/gtest/src/json_rpc_tests.cpp +++ b/libs/dfi/gtest/src/json_rpc_tests.cpp @@ -37,6 +37,7 @@ extern "C" { #include "dyn_function.h" #include "json_serializer.h" #include "json_rpc.h" +#include "json_rpc_test.h" #include "celix_compiler.h" #include "celix_errno.h" #include "celix_err.h" @@ -177,13 +178,6 @@ extern "C" { dynFunction_destroy(dynFunc); } - - - int add(void*, double a, double b, double *result) { - *result = a + b; - return 0; - } - int addFailed(void*, double , double , double *) { return CELIX_CUSTOMER_ERROR_MAKE(0,1);// return customer error } @@ -193,58 +187,6 @@ extern "C" { return 0; } - struct tst_seq { - uint32_t cap; - uint32_t len; - double *buf; - }; - - - //StatsResult={DDD[D average min max input} - struct tst_StatsResult { - double average; - double min; - double max; - struct tst_seq input; - }; - - - int stats(void*, struct tst_seq input, struct tst_StatsResult **out) { - assert(out != nullptr); - assert(*out == nullptr); - double total = 0.0; - unsigned int count = 0; - auto max = DBL_MIN; - auto min = DBL_MAX; - - unsigned int i; - for (i = 0; i max) { - max = input.buf[i]; - } - if (input.buf[i] < min) { - min = input.buf[i]; - } - } - - auto result = static_cast(calloc(1, sizeof(tst_StatsResult))); - if(count>0) { - result->average = total / count; - } - result->min = min; - result->max = max; - auto buf = static_cast(calloc(input.len, sizeof(double))); - memcpy(buf, input.buf, input.len * sizeof(double)); - result->input.len = input.len; - result->input.cap = input.len; - result->input.buf = buf; - - *out = result; - return 0; - } - struct item { double a; double b; @@ -342,28 +284,37 @@ extern "C" { rc = jsonRpc_call(intf, &serv, R"({)", &result); EXPECT_STREQ("Got json error: string or '}' expected near end of file", celix_err_popLastError()); ASSERT_EQ(1, rc); + celix_err_resetErrors(); rc = jsonRpc_call(intf, &serv, R"({"a": [1.0,2.0]})", &result); EXPECT_STREQ("Error getting method signature", celix_err_popLastError()); ASSERT_EQ(1, rc); + celix_err_resetErrors(); //request missing argument rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;"})", &result); - EXPECT_STREQ("Error getting arguments array", celix_err_popLastError()); + EXPECT_STREQ("Error getting arguments array for stats([D)LStatsResult;", celix_err_popLastError()); ASSERT_EQ(1, rc); + celix_err_resetErrors(); //request non-array argument rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": "hello"})", &result); - EXPECT_STREQ("Error getting arguments array", celix_err_popLastError()); + EXPECT_STREQ("Error getting arguments array for stats([D)LStatsResult;", celix_err_popLastError()); ASSERT_EQ(1, rc); + celix_err_resetErrors(); // argument number mismatch - //rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": []})", &result); - //ASSERT_EQ(1, rc); + rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": []})", &result); + ASSERT_EQ(1, rc); + EXPECT_STREQ("Wrong number of standard arguments for stats([D)LStatsResult;. Expected 1, got 0", celix_err_popLastError()); + celix_err_resetErrors(); //request argument type mismatch rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": [1.0]})", &result); ASSERT_EQ(1, rc); + EXPECT_STREQ("Error deserializing argument 1 for stats([D)LStatsResult;", celix_err_popLastError()); + EXPECT_STREQ("Expected json array type got '4'", celix_err_popLastError()); + celix_err_resetErrors(); dynInterface_destroy(intf); } @@ -778,6 +729,24 @@ TEST_F(JsonRpcTests, callPre) { callTestPreAllocated(); } +TEST_F(JsonRpcTests, callPreWithMismatchedArgumentNumber) { + 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, add, nullptr, nullptr, nullptr}; + + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DD)D", "a": [1.0,2.0,3.0]})", &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Wrong number of standard arguments for add(DD)D. Expected 2, got 3", celix_err_popLastError()); + dynInterface_destroy(intf); +} + TEST_F(JsonRpcTests, callFailedPre) { callFailedTestPreAllocated(); } @@ -786,7 +755,6 @@ TEST_F(JsonRpcTests, callOut) { callTestOutput(); } - TEST_F(JsonRpcTests, callOutNullResult) { dyn_interface_type *intf = nullptr; FILE *desc = fopen("descriptors/example1.descriptor", "r"); @@ -946,4 +914,22 @@ TEST_F(JsonRpcTests, callTestChar) { TEST_F(JsonRpcTests, callTestConstChar) { callTestConstChar(); +} + +TEST_F(JsonRpcTests, callWithTooManyArguments) { + dyn_interface_type *intf = nullptr; + FILE *desc = fopen("descriptors/invalids/methodWithTooManyArgs.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, add, nullptr, nullptr, nullptr}; + + rc = jsonRpc_call(intf, &serv, R"({"m":"add(DDDDDDDDDDDDDDD)D", "a": [1.0,2.0]})", &result); + ASSERT_NE(0, rc); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Too many arguments for add(DDDDDDDDDDDDDDD)D: 17 > 16", celix_err_popLastError()); + dynInterface_destroy(intf); } \ No newline at end of file diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index 707d81bc3..b744b4e4e 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -22,7 +22,7 @@ #include "dyn_type.h" #include "dyn_interface.h" #include "dyn_type_common.h" -#include "celix_compiler.h" +#include "celix_cleanup.h" #include "celix_err.h" #include @@ -30,6 +30,8 @@ #include #include +#define CELIX_JSON_RPC_MAX_ARGS 16 + static int OK = 0; static int ERROR = 1; @@ -40,6 +42,51 @@ struct generic_service_layout { gen_func_type methods[]; }; +typedef struct celix_rpc_args { + const struct dyn_function_arguments_head* dynArgs; + void* args[CELIX_JSON_RPC_MAX_ARGS]; +}celix_rpc_args_t; + +static void celix_rpcArgs_cleanup(celix_rpc_args_t* args) { + const struct dyn_function_arguments_head* dynArgs = args->dynArgs; + if (dynArgs == NULL) { + return; + } + dyn_function_argument_type* entry = NULL; + TAILQ_FOREACH(entry, dynArgs, entries) { + const dyn_type* argType = dynType_realType(entry->type); + enum dyn_function_argument_meta meta = entry->argumentMeta; + if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { + if (dynType_descriptorType(argType) == 't') { + const char* isConst = dynType_getMetaInfo(entry->type, "const"); + if (isConst != NULL && strncmp("true", isConst, 5) == 0) { + dynType_free(argType, args->args[entry->index]); + } else { + //char* -> callee is now owner, no free for char seq needed + //will free the actual pointer + free(args->args[entry->index]); + } + } else { + dynType_free(argType, args->args[entry->index]); + } + } else if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + const dyn_type* subType = dynType_typedPointer_getTypedType(argType); + dynType_free(subType, *(void**)(args->args[entry->index])); + } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { + const dyn_type* typedType = dynType_typedPointer_getTypedType(argType); + if (dynType_descriptorType(typedType) == 't') { + free(**(void***)args->args[entry->index]); + } else { + const dyn_type* typedTypedType = dynType_typedPointer_getTypedType(typedType); + dynType_free(typedTypedType, **(void***)args->args[entry->index]); + } + } + } + args->dynArgs = NULL; +} + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_rpc_args_t, celix_rpcArgs_cleanup) + int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* request, char** out) { int status = OK; @@ -57,7 +104,7 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ } arguments = json_object_get(js_request, "a"); if (arguments == NULL || !json_is_array(arguments)) { - celix_err_push("Error getting arguments array"); + celix_err_pushf("Error getting arguments array for %s", sig); return ERROR; } @@ -68,129 +115,83 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ } struct generic_service_layout* serv = service; - - const dyn_function_type* func = method->dynFunc; - int nrOfArgs = dynFunction_nrOfArguments(func); - void* args[nrOfArgs]; - - json_t* value = NULL; - - int i; - int index = 0; - + const struct dyn_function_arguments_head* dynArgs = dynFunction_arguments(method->dynFunc); + const dyn_function_argument_type* last = TAILQ_LAST(dynArgs, dyn_function_arguments_head); + int nrOfArgs = dynFunction_nrOfArguments(method->dynFunc); + if (nrOfArgs > CELIX_JSON_RPC_MAX_ARGS) { + celix_err_pushf("Too many arguments for %s: %d > %d", sig, nrOfArgs, CELIX_JSON_RPC_MAX_ARGS); + return ERROR; + } + celix_auto(celix_rpc_args_t) rpcArgs = { dynArgs, {0} }; void* ptr = NULL; void* ptrToPtr = &ptr; + rpcArgs.args[0] = &serv->handle; + --nrOfArgs; + if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + const dyn_type *subType = dynType_typedPointer_getTypedType(dynType_realType(last->type)); + rpcArgs.args[last->index] = &ptr; + if ((status = dynType_alloc(subType, &ptr)) != OK) { + celix_err_pushf("Error allocating memory for pre-allocated output argument of %s", sig); + return ERROR; + } + --nrOfArgs; + } else if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { + rpcArgs.args[last->index] = &ptrToPtr; + --nrOfArgs; + } + if ((size_t)nrOfArgs != json_array_size(arguments)) { + celix_err_pushf("Wrong number of standard arguments for %s. Expected %d, got %zu", + sig, nrOfArgs, json_array_size(arguments)); + return ERROR; + } //setup and deserialize input - for (i = 0; i < nrOfArgs; ++i) { - const dyn_type* argType = dynType_realType(dynFunction_argumentTypeForIndex(func, i)); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { - value = json_array_get(arguments, index++); - void* outPtr = NULL; - status = jsonSerializer_deserializeJson(argType, value, &outPtr); - args[i] = outPtr; - } else if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - void* inst = NULL; - const dyn_type *subType = dynType_typedPointer_getTypedType(argType); - dynType_alloc(subType, &inst); - 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] = &serv->handle; + dyn_function_argument_type* entry = NULL; + TAILQ_FOREACH(entry, dynArgs, entries) { + if (entry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__STD) { + continue; } - + status = jsonSerializer_deserializeJson(entry->type, json_array_get(arguments, entry->index-1), &(rpcArgs.args[entry->index])); if (status != OK) { - // FIXME: part of args uninitialized - break; + celix_err_pushf("Error deserializing argument %d for %s", entry->index, sig); + return status; } } - json_decref(celix_steal_ptr(js_request)); - ffi_sarg returnVal = 1; - - if (status == OK) { - status = dynFunction_call(func, serv->methods[method->index], (void *) &returnVal, args); - } + (void)dynFunction_call(method->dynFunc, serv->methods[method->index], (void *) &returnVal, rpcArgs.args); int funcCallStatus = (int)returnVal; - - //free input args - json_t* jsonResult = NULL; - for(i = 0; i < nrOfArgs; ++i) { - const dyn_type* argType = dynFunction_argumentTypeForIndex(func, i); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__STD) { - if (dynType_descriptorType(argType) == 't') { - const char* isConst = dynType_getMetaInfo(argType, "const"); - if (isConst != NULL && strncmp("true", isConst, 5) == 0) { - dynType_free(argType, args[i]); - } else { - //char* -> callee is now owner, no free for char seq needed - //will free the actual pointer - free(args[i]); - } - } else { - dynType_free(argType, args[i]); - } + //serialize output + json_auto_t* jsonResult = NULL; + if (funcCallStatus == 0) { + const dyn_type* argType = dynType_realType(last->type); + if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { + status = jsonSerializer_serializeJson(argType, rpcArgs.args[last->index], &jsonResult); + } else if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { + status = jsonSerializer_serializeJson(dynType_typedPointer_getTypedType(argType), (void*) &ptr, &jsonResult); } - } - - //serialize and free output - for (i = 0; i < nrOfArgs; i += 1) { - const dyn_type* argType = dynType_realType(dynFunction_argumentTypeForIndex(func, i)); - enum dyn_function_argument_meta meta = dynFunction_argumentMetaForIndex(func, i); - if (meta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) { - if (funcCallStatus == 0 && status == OK) { - status = jsonSerializer_serializeJson(argType, args[i], &jsonResult); - } - const dyn_type* subType = dynType_typedPointer_getTypedType(argType); - dynType_free(subType, ptr); - } else if (meta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) { - 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); - } - } - if (status != OK) { - break; + celix_err_pushf("Error serializing result for %s", sig); + return status; } } + celix_rpcArgs_cleanup(&rpcArgs); - char* response = NULL; - if (status == OK) { - json_t* payload = json_object(); - if (funcCallStatus == 0) { - if (jsonResult == NULL) { - //ignore -> no result - } else { - json_object_set_new_nocheck(payload, "r", jsonResult); - } - } else { - json_object_set_new_nocheck(payload, "e", json_integer(funcCallStatus)); + json_auto_t* payload = json_object(); + if (funcCallStatus == 0) { + if (jsonResult != NULL) { + status = json_object_set_new_nocheck(payload, "r", celix_steal_ptr(jsonResult)); } - //use JSON_COMPACT to reduce the size of the JSON string. - response = json_dumps(payload, JSON_COMPACT | JSON_ENCODE_ANY); - json_decref(payload); - } - - if (status == OK) { - *out = response; } else { - free(response); + status = json_object_set_new_nocheck(payload, "e", json_integer(funcCallStatus)); } - - return status; + if (status != 0) { + celix_err_pushf("Error generating response payload for %s", sig); + return ERROR; + } + //use JSON_COMPACT to reduce the size of the JSON string. + *out = json_dumps(payload, JSON_COMPACT | JSON_ENCODE_ANY); + return (*out != NULL) ? OK : ERROR; } int jsonRpc_prepareInvokeRequest(const dyn_function_type* func, const char* id, void* args[], char** out) { diff --git a/libs/error_injector/jansson/CMakeLists.txt b/libs/error_injector/jansson/CMakeLists.txt index d325c1a2f..b536956a0 100644 --- a/libs/error_injector/jansson/CMakeLists.txt +++ b/libs/error_injector/jansson/CMakeLists.txt @@ -34,5 +34,6 @@ target_link_options(jansson_ei INTERFACE LINKER:--wrap,json_array_append_new LINKER:--wrap,json_integer LINKER:--wrap,json_string + LINKER:--wrap,json_real ) add_library(Celix::jansson_ei ALIAS jansson_ei) diff --git a/libs/error_injector/jansson/include/jansson_ei.h b/libs/error_injector/jansson/include/jansson_ei.h index dd91168ea..60f04e45d 100644 --- a/libs/error_injector/jansson/include/jansson_ei.h +++ b/libs/error_injector/jansson/include/jansson_ei.h @@ -33,6 +33,7 @@ CELIX_EI_DECLARE(json_array, json_t*); CELIX_EI_DECLARE(json_array_append_new, int); CELIX_EI_DECLARE(json_integer, json_t*); CELIX_EI_DECLARE(json_string, json_t*); +CELIX_EI_DECLARE(json_real, json_t*); #ifdef __cplusplus } diff --git a/libs/error_injector/jansson/src/jansson_ei.cc b/libs/error_injector/jansson/src/jansson_ei.cc index 1cf4756b3..57033c0c4 100644 --- a/libs/error_injector/jansson/src/jansson_ei.cc +++ b/libs/error_injector/jansson/src/jansson_ei.cc @@ -30,16 +30,16 @@ size_t __wrap_json_array_size(const json_t *array) { return __real_json_array_size(array); } -char* __real_json_dumps(const json_t *json, size_t flags); +char *__real_json_dumps(const json_t *json, size_t flags); CELIX_EI_DEFINE(json_dumps, char*) -char* __wrap_json_dumps(const json_t *json, size_t flags) { +char *__wrap_json_dumps(const json_t *json, size_t flags) { CELIX_EI_IMPL(json_dumps); return __real_json_dumps(json, flags); } -json_t* __real_json_object(void); +json_t *__real_json_object(void); CELIX_EI_DEFINE(json_object, json_t*) -json_t* __wrap_json_object(void) { +json_t *__wrap_json_object(void) { CELIX_EI_IMPL(json_object); return __real_json_object(); } @@ -47,14 +47,14 @@ json_t* __wrap_json_object(void) { int __real_json_object_set_new(json_t *object, const char *key, json_t *value); CELIX_EI_DEFINE(json_object_set_new, int) int __wrap_json_object_set_new(json_t *object, const char *key, json_t *value) { - json_auto_t* val = value; + json_auto_t *val = value; CELIX_EI_IMPL(json_object_set_new); return __real_json_object_set_new(object, key, celix_steal_ptr(val)); } -json_t* __real_json_array(void); +json_t *__real_json_array(void); CELIX_EI_DEFINE(json_array, json_t*) -json_t* __wrap_json_array(void) { +json_t *__wrap_json_array(void) { CELIX_EI_IMPL(json_array); return __real_json_array(); } @@ -67,17 +67,25 @@ int __wrap_json_array_append_new(json_t *array, json_t *value) { return __real_json_array_append_new(array, celix_steal_ptr(val)); } -json_t* __real_json_integer(json_int_t value); +json_t *__real_json_integer(json_int_t value); CELIX_EI_DEFINE(json_integer, json_t*) -json_t* __wrap_json_integer(json_int_t value) { +json_t *__wrap_json_integer(json_int_t value) { CELIX_EI_IMPL(json_integer); return __real_json_integer(value); } -json_t* __real_json_string(const char *value); +json_t *__real_json_string(const char *value); CELIX_EI_DEFINE(json_string, json_t*) -json_t* __wrap_json_string(const char *value) { +json_t *__wrap_json_string(const char *value) { CELIX_EI_IMPL(json_string); return __real_json_string(value); } + +json_t *__real_json_real(double value); +CELIX_EI_DEFINE(json_real, json_t*) +json_t *__wrap_json_real(double value) { + CELIX_EI_IMPL(json_real); + return __real_json_real(value); +} + } \ No newline at end of file From 51f0f1d9ebe0ba2e97b30c7205bde5c26350dcd3 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 28 Jan 2024 21:46:52 +0800 Subject: [PATCH 57/62] Fix compilation errors. --- libs/dfi/gtest/src/json_rpc_test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/dfi/gtest/src/json_rpc_test.c b/libs/dfi/gtest/src/json_rpc_test.c index 0219b9bb3..57227b8f1 100644 --- a/libs/dfi/gtest/src/json_rpc_test.c +++ b/libs/dfi/gtest/src/json_rpc_test.c @@ -18,18 +18,19 @@ */ #include "json_rpc_test.h" +#include "celix_compiler.h" #include #include #include #include #include -int add(void*, double a, double b, double *result) { +int add(void* handle CELIX_UNUSED, double a, double b, double *result) { *result = a + b; return 0; } -int stats(void*, struct tst_seq input, struct tst_StatsResult **out) { +int stats(void* handle CELIX_UNUSED, struct tst_seq input, struct tst_StatsResult **out) { assert(out != NULL); assert(*out == NULL); double total = 0.0; From 876471dd5f82ad676441a7340035cc110fbfcc6d Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 28 Jan 2024 21:59:53 +0800 Subject: [PATCH 58/62] Fix stack-use-after-scope reported by clang build. `rpcArgs` refers to stack variables, which must be declared before `rpcArgs`. --- libs/dfi/src/json_rpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c index b744b4e4e..d6547be9c 100644 --- a/libs/dfi/src/json_rpc.c +++ b/libs/dfi/src/json_rpc.c @@ -122,9 +122,9 @@ int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* requ celix_err_pushf("Too many arguments for %s: %d > %d", sig, nrOfArgs, CELIX_JSON_RPC_MAX_ARGS); return ERROR; } - celix_auto(celix_rpc_args_t) rpcArgs = { dynArgs, {0} }; void* ptr = NULL; void* ptrToPtr = &ptr; + celix_auto(celix_rpc_args_t) rpcArgs = { dynArgs, {0} }; rpcArgs.args[0] = &serv->handle; --nrOfArgs; From 8d1b0bf85b52abbbfa66c93ba57650e8aeb93df5 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 28 Jan 2024 22:16:24 +0800 Subject: [PATCH 59/62] Add test for dynFunction_argumentMetaForIndex. --- libs/dfi/gtest/src/dyn_interface_tests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp b/libs/dfi/gtest/src/dyn_interface_tests.cpp index 5de1e8bd1..58443521a 100644 --- a/libs/dfi/gtest/src/dyn_interface_tests.cpp +++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp @@ -60,6 +60,13 @@ extern "C" { checkInterfaceVersion(dynIntf,"1.0.0"); + auto method = dynInterface_findMethod(dynIntf, "add(DD)D"); + EXPECT_EQ(DYN_FUNCTION_ARGUMENT_META__HANDLE, dynFunction_argumentMetaForIndex(method->dynFunc, 0)); + EXPECT_EQ(DYN_FUNCTION_ARGUMENT_META__STD, dynFunction_argumentMetaForIndex(method->dynFunc, 1)); + EXPECT_EQ(DYN_FUNCTION_ARGUMENT_META__STD, dynFunction_argumentMetaForIndex(method->dynFunc, 2)); + EXPECT_EQ(DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT, dynFunction_argumentMetaForIndex(method->dynFunc, 3)); + EXPECT_EQ(DYN_FUNCTION_ARGUMENT_META__STD, dynFunction_argumentMetaForIndex(method->dynFunc, 4)); + const char *annVal = NULL; status = dynInterface_getAnnotationEntry(dynIntf, "classname", &annVal); ASSERT_EQ(0, status); From d099a70433a46700c78b125823e61bb1e2fc14e4 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 31 Jan 2024 13:49:11 +0800 Subject: [PATCH 60/62] Apply suggestions from code review Co-authored-by: Pepijn Noltes --- libs/dfi/include/dyn_type.h | 2 +- libs/dfi/src/dyn_common.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 20c8c6504..77f4dbec7 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -377,7 +377,7 @@ CELIX_DFI_EXPORT void dynType_simple_setValue(const dyn_type* type, void* inst, * Any non-pointer simple type is trivial, while typed/untyped pointer is non-trivial. * A sequence is non-trivial since it contains a pointer to the buffer. * Text is nontrivial. - * A complex is trivial iff each field is trivial. + * A complex is trivial if each field is trivial. */ CELIX_DFI_EXPORT bool dynType_isTrivial(const dyn_type* type); diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 06b8c2cc6..2b92075e3 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -56,7 +56,7 @@ int dynCommon_parseNameAlsoAccept(FILE* stream, const char* acceptedChars, char* ungetc(c, stream); } if(fclose(celix_steal_ptr(name)) != 0) { - celix_err_pushf("Error creating mem stream for name. %s", strerror(errno)); + celix_err_pushf("Error closing mem stream for name. %s", strerror(errno)); return ERROR; } From ca7dab29ece12ef99926aec767cea9e0af408e31 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 31 Jan 2024 13:55:07 +0800 Subject: [PATCH 61/62] Fix failing test. --- libs/dfi/gtest/src/dyn_common_ei_tests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/dfi/gtest/src/dyn_common_ei_tests.cc b/libs/dfi/gtest/src/dyn_common_ei_tests.cc index 4efdf5d53..8e92e631c 100644 --- a/libs/dfi/gtest/src/dyn_common_ei_tests.cc +++ b/libs/dfi/gtest/src/dyn_common_ei_tests.cc @@ -68,7 +68,7 @@ TEST_F(DynCommonErrorInjectionTestSuite, ParseNameErrors) { // fail to close name stream celix_ei_expect_fclose((void *) dynCommon_parseName, 1, EOF); ASSERT_EQ(dynCommon_parseName(stream, &result), 1); - msg = "Error creating mem stream for name. "; + msg = "Error closing mem stream for name. "; msg += strerror(ENOSPC); ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); } From 90d643a93286279aac71b6205fc413c49be2c651 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 31 Jan 2024 14:31:05 +0800 Subject: [PATCH 62/62] Some early return fixes and documentation improvements. --- libs/dfi/include/dyn_interface.h | 8 ++++---- libs/dfi/src/dyn_common.c | 30 ++++++++++++++---------------- libs/dfi/src/dyn_type.c | 22 +++++++--------------- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/libs/dfi/include/dyn_interface.h b/libs/dfi/include/dyn_interface.h index fa9dad443..1694dbda3 100644 --- a/libs/dfi/include/dyn_interface.h +++ b/libs/dfi/include/dyn_interface.h @@ -45,9 +45,9 @@ typedef struct _dyn_interface_type dyn_interface_type; TAILQ_HEAD(methods_head, method_entry); struct method_entry { - int index; - char* id; - dyn_function_type* dynFunc; + int index; ///< The index of the method in the interface + char* id; ///< The signature of the method + dyn_function_type* dynFunc; ///< The function type of the method TAILQ_ENTRY(method_entry) entries; }; @@ -141,7 +141,7 @@ CELIX_DFI_EXPORT int dynInterface_nrOfMethods(const dyn_interface_type* intf); * 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. + * @param[in] id The id of the method to find, which is currently the signature of the method. * @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); diff --git a/libs/dfi/src/dyn_common.c b/libs/dfi/src/dyn_common.c index 2b92075e3..4a27c1a55 100644 --- a/libs/dfi/src/dyn_common.c +++ b/libs/dfi/src/dyn_common.c @@ -74,22 +74,20 @@ static int dynCommon_parseNameValue(FILE* stream, char** outName, char** outValu int status; celix_autofree char* name = NULL; celix_autofree char* value = NULL; - do { - if ((status = dynCommon_parseName(stream, &name)) != OK) { - break; - } - if ((status = dynCommon_eatChar(stream, '=')) != OK) { - break; - } - const char *valueAcceptedChars = ".<>{}[]?;:~!@#$%^&*()_+-=,./\\'\""; - //NOTE use different more lenient function e.g. only stop at '\n' ? - if ((status = dynCommon_parseNameAlsoAccept(stream, valueAcceptedChars, &value)) != OK) { - break; - } - *outName = celix_steal_ptr(name); - *outValue = celix_steal_ptr(value); - } while(false); - return status; + if ((status = dynCommon_parseName(stream, &name)) != OK) { + return status; + } + if ((status = dynCommon_eatChar(stream, '=')) != OK) { + return status; + } + const char *valueAcceptedChars = ".<>{}[]?;:~!@#$%^&*()_+-=,./\\'\""; + //NOTE use different more lenient function e.g. only stop at '\n' ? + if ((status = dynCommon_parseNameAlsoAccept(stream, valueAcceptedChars, &value)) != OK) { + return status; + } + *outName = celix_steal_ptr(name); + *outValue = celix_steal_ptr(value); + return OK; } int dynCommon_eatChar(FILE* stream, int expected) { diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index 6a15179a2..399bb7663 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -177,6 +177,8 @@ static int dynType_parseAny(FILE* stream, dyn_type* type) { status = dynType_parseMetaInfo(stream, type); if (status == OK) { status = dynType_parseAny(stream, type); + } else { + celix_err_push("Failed to parse meta properties"); } break; default : @@ -188,39 +190,29 @@ static int dynType_parseAny(FILE* stream, dyn_type* type) { } static int dynType_parseMetaInfo(FILE* stream, dyn_type* type) { - int status = OK; celix_autofree char* name = NULL; celix_autofree char* value = NULL; if (dynCommon_parseName(stream, &name) != OK) { - status = PARSE_ERROR; - goto bail_out; + return PARSE_ERROR; } if (dynCommon_eatChar(stream, '=') != OK) { - status = PARSE_ERROR; - goto bail_out; + return PARSE_ERROR; } if (dynCommon_parseName(stream, &value) != OK) { - status = PARSE_ERROR; - goto bail_out; + return PARSE_ERROR; } if (dynCommon_eatChar(stream, ';') != OK) { - status = PARSE_ERROR; - goto bail_out; + return PARSE_ERROR; } struct meta_entry *entry = calloc(1, sizeof(*entry)); if (entry == NULL) { - status = MEM_ERROR; - goto bail_out; + return MEM_ERROR; } entry->name = celix_steal_ptr(name); entry->value = celix_steal_ptr(value); TAILQ_INSERT_TAIL(&type->metaProperties, entry, entries); return OK; - -bail_out: - celix_err_push("Failed to parse meta properties"); - return status; } static int dynType_parseText(FILE* stream, dyn_type* type) {