Skip to content

Commit

Permalink
Fix FFI specs on release builds (crystal-lang#12601)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil authored and lbguilherme committed Oct 24, 2022
1 parent 83875d1 commit 3536605
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 55 deletions.
53 changes: 29 additions & 24 deletions spec/compiler/data/ffi/sum.c
Original file line number Diff line number Diff line change
@@ -1,71 +1,76 @@
#include <stdarg.h>
#include <stdint.h>
#include "../visibility.h"

EXPORT int answer()
// all the integral return types must be at least as large as the register size
// to avoid integer promotion by FFI!

EXPORT int64_t answer()
{
return 42;
}

EXPORT int sum(int a, int b, int c)
EXPORT int64_t sum(int32_t a, int32_t b, int32_t c)
{
return a + b + c;
}

EXPORT void sum_primitive_types(
unsigned char a, signed char b,
unsigned short c, signed short d,
unsigned long e, signed long f,
unsigned long long g, signed long long h,
uint8_t a, int8_t b,
uint16_t c, int16_t d,
uint32_t e, int32_t f,
uint64_t g, int64_t h,
float i, double j,
long *k)
int64_t *k)
{
*k = a + b + c + d + e + f + g + h + (long)i + (long)j + *k;
*k = a + b + c + d + e + f + g + h + (int64_t)i + (int64_t)j + *k;
}

struct test_struct
{
char b;
short s;
int i;
long long j;
int8_t b;
int16_t s;
int32_t i;
int64_t j;
float f;
double d;
int *p;
void *p;
};

EXPORT int sum_struct(struct test_struct s)
EXPORT int64_t sum_struct(struct test_struct s)
{
*s.p = s.b + s.s + s.i + s.j + s.f + s.d + *(s.p);
return *s.p;
int64_t *p = (int64_t *)s.p;
*p = s.b + s.s + s.i + s.j + s.f + s.d + *p;
return *p;
}

EXPORT int sum_array(int ary[4])
EXPORT int64_t sum_array(int32_t ary[4])
{
int sum = 0;
for (int i = 0; i < 4; i++)
int64_t sum = 0;
for (int32_t i = 0; i < 4; i++)
{
sum += ary[i];
}
return sum;
}

EXPORT int sum_variadic(int count, ...)
EXPORT int64_t sum_variadic(int32_t count, ...)
{
va_list ap;
int j;
int sum = 0;
int32_t j;
int64_t sum = 0;

va_start(ap, count); /* Requires the last fixed parameter (to get the address) */
for (j = 0; j < count; j++)
{
sum += va_arg(ap, int); /* Increments ap to the next argument. */
sum += va_arg(ap, int32_t); /* Increments ap to the next argument. */
}
va_end(ap);

return sum;
}

EXPORT struct test_struct make_struct(char b, short s, int i, long long j, float f, double d, void *p)
EXPORT struct test_struct make_struct(int8_t b, int16_t s, int32_t i, int64_t j, float f, double d, void *p)
{
struct test_struct t;
t.b = b;
Expand Down
65 changes: 34 additions & 31 deletions spec/compiler/ffi/ffi_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ require "compiler/crystal/ffi"
require "compiler/crystal/loader"
require "../loader/spec_helper"

# all the integral return types must be at least as large as the register size
# to avoid integer promotion by FFI!

@[Extern]
private record TestStruct,
b : LibC::Char,
s : LibC::Short,
i : LibC::Int,
j : LibC::LongLong,
f : LibC::Float,
d : LibC::Double,
b : Int8,
s : Int16,
i : Int32,
j : Int64,
f : Float32,
d : Float64,
p : Pointer(Void)

describe Crystal::FFI::CallInterface do
Expand All @@ -28,32 +31,32 @@ describe Crystal::FFI::CallInterface do

describe ".new" do
it "simple call" do
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint16, [] of Crystal::FFI::Type
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [] of Crystal::FFI::Type

loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH])
loader.load_library "sum"
function_pointer = loader.find_symbol("answer")
return_value = 0_i32
return_value = 0_i64
call_interface.call(function_pointer, Pointer(Pointer(Void)).null, pointerof(return_value).as(Void*))
return_value.should eq 42
return_value.should eq 42_i64
ensure
loader.try &.close_all
end

it "with args" do
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint16, [
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [
Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32,
] of Crystal::FFI::Type

loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH])
loader.load_library "sum"
function_pointer = loader.find_symbol("sum")

