Skip to content

Commit 703d336

Browse files
committed
Add support for array by value.
1 parent 10df5e1 commit 703d336

File tree

3 files changed

+222
-5
lines changed

3 files changed

+222
-5
lines changed

source/loaders/c_loader/source/c_loader_impl.cpp

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,95 @@ class c_loader_closure_value
517517
}
518518
};
519519

520+
static type_id c_loader_impl_clang_type(loader_impl impl, CXCursor cursor, CXType cx_type, c_loader_type_impl **impl_type);
521+
522+
class c_loader_array_type : public c_loader_type_impl
523+
{
524+
public:
525+
std::optional<long long> size;
526+
type_id id;
527+
528+
c_loader_array_type(loader_impl impl, CXCursor cursor, CXType cx_type, c_loader_type_impl **impl_type)
529+
{
530+
CXType element_cx_type = clang_getArrayElementType(cx_type);
531+
532+
id = c_loader_impl_clang_type(impl, cursor, element_cx_type, impl_type);
533+
534+
if (cx_type.kind == CXType_ConstantArray)
535+
{
536+
size = clang_getArraySize(cx_type);
537+
538+
if (size < 0)
539+
{
540+
size = 0;
541+
}
542+
}
543+
}
544+
545+
~c_loader_array_type() {}
546+
547+
void *generate_c_array(void *array, size_t args_count, function func, void **error)
548+
{
549+
size_t count = metacall_value_count(array);
550+
551+
/* Check if array size is correct */
552+
if (size.has_value())
553+
{
554+
if (count != static_cast<size_t>(*size))
555+
{
556+
*error = metacall_error_throw("C Loader Error", 0, "", "Argument %" PRIuS " of type array with different size when calling %s (expecting an array of size %d, received an array of size %" PRIuS ")", args_count, function_name(func), *size, count);
557+
return NULL;
558+
}
559+
}
560+
561+
/* Check if array type is correct */
562+
void **array_ptr = metacall_value_to_array(array);
563+
564+
for (size_t it = 0; it < count; ++it)
565+
{
566+
if (metacall_value_id(array_ptr[it]) != id)
567+
{
568+
*error = metacall_error_throw("C Loader Error", 0, "", "Argument %" PRIuS " of type array with different type when calling %s (expecting an array of type %s, received an array of type %s in the element %" PRIuS ")", args_count, function_name(func), type_id_name(id), metacall_value_type_name(array_ptr[it]), it);
569+
return NULL;
570+
}
571+
}
572+
573+
/* Allocate temporal memory */
574+
size_t type_size = value_type_id_size(id);
575+
void **memory_ptr = static_cast<void **>(malloc(sizeof(void **)));
576+
577+
if (memory_ptr == NULL)
578+
{
579+
*error = metacall_error_throw("C Loader Error", 0, "", "Argument %" PRIuS " failed to allocate memory pointer for the array when calling %s", args_count, function_name(func));
580+
return NULL;
581+
}
582+
583+
void *memory = malloc(count * type_size);
584+
585+
if (memory == NULL)
586+
{
587+
*error = metacall_error_throw("C Loader Error", 0, "", "Argument %" PRIuS " failed to allocate memory for the array when calling %s", args_count, function_name(func));
588+
free(memory_ptr);
589+
return NULL;
590+
}
591+
592+
for (size_t it = 0; it < count; ++it)
593+
{
594+
std::memcpy(&((unsigned char *)memory)[it * type_size], array_ptr[it], type_size);
595+
}
596+
597+
*memory_ptr = memory;
598+
599+
return memory_ptr;
600+
}
601+
602+
static void free_c_array(void **memory_ptr)
603+
{
604+
free(*memory_ptr);
605+
free(memory_ptr);
606+
}
607+
};
608+
520609
std::string c_loader_impl_cxstring_to_str(const CXString &s)
521610
{
522611
std::string result = clang_getCString(s);
@@ -690,11 +779,10 @@ function_return function_c_interface_invoke(function func, function_impl impl, f
690779

691780
closures.push_back(closure);
692781
}
693-
else if (id == TYPE_STRING || id == TYPE_BUFFER || id == TYPE_ARRAY || id == TYPE_PTR)
782+
else if (id == TYPE_STRING || id == TYPE_BUFFER || id == TYPE_PTR)
694783
{
695784
/*
696785
String, buffer requires to be pointer to a string
697-
Array requires to be pointer to a array
698786
Pointer requires to be pointer to pointer
699787
*/
700788

@@ -703,6 +791,19 @@ function_return function_c_interface_invoke(function func, function_impl impl, f
703791

704792
c_function->values[args_count] = (void *)&args[args_count];
705793
}
794+
else if (id == TYPE_ARRAY)
795+
{
796+
c_loader_array_type *array = static_cast<c_loader_array_type *>(type_derived(t));
797+
void *error = NULL;
798+
void *array_ptr = array->generate_c_array(args[args_count], args_count, func, &error);
799+
800+
if (error != NULL)
801+
{
802+
return error;
803+
}
804+
805+
c_function->values[args_count] = array_ptr;
806+
}
706807
else if (type_id_integer(id) == 0 || type_id_decimal(id) == 0)
707808
{
708809
/* Primitive types already have the pointer indirection */
@@ -778,6 +879,17 @@ function_return function_c_interface_invoke(function func, function_impl impl, f
778879
}
779880
}
780881

882+
for (size_t args_count = 0; args_count < args_size; ++args_count)
883+
{
884+
type t = signature_get_type(s, args_count);
885+
type_id id = type_index(t);
886+
887+
if (id == TYPE_ARRAY)
888+
{
889+
c_loader_array_type::free_c_array(static_cast<void **>(c_function->values[args_count]));
890+
}
891+
}
892+
781893
/* Clear allocated closures if any */
782894
for (c_loader_closure_value *closure : closures)
783895
{
@@ -984,7 +1096,7 @@ int c_loader_impl_execution_path(loader_impl impl, const loader_path path)
9841096
return 0;
9851097
}
9861098

987-
static type_id c_loader_impl_clang_type(loader_impl impl, CXCursor cursor, CXType cx_type, c_loader_type_impl **impl_type)
1099+
type_id c_loader_impl_clang_type(loader_impl impl, CXCursor cursor, CXType cx_type, c_loader_type_impl **impl_type)
9881100
{
9891101
switch (cx_type.kind)
9901102
{
@@ -1011,8 +1123,18 @@ static type_id c_loader_impl_clang_type(loader_impl impl, CXCursor cursor, CXTyp
10111123
}
10121124

10131125
case CXType_ConstantArray:
1014-
case CXType_IncompleteArray:
1015-
return TYPE_ARRAY;
1126+
case CXType_IncompleteArray: {
1127+
c_loader_array_type *array_type = new c_loader_array_type(impl, cursor, cx_type, impl_type);
1128+
1129+
if (array_type != nullptr)
1130+
{
1131+
*impl_type = static_cast<c_loader_array_type *>(array_type);
1132+
1133+
return TYPE_ARRAY;
1134+
}
1135+
1136+
return TYPE_INVALID;
1137+
}
10161138

10171139
case CXType_FunctionProto:
10181140
case CXType_FunctionNoProto: {

source/scripts/c/compiled/source/compiled.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,37 @@ void free_data(data_ptr_t ptr)
7777
free(ptr);
7878
}
7979

80+
/* https://github.com/metacall/core/issues/570 */
81+
void apply_blur_filter(int pixels[], int width, int height)
82+
{
83+
int size = width * height;
84+
85+
printf("pixels == %p\n", pixels);
86+
fflush(stdout);
87+
88+
for (int i = 0; i < size; i++)
89+
{
90+
printf("pixels[%d] == %d\n", pixels[i], i);
91+
fflush(stdout);
92+
assert(pixels[i] == i);
93+
pixels[i] = pixels[i] / 2;
94+
}
95+
printf("C: Blur filter applied on %d pixels\n", size);
96+
}
97+
98+
double calculate_brightness(int pixels[], int size)
99+
{
100+
long sum = 0;
101+
for (int i = 0; i < size; i++)
102+
{
103+
assert(pixels[i] == i);
104+
sum += pixels[i];
105+
}
106+
double avg = (double)sum / (double)size;
107+
printf("C: Average brightness = %f\n", avg);
108+
return avg;
109+
}
110+
80111
// TODO: When calling from NodeJS it does not work,
81112
// NodeJS emmits double as a call, and this expects long, it needs a casting
82113
void modify_int_ptr(long *l)

source/tests/metacall_c_test/source/metacall_c_test.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,70 @@ TEST_F(metacall_c_test, DefaultConstructor)
6060

6161
metacall_value_destroy(ret);
6262

63+
/* https://github.com/metacall/core/issues/570 */
64+
{
65+
/* Call by array */
66+
{
67+
void *args[] = {
68+
metacall_value_create_array(NULL, 100),
69+
metacall_value_create_int(10),
70+
metacall_value_create_int(10)
71+
};
72+
73+
void **array_ptr = metacall_value_to_array(args[0]);
74+
75+
for (int i = 0; i < 100; ++i)
76+
{
77+
array_ptr[i] = metacall_value_create_int(i);
78+
}
79+
80+
std::cout << "value: " << args[0] << std::endl;
81+
std::cout << "array: " << array_ptr << std::endl;
82+
83+
ret = metacallv("apply_blur_filter", args);
84+
85+
EXPECT_NE((void *)NULL, (void *)ret);
86+
87+
EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL);
88+
89+
metacall_value_destroy(ret);
90+
91+
metacall_value_destroy(args[0]);
92+
metacall_value_destroy(args[1]);
93+
metacall_value_destroy(args[2]);
94+
}
95+
96+
/* Call by pointer */
97+
{
98+
int array[100];
99+
100+
void *args[] = {
101+
metacall_value_create_ptr(array),
102+
metacall_value_create_int(10),
103+
metacall_value_create_int(10)
104+
};
105+
106+
for (int i = 0; i < 100; ++i)
107+
{
108+
array[i] = i;
109+
}
110+
111+
ret = metacallv("apply_blur_filter", args);
112+
113+
EXPECT_NE((void *)NULL, (void *)ret);
114+
115+
EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL);
116+
117+
metacall_value_destroy(ret);
118+
119+
metacall_value_destroy(args[0]);
120+
metacall_value_destroy(args[1]);
121+
metacall_value_destroy(args[2]);
122+
}
123+
124+
// TODO: double calculate_brightness(int pixels[], int size)
125+
}
126+
63127
/* File with dependencies */
64128
const char *c_dep_scripts[] = {
65129
"ffi.c",

0 commit comments

Comments
 (0)