Skip to content

Commit

Permalink
#723 Add triviality test for dynType so that definitions of dfi argum…
Browse files Browse the repository at this point in the history
…ent 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.
  • Loading branch information
PengZheng committed Jan 25, 2024
1 parent f896601 commit e34eb00
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 39 deletions.
18 changes: 18 additions & 0 deletions libs/dfi/gtest/src/dyn_function_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
65 changes: 65 additions & 0 deletions libs/dfi/gtest/src/dyn_type_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
24 changes: 0 additions & 24 deletions libs/dfi/gtest/src/json_rpc_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -905,10 +885,6 @@ TEST_F(JsonRpcTests, handleOutChar) {
handleTestOutChar();
}

TEST_F(JsonRpcTests, handlePreChar) {
handleTestPreChar();
}

TEST_F(JsonRpcTests, handleReplyError) {
handleTestReplyError();
}
Expand Down
6 changes: 4 additions & 2 deletions libs/dfi/include/dyn_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions libs/dfi/include/dyn_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions libs/dfi/src/dyn_function.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions libs/dfi/src/dyn_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

18 changes: 5 additions & 13 deletions libs/dfi/src/json_rpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit e34eb00

Please sign in to comment.