Skip to content

Commit

Permalink
Handle including <time.h> in --std=c99 before <ruby.h>
Browse files Browse the repository at this point in the history
* Add a cexts test for it.
* See socketry/io-event#88 for details.
  • Loading branch information
eregon committed Jan 23, 2024
1 parent 410e95a commit 9d9e9f4
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 20 deletions.
16 changes: 0 additions & 16 deletions lib/cext/include/ruby/internal/intern/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,7 @@ void rb_tr_time_timeval(VALUE time, struct timeval *result);
* @exception rb_eRangeError `time` is out of range of `timeval::tv_sec`.
* @return A struct that represents the identical time to `num`.
*/
#ifdef TRUFFLERUBY
static inline struct timeval rb_time_timeval(VALUE time) {
struct timeval result;
rb_tr_time_timeval(time, &result);
return result;
}
#else
struct timeval rb_time_timeval(VALUE time);
#endif

#ifdef TRUFFLERUBY
void rb_tr_time_timespec(VALUE time, struct timespec *result);
Expand All @@ -163,15 +155,7 @@ void rb_tr_time_timespec(VALUE time, struct timespec *result);
* @exception rb_eRangeError `time` is out of range of `timeval::tv_sec`.
* @return A struct that represents the identical time to `num`.
*/
#ifdef TRUFFLERUBY
static inline struct timespec rb_time_timespec(VALUE time) {
struct timespec result;
rb_tr_time_timespec(time, &result);
return result;
}
#else
struct timespec rb_time_timespec(VALUE time);
#endif

