Skip to content

Commit 48d8256

Browse files
committed
Add test for reconstructing out parameters on arguments of functions in c loader.
1 parent 4f68ac2 commit 48d8256

File tree

3 files changed

+93
-10
lines changed

3 files changed

+93
-10
lines changed

source/loaders/c_loader/source/c_loader_impl.cpp

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,19 @@ class c_loader_closure_value
516516
}
517517
};
518518

519+
class c_loader_pointer_type : public c_loader_type_impl
520+
{
521+
protected:
522+
loader_impl impl;
523+
CXType cx_type;
524+
525+
public:
526+
c_loader_pointer_type(loader_impl impl, CXType cx_type) :
527+
impl(impl), cx_type(cx_type) {}
528+
529+
~c_loader_pointer_type() {}
530+
};
531+
519532
std::string c_loader_impl_cxstring_to_str(const CXString &s)
520533
{
521534
std::string result = clang_getCString(s);
@@ -662,6 +675,7 @@ function_return function_c_interface_invoke(function func, function_impl impl, f
662675
for (size_t args_count = 0; args_count < args_size; ++args_count)
663676
{
664677
type t = signature_get_type(s, args_count);
678+
type_impl impl_type = type_derived(t);
665679
type_id id = type_index(t);
666680
type_id value_id = value_type_id((value)args[args_count]);
667681

@@ -687,13 +701,22 @@ function_return function_c_interface_invoke(function func, function_impl impl, f
687701

688702
closures.push_back(closure);
689703
}
690-
else if (id == TYPE_STRING)
704+
else if (id == TYPE_STRING || (id == TYPE_PTR && impl_type != nullptr))
691705
{
692-
/* String requires to be pointer to a string */
706+
/* String requires to be pointer to a string and
707+
Pointer requires to be pointer to pointer */
693708
c_function->values[args_count] = value_create_ptr((value)args[args_count]);
694709
}
695710
else
696711
{
712+
/* In case of (id == TYPE_PTR && impl_type == nullptr it means that
713+
the parameter is normally int*, long*, etc... we just let the pointer
714+
be passed as it is because it cannot output a pointer, only modify the contents
715+
*/
716+
717+
/* TODO: Check if types are not equal and do a casting?
718+
Note: It will involve destroying the new casted values */
719+
697720
c_function->values[args_count] = value_data((value)args[args_count]);
698721
}
699722
}
@@ -741,9 +764,27 @@ function_return function_c_interface_invoke(function func, function_impl impl, f
741764
for (size_t args_count = 0; args_count < args_size; ++args_count)
742765
{
743766
type t = signature_get_type(s, args_count);
767+
type_impl impl_type = type_derived(t);
744768
type_id id = type_index(t);
745769

746-
if (id == TYPE_STRING)
770+
/* This is very tricky, if the type was a pointer to pointer, if it
771+
replaced the target pointer we have to update it in the MetaCall value,
772+
for example:
773+
void f(char **str_ptr)
774+
{
775+
*str_ptr = "aaa";
776+
}
777+
In order to make it work we have to recreate the metacall value, this is
778+
highly unsafe if we mix types because we will use the type info of the
779+
underlaying type in order to recreate it, in this example, a string
780+
*/
781+
if (id == TYPE_PTR && impl_type != nullptr)
782+
{
783+
// TODO: Reconstruct the pointer value from the type info
784+
785+
value_type_destroy(c_function->values[args_count]);
786+
}
787+
else if (id == TYPE_STRING)
747788
{
748789
/* Clear the pointer to string allocated before */
749790
value_type_destroy(c_function->values[args_count]);
@@ -978,6 +1019,13 @@ static type_id c_loader_impl_clang_type(loader_impl impl, CXCursor cursor, CXTyp
9781019
{
9791020
return c_loader_impl_clang_type(impl, cursor, pointee_type, impl_type);
9801021
}
1022+
/* Check for pointers to pointers, in this case we need the type info for reconstructing the data */
1023+
else if (pointee_type.kind == CXType_Pointer)
1024+
{
1025+
c_loader_pointer_type *pointer_type = new c_loader_pointer_type(impl, cx_type);
1026+
1027+
*impl_type = static_cast<c_loader_pointer_type *>(pointer_type);
1028+
}
9811029

9821030
return TYPE_PTR;
9831031
}

source/tests/metacall_node_port_c_test/source/metacall_node_port_c_test.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ TEST_F(metacall_node_port_c_test, DefaultConstructor)
3838
static const char buffer[] =
3939
"const assert = require('assert');\n"
4040
"const { metacall_load_from_file_export, metacall_value_create_ptr, metacall_value_reference, metacall_value_dereference } = require('" METACALL_NODE_PORT_PATH "');\n"
41-
"const { return_text, process_text, modify_int_ptr, compare_data_value, alloc_data, alloc_data_args, set_data_value, get_data_value, free_data } = metacall_load_from_file_export('c', ['compiled.c']);\n"
41+
"const { return_text, process_text, modify_double_ptr, modify_str_ptr, compare_data_value, alloc_data, alloc_data_args, set_data_value, get_data_value, free_data } = metacall_load_from_file_export('c', ['compiled.c']);\n"
4242
// Test strings
4343
"const result = return_text();\n"
4444
"console.log(`'${result}'`);\n"
@@ -52,11 +52,19 @@ TEST_F(metacall_node_port_c_test, DefaultConstructor)
5252
"assert(get_data_value(data_ptr) == 12);\n"
5353
"free_data(data_ptr);\n"
5454
// Test passing reference by arguments
55-
"int_val = 324444;\n"
56-
"int_val_ref = metacall_value_reference(int_val);\n"
57-
"modify_int_ptr(int_val_ref);\n"
58-
"int_val_deref = metacall_value_dereference(int_val_ref);\n"
59-
"assert(int_val_deref == 111);\n"
55+
"double_val = 324444.0;\n"
56+
"double_val_ref = metacall_value_reference(double_val);\n"
57+
"modify_double_ptr(double_val_ref);\n"
58+
"double_val_deref = metacall_value_dereference(double_val_ref);\n"
59+
"assert(double_val_deref == 111.0);\n"
60+
// Test passing reference by arguments string
61+
"str_val = 'asd';\n"
62+
"str_val_ref = metacall_value_reference(str_val);\n"
63+
"console.log(str_val);\n"
64+
"console.log(str_val_ref);\n"
65+
"modify_str_ptr(str_val_ref);\n"
66+
"str_val_deref = metacall_value_dereference(str_val_ref);\n"
67+
"assert(str_val_deref == 'yeet');\n"
6068
// Test passing reference of structs by arguments (with no args on create ptr)
6169
"data_ptr = metacall_value_create_ptr();\n"
6270
"data_ptr_ref = metacall_value_reference(data_ptr);\n"

source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ TEST_F(metacall_python_port_pointer_test, DefaultConstructor)
6060
static const char buffer[] =
6161
"import sys\n"
6262
"sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n"
63-
"from metacall import metacall_load_from_package, metacall, metacall_value_create_ptr, metacall_value_reference, metacall_value_dereference\n"
63+
"from metacall import metacall_load_from_package, metacall_load_from_file, metacall, metacall_value_create_ptr, metacall_value_reference, metacall_value_dereference\n"
6464
"metacall_load_from_package('c', 'loadtest')\n"
65+
"metacall_load_from_file('c', ['compiled.c'])\n"
6566

6667
"def test_int_ptr() -> int:\n"
6768
" print('Test start')\n"
@@ -81,6 +82,24 @@ TEST_F(metacall_python_port_pointer_test, DefaultConstructor)
8182

8283
" return int_val_deref\n"
8384

85+
"def test_str_ptr() -> str:\n"
86+
" print('Test start')\n"
87+
" sys.stdout.flush()\n"
88+
89+
" str_val = 'asd'\n"
90+
" str_val_ref = metacall_value_reference(str_val)\n"
91+
92+
" print(str_val_ref)\n"
93+
" sys.stdout.flush()\n"
94+
95+
" metacall('modify_str_ptr', str_val_ref)\n"
96+
" str_val_deref = metacall_value_dereference(str_val_ref)\n"
97+
98+
" print(str_val, '!=', str_val_deref)\n"
99+
" sys.stdout.flush()\n"
100+
101+
" return str_val_deref\n"
102+
84103
"def test_struct_ptr() -> float:\n"
85104
" print('Test start')\n"
86105
" sys.stdout.flush()\n"
@@ -110,6 +129,14 @@ TEST_F(metacall_python_port_pointer_test, DefaultConstructor)
110129

111130
metacall_value_destroy(ret);
112131

132+
ret = metacall("test_str_ptr");
133+
134+
ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret));
135+
136+
EXPECT_STREQ("yeet", metacall_value_to_string(ret));
137+
138+
metacall_value_destroy(ret);
139+
113140
ret = metacall("test_struct_ptr");
114141

115142
ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret));

0 commit comments

Comments
 (0)