return_value = 0_i32
return_value = 0_i64
args = Int32[1, 3, 5]
arg_pointers = StaticArray(Pointer(Void), 3).new { |i| (args.to_unsafe + i).as(Void*) }
call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))
return_value.should eq 9
return_value.should eq 9_i64
ensure
loader.try &.close_all
end
Expand All @@ -72,7 +75,7 @@ describe Crystal::FFI::CallInterface do
loader.load_library "sum"
function_pointer = loader.find_symbol("sum_primitive_types")

pointer_value = 11_i32
pointer_value = 11_i64
arg_pointers = StaticArray[
Pointer(UInt8).malloc(1, 1).as(Void*),
Pointer(Int8).malloc(1, 2).as(Void*),
Expand All @@ -84,7 +87,7 @@ describe Crystal::FFI::CallInterface do
Pointer(Int64).malloc(1, 8).as(Void*),
Pointer(Float32).malloc(1, 9.0).as(Void*),
Pointer(Float64).malloc(1, 10.0).as(Void*),
Pointer(Int32*).malloc(1, pointerof(pointer_value)).as(Void*),
Pointer(Int64*).malloc(1, pointerof(pointer_value)).as(Void*),
Pointer(Void).null,
]

Expand Down Expand Up @@ -130,7 +133,7 @@ describe Crystal::FFI::CallInterface do
end

it "sum struct" do
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint32, [
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [
Crystal::FFI::Type.struct([
Crystal::FFI::Type.sint8,
Crystal::FFI::Type.sint16,
Expand All @@ -146,7 +149,7 @@ describe Crystal::FFI::CallInterface do
loader.load_library "sum"
function_pointer = loader.find_symbol("sum_struct")

pointer_value = 11_i32
pointer_value = 11_i64
arg_pointers = StaticArray[
Pointer(TestStruct).malloc(1, TestStruct.new(
b: 2,
Expand All @@ -160,18 +163,18 @@ describe Crystal::FFI::CallInterface do
Pointer(Void).null,
]

return_value = 0i32
return_value = 0_i64
call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))
return_value.should eq 50
pointer_value.should eq 50
return_value.should eq 50_i64
pointer_value.should eq 50_i64
ensure
loader.try &.close_all
end

# passing C array by value is not supported everywhere
{% unless flag?(:win32) %}
it "array" do
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint32, [
call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [
Crystal::FFI::Type.struct([
Crystal::FFI::Type.sint32,
Crystal::FFI::Type.sint32,
Expand All @@ -184,7 +187,7 @@ describe Crystal::FFI::CallInterface do
loader.load_library "sum"
function_pointer = loader.find_symbol("sum_array")

return_value = 0_i32
return_value = 0_i64

ary = [1, 2, 3, 4]

Expand All @@ -194,7 +197,7 @@ describe Crystal::FFI::CallInterface do
]

call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))
return_value.should eq 10
return_value.should eq 10_i64
ensure
loader.try &.close_all
end
Expand All @@ -203,43 +206,43 @@ describe Crystal::FFI::CallInterface do

describe ".variadic" do
it "basic" do
call_interface = Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint16, [Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32] of Crystal::FFI::Type, 1
call_interface = Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32] of Crystal::FFI::Type, 1

loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH])
loader.load_library "sum"
function_pointer = loader.find_symbol("sum_variadic")

return_value = 0_i32
return_value = 0_i64
args = Int32[3, 1, 3, 5]
arg_pointers = StaticArray(Pointer(Void), 4).new { |i| (args.to_unsafe + i).as(Void*) }
call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))
return_value.should eq 9
return_value.should eq 9_i64
ensure
loader.try &.close_all
end

it "zero varargs" do
call_interface = Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint16, [Crystal::FFI::Type.sint32] of Crystal::FFI::Type, 1
call_interface = Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [Crystal::FFI::Type.sint32] of Crystal::FFI::Type, 1

loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH])
loader.load_library "sum"
function_pointer = loader.find_symbol("sum_variadic")

return_value = 1_i32
count = 0
return_value = 1_i64
count = 0_i32
arg_pointer = pointerof(count).as(Void*)
call_interface.call(function_pointer, pointerof(arg_pointer), pointerof(return_value).as(Void*))
return_value.should eq 0
return_value.should eq 0_i64
ensure
loader.try &.close_all
end

it "validates args size" do
expect_raises Exception, "invalid value for fixed_args" do
Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint32, [] of Crystal::FFI::Type, 1
Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [] of Crystal::FFI::Type, 1
end
expect_raises Exception, "invalid value for fixed_args" do
Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint32, [] of Crystal::FFI::Type, -1
Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [] of Crystal::FFI::Type, -1
end
end
end
Expand Down

0 comments on commit 3536605

Please sign in to comment.