/**
* Identical to rb_time_interval(), except for return type.
Expand Down
2 changes: 1 addition & 1 deletion lib/cext/include/truffleruby/truffleruby-abi-version.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
// $RUBY_VERSION must be the same as TruffleRuby.LANGUAGE_VERSION.
// $ABI_NUMBER starts at 1 and is incremented for every ABI-incompatible change.

#define TRUFFLERUBY_ABI_VERSION "3.2.2.10"
#define TRUFFLERUBY_ABI_VERSION "3.2.2.11"

#endif
14 changes: 14 additions & 0 deletions src/main/c/cext/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ void rb_tr_time_timespec(VALUE time_val, struct timespec *result) {
result->tv_nsec = polyglot_as_i64(RUBY_INVOKE_NO_WRAP(time, "tv_nsec"));
}

// Only used with --cexts-sulong
struct timeval rb_time_timeval(VALUE time) {
struct timeval result;
rb_tr_time_timeval(time, &result);
return result;
}

// Only used with --cexts-sulong
struct timespec rb_time_timespec(VALUE time) {
struct timespec result;
rb_tr_time_timespec(time, &result);
return result;
}

VALUE rb_time_timespec_new(const struct timespec *ts, int offset) {
void* is_utc = rb_tr_unwrap(rb_boolean(offset == INT_MAX-1));
void* is_local = rb_tr_unwrap(rb_boolean(offset == INT_MAX));
Expand Down
3 changes: 3 additions & 0 deletions test/truffle/cexts/no_timespec/bin/no_timespec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env ruby

require 'no_timespec/no_timespec'
1 change: 1 addition & 0 deletions test/truffle/cexts/no_timespec/expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello!
3 changes: 3 additions & 0 deletions test/truffle/cexts/no_timespec/ext/no_timespec/extconf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require 'mkmf'
$CFLAGS += ' --std=c99'
create_makefile('no_timespec')
9 changes: 9 additions & 0 deletions test/truffle/cexts/no_timespec/ext/no_timespec/no_timespec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Include time.h before ruby.h, this has the effect together with --std=c99 to not define struct timespec
#include <time.h>
#include <ruby.h>

void Init_no_timespec() {
// This would fail but what we want to test is that including ruby.h works in this situation
// struct timespec t;
printf("Hello!\n");
}
Empty file.
29 changes: 28 additions & 1 deletion tool/generate-cext-trampoline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
rb_tr_not_implemented
]

RETURN_STRUCT_BY_VALUE = {
'rb_time_timeval' => 'rb_tr_time_timeval',
'rb_time_timespec' => 'rb_tr_time_timespec',
}

type_regexp = /\b(?:(?:const|unsigned|volatile|struct|enum)\s+)*\w+\b(?:\s+const)?(?:\s*\*+\s*)?/
function_pointer_arg_regexp = /\b\w+\s*\(\*(\w+)\)\s*\([^)]*?\)/
argument_regexp = /\bvoid\b|\.\.\.|#{type_regexp}\s*(\w+)(?:\[\d*\])?|#{function_pointer_arg_regexp}/
Expand Down Expand Up @@ -65,7 +70,7 @@ def struct_by_value?(type)
if declaration.include? "\n"
abort "This declaration includes newlines but should not:\n#{declaration}\n\n"
end
if struct_by_value? return_type
if struct_by_value?(return_type) and !RETURN_STRUCT_BY_VALUE.include?(function_name)
abort "Returning a struct by value from Sulong to NFI is not supported for:\n#{declaration}"
end
end
Expand Down Expand Up @@ -183,12 +188,15 @@ def struct_by_value?(type)

f.puts "\n// Functions\n\n"
functions.each do |declaration, return_type, function_name, argument_types|
next if RETURN_STRUCT_BY_VALUE.include?(function_name)
f.puts "#undef #{function_name}"
f.puts "static #{declaration.sub(/\{$/, '').rstrip.sub(function_name, "(*impl_#{function_name})")};"
end
f.puts

functions.each do |declaration, return_type, function_name, argument_types|
next if RETURN_STRUCT_BY_VALUE.include?(function_name)

argument_names = argument_types.delete_prefix('(').delete_suffix(')')
argument_names = argument_names.scan(/(?:^|,)\s*(#{argument_regexp})\s*(?=,|$)/o)
argument_names = argument_names.map { |full_arg, name1, name2|
Expand Down Expand Up @@ -237,13 +245,32 @@ def struct_by_value?(type)
f.puts
end

f.puts <<C
// Return struct-by-value functions
struct timeval rb_time_timeval(VALUE time) {
struct timeval result;
rb_tr_time_timeval(time, &result);
return result;
}
struct timespec rb_time_timespec(VALUE time) {
struct timespec result;
rb_tr_time_timespec(time, &result);
return result;
}
C

f.puts <<C
// Init functions
void rb_tr_trampoline_init_functions(TruffleEnv* env, void* (*get_libtruffleruby_function)(const char*)) {
nfiContext = (*env)->getTruffleContext(env);
C
functions.each do |declaration, return_type, function_name, argument_types|
next if RETURN_STRUCT_BY_VALUE.include?(function_name)
f.puts " impl_#{function_name} = get_libtruffleruby_function(\"#{function_name}\");"
end
f.puts "}"
Expand Down
4 changes: 2 additions & 2 deletions tool/jt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1415,7 +1415,7 @@ def retag(*args)

ALL_CEXTS_TESTS = %w[
tools postinstallhook
minimum module method globals backtraces xopenssl werror stripped
minimum module method globals backtraces xopenssl werror no_timespec stripped
oily_png psd_native
puma sqlite3 unf_ext json grpc RubyInline msgpack
]
Expand All @@ -1442,7 +1442,7 @@ def retag(*args)
# Test tools
run_ruby 'test/truffle/cexts/test_preprocess.rb'

when 'minimum', 'module', 'method', 'globals', 'backtraces', 'xopenssl', 'werror', 'stripped'
when 'minimum', 'module', 'method', 'globals', 'backtraces', 'xopenssl', 'werror', 'no_timespec', 'stripped'
# Test that we can compile and run some very basic C extensions
output_file = 'cext-output.txt'
dir = "test/truffle/cexts/#{test_name}"
Expand Down

0 comments on commit 9d9e9f4

Please sign in to comment.