Skip to content

Commit ab4f2f8

Browse files
committed
Use value wrappers for c api
PullRequest: truffleruby/515
2 parents 99fc91f + 50144bf commit ab4f2f8

File tree

14 files changed

+726
-579
lines changed

14 files changed

+726
-579
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Bug fixes:
1919

2020
Compatibility:
2121

22+
* Change to a new system for handling Ruby objects in C extensions which
23+
greatly increases compatibility with MRI.
2224
* Implemented `Dir.each_child`.
2325
* Adding missing support for the `close_others` option to `exec` and `spawn`.
2426
* Implemented the missing `MatchData#named_captures` method (#1512).

lib/cext/include/ruby/io.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ POLYGLOT_DECLARE_STRUCT(rb_io_t)
8686
/* #define FMODE_INET 0x00400000 */
8787
/* #define FMODE_INET6 0x00800000 */
8888

89-
#define GetOpenFile(file, pointer) rb_io_check_closed((pointer) = polyglot_as_rb_io_t(polyglot_invoke(RUBY_CEXT, "GetOpenFile", file)))
89+
#define GetOpenFile(file, pointer) rb_io_check_closed((pointer) = polyglot_as_rb_io_t(RUBY_CEXT_INVOKE_NO_WRAP("GetOpenFile", file)))
9090

9191

9292
#define MakeOpenFile(obj, fp) do {\

lib/cext/include/ruby/ruby.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ void *alloca();
7878
# endif /* HAVE_ALLOCA_H */
7979
#endif /* __GNUC__ */
8080

81-
typedef void *VALUE;
82-
typedef long SIGNED_VALUE;
83-
typedef VALUE ID;
84-
85-
8681
typedef char ruby_check_sizeof_int[SIZEOF_INT == sizeof(int) ? 1 : -1];
8782
typedef char ruby_check_sizeof_long[SIZEOF_LONG == sizeof(long) ? 1 : -1];
8883
#ifdef HAVE_LONG_LONG
@@ -310,7 +305,7 @@ int rb_long2int(long value);
310305
#define MODET2NUM(v) INT2NUM(v)
311306
#endif
312307

313-
#define RB_FIX2LONG(x) ((long)(x))
308+
#define RB_FIX2LONG(x) ((long)(rb_tr_unwrap(x)))
314309
static inline long
315310
rb_fix2long(VALUE x)
316311
{
@@ -339,7 +334,7 @@ int RB_FIXNUM_P(VALUE value);
339334
static inline VALUE
340335
rb_symbol_p_inline(VALUE v)
341336
{
342-
return polyglot_invoke(RUBY_CEXT, "SYMBOL_P", v);
337+
return rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "SYMBOL_P", rb_tr_unwrap(v)));
343338
}
344339

345340
ID rb_sym2id(VALUE);
@@ -998,7 +993,8 @@ VALUE RARRAY_AREF(VALUE array, long index);
998993
RARRAY_PTR_USE_END(_ary); \
999994
} while (0)
1000995

1001-
#define RARRAY_PTR(array) ((VALUE *)array)
996+
#define RARRAY_PTR(array) RARRAY_PTR_IMPL(array)
997+
VALUE *RARRAY_PTR_IMPL(VALUE array);
1002998

1003999
struct RRegexp {
10041000
struct RBasic basic;
@@ -1480,7 +1476,7 @@ rb_ulong2num_inline(unsigned long v)
14801476
if (RB_POSFIXABLE(v))
14811477
return RB_LONG2FIX(v);
14821478
else
1483-
return polyglot_invoke(RUBY_CEXT, "rb_ulong2num", (long) v);
1479+
return rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "rb_ulong2num", (long) v));
14841480
}
14851481
#define RB_ULONG2NUM(x) rb_ulong2num_inline(x)
14861482

lib/cext/include/truffleruby/truffleruby-pre.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ extern "C" {
2525

2626
#include <ctype.h> // isdigit
2727

28+
// Value types
29+
30+
typedef void *VALUE;
31+
typedef long SIGNED_VALUE;
32+
typedef VALUE ID;
33+
34+
// Wrapping and unwrapping of values.
35+
36+
VALUE rb_tr_wrap(VALUE);
37+
VALUE rb_tr_unwrap(VALUE);
38+
2839
// Support
2940

3041
extern void* rb_tr_cext;

lib/cext/include/truffleruby/truffleruby.h

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extern "C" {
1919
#endif
2020

2121
#define rb_sprintf(format, ...) \
22-
(VALUE) polyglot_invoke(RUBY_CEXT, "rb_sprintf", rb_str_new_cstr(format), ##__VA_ARGS__)
22+
rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "rb_sprintf", rb_tr_unwrap(rb_str_new_cstr(format)), ##__VA_ARGS__))
2323

2424
NORETURN(VALUE rb_f_notimplement(int args_count, const VALUE *args, VALUE object));
2525

@@ -90,22 +90,17 @@ int rb_tr_writable(int mode);
9090

9191
typedef void *(*gvl_call)(void *);
9292

93-
// Exceptions
94-
95-
#define rb_raise(EXCEPTION, FORMAT, ...) \
96-
rb_exc_raise(rb_exc_new_str(EXCEPTION, (VALUE) polyglot_invoke(RUBY_CEXT, "rb_sprintf", rb_str_new_cstr(FORMAT), ##__VA_ARGS__)))
97-
9893
// Utilities
9994

10095
#define rb_warn(FORMAT, ...) do { \
10196
if (polyglot_as_boolean(polyglot_invoke(RUBY_CEXT, "warn?"))) { \
102-
polyglot_invoke(rb_mKernel, "warn", (VALUE) polyglot_invoke(rb_mKernel, "sprintf", rb_str_new_cstr(FORMAT), ##__VA_ARGS__)); \
97+
polyglot_invoke(rb_tr_unwrap(rb_mKernel), "warn", (VALUE) polyglot_invoke(rb_tr_unwrap(rb_mKernel), "sprintf", rb_tr_unwrap(rb_str_new_cstr(FORMAT)), ##__VA_ARGS__)); \
10398
} \
10499
} while (0);
105100

106101
#define rb_warning(FORMAT, ...) do { \
107102
if (polyglot_as_boolean(polyglot_invoke(RUBY_CEXT, "warning?"))) { \
108-
polyglot_invoke(rb_mKernel, "warn", (VALUE) polyglot_invoke(rb_mKernel, "sprintf", rb_str_new_cstr(FORMAT), ##__VA_ARGS__)); \
103+
polyglot_invoke(rb_tr_unwrap(rb_mKernel), "warn", (VALUE) polyglot_invoke(rb_tr_unwrap(rb_mKernel), "sprintf", rb_tr_unwrap(rb_str_new_cstr(FORMAT)), ##__VA_ARGS__)); \
109104
} \
110105
} while (0);
111106

@@ -125,9 +120,51 @@ MUST_INLINE int rb_tr_scan_args(int argc, VALUE *argv, const char *format, VALUE
125120
#define SCAN_ARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NAME, ...) NAME
126121
#define rb_scan_args(ARGC, ARGV, FORMAT, ...) SCAN_ARGS_IMPL(__VA_ARGS__, rb_tr_scan_args_10, rb_tr_scan_args_9, rb_tr_scan_args_8, rb_tr_scan_args_7, rb_tr_scan_args_6, rb_tr_scan_args_5, rb_tr_scan_args_4, rb_tr_scan_args_3, rb_tr_scan_args_2, rb_tr_scan_args_1)(ARGC, ARGV, FORMAT, __VA_ARGS__)
127122

123+
// Invoking ruby methods.
124+
125+
// These macros implement ways to call the methods on Truffle::CExt
126+
// (RUBY_CEXT_INVOKE) and other ruby objects (RUBY_INVOKE) and handle
127+
// all the unwrapping of arguments. They also come in _NO_WRAP
128+
// variants which will not attempt to wrap the result. This is
129+
// important if it is not an actual ruby object being returned as an
130+
// error will be raised when attempting to wrap such objects.
131+
132+
// Internal macros for the implementation
133+
#define RUBY_INVOKE_IMPL_0(recv, name) polyglot_invoke(recv, name)
134+
#define RUBY_INVOKE_IMPL_1(recv, name, V1) polyglot_invoke(recv, name, rb_tr_unwrap(V1))
135+
#define RUBY_INVOKE_IMPL_2(recv, name, V1, V2) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2))
136+
#define RUBY_INVOKE_IMPL_3(recv, name, V1, V2, V3) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3))
137+
#define RUBY_INVOKE_IMPL_4(recv, name, V1, V2, V3, V4) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4))
138+
#define RUBY_INVOKE_IMPL_5(recv, name, V1, V2, V3, V4, V5) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4), rb_tr_unwrap(V5))
139+
#define RUBY_INVOKE_IMPL_6(recv, name, V1, V2, V3, V4, V5, V6) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4), rb_tr_unwrap(V5), rb_tr_unwrap(V6))
140+
#define RUBY_INVOKE_IMPL_7(recv, name, V1, V2, V3, V4, V5, V6, V7) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4), rb_tr_unwrap(V5), rb_tr_unwrap(V6), rb_tr_unwrap(V7))
141+
#define RUBY_INVOKE_IMPL_8(recv, name, V1, V2, V3, V4, V5, V6, V7, V8) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4), rb_tr_unwrap(V5), rb_tr_unwrap(V6), rb_tr_unwrap(V7), rb_tr_unwrap(V8))
142+
#define RUBY_INVOKE_IMPL_9(recv, name, V1, V2, V3, V4, V5, V6, V7, V8, V9) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4), rb_tr_unwrap(V5), rb_tr_unwrap(V6), rb_tr_unwrap(V7), rb_tr_unwrap(V8), rb_tr_unwrap(V9))
143+
#define RUBY_INVOKE_IMPL_10(recv, name, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10) polyglot_invoke(recv, name, rb_tr_unwrap(V1), rb_tr_unwrap(V2), rb_tr_unwrap(V3), rb_tr_unwrap(V4), rb_tr_unwrap(V5), rb_tr_unwrap(V6), rb_tr_unwrap(V7), rb_tr_unwrap(V8), rb_tr_unwrap(V9), rb_tr_unwrap(V10))
144+
#define INVOKE_IMPL(RECV, MESG, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NAME, ...) NAME
145+
#define RUBY_INVOKE_IMPL_NO_WRAP(RECV, NAME, ...) INVOKE_IMPL(RECV, NAME, ##__VA_ARGS__, RUBY_INVOKE_IMPL_10, RUBY_INVOKE_IMPL_9, RUBY_INVOKE_IMPL_8, RUBY_INVOKE_IMPL_7, RUBY_INVOKE_IMPL_6, RUBY_INVOKE_IMPL_5, RUBY_INVOKE_IMPL_4, RUBY_INVOKE_IMPL_3, RUBY_INVOKE_IMPL_2, RUBY_INVOKE_IMPL_1, RUBY_INVOKE_IMPL_0)(RECV, NAME, ##__VA_ARGS__)
146+
#define RUBY_INVOKE_IMPL(RECV, NAME, ...) rb_tr_wrap(RUBY_INVOKE_IMPL_NO_WRAP(RECV, NAME, ##__VA_ARGS__))
147+
148+
// Public macros used in this header and ruby.c
149+
#define RUBY_INVOKE(RECV, NAME, ...) RUBY_INVOKE_IMPL(rb_tr_unwrap(RECV), NAME, ##__VA_ARGS__)
150+
#define RUBY_INVOKE_NO_WRAP(RECV, NAME, ...) RUBY_INVOKE_IMPL_NO_WRAP(rb_tr_unwrap(RECV), NAME, ##__VA_ARGS__)
151+
152+
#define RUBY_CEXT_INVOKE(NAME, ...) RUBY_INVOKE_IMPL(RUBY_CEXT, NAME, ##__VA_ARGS__)
153+
#define RUBY_CEXT_INVOKE_NO_WRAP(NAME, ...) RUBY_INVOKE_IMPL_NO_WRAP(RUBY_CEXT, NAME, ##__VA_ARGS__)
154+
128155
// Calls
129156

130-
#define rb_funcall(object, ...) polyglot_invoke(RUBY_CEXT, "rb_funcall", (void *) object, __VA_ARGS__)
157+
// We use this pair of macros because ##__VA_ARGS__ args will not
158+
// have macro substitution done on them at the right point in
159+
// preprocessing and will prevent rb_funcall(..., rb_funcall(...))
160+
// from being expanded correctly.
161+
#define rb_tr_funcall(object, method, n,...) RUBY_CEXT_INVOKE("rb_funcall", object, method, INT2FIX(n), ##__VA_ARGS__)
162+
#define rb_funcall(object, method, ...) rb_tr_funcall(object, method, __VA_ARGS__)
163+
164+
// Exceptions
165+
166+
#define rb_raise(EXCEPTION, FORMAT, ...) \
167+
rb_exc_raise(rb_exc_new_str(EXCEPTION, (VALUE) rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "rb_sprintf", rb_tr_unwrap(rb_str_new_cstr(FORMAT)), ##__VA_ARGS__))))
131168

132169
// Additional non-standard
133170
VALUE rb_java_class_of(VALUE val);
@@ -139,39 +176,34 @@ VALUE rb_ivar_lookup(VALUE object, const char *name, VALUE default_value);
139176
// Inline implementations
140177

141178
MUST_INLINE int rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock) {
142-
*lock = polyglot_invoke(RUBY_CEXT, "rb_nativethread_lock_initialize");
179+
*lock = RUBY_CEXT_INVOKE("rb_nativethread_lock_initialize");
143180
return 0;
144181
}
145182

146183
MUST_INLINE int rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock) {
147-
*lock = NULL;
184+
*lock = RUBY_CEXT_INVOKE("rb_nativethread_lock_destroy", *lock);
148185
return 0;
149186
}
150187

151188
MUST_INLINE int rb_nativethread_lock_lock(rb_nativethread_lock_t *lock) {
152-
polyglot_invoke(*lock, "lock");
189+
RUBY_INVOKE_NO_WRAP(*lock, "lock");
153190
return 0;
154191
}
155192

156193
MUST_INLINE int rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock) {
157-
polyglot_invoke(*lock, "unlock");
194+
RUBY_INVOKE_NO_WRAP(*lock, "unlock");
158195
return 0;
159196
}
160197

161198
MUST_INLINE int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp) {
162-
if (rb_obj_is_kind_of(range, rb_cRange)) {
163-
*begp = (VALUE) polyglot_invoke(range, "begin");
164-
*endp = (VALUE) polyglot_invoke(range, "end");
165-
*exclp = (int) polyglot_as_boolean(polyglot_invoke(range, "exclude_end?"));
199+
if (!rb_obj_is_kind_of(range, rb_cRange)) {
200+
if (!RTEST(RUBY_INVOKE(range, "respond_to?", rb_intern("begin")))) return Qfalse_int_const;
201+
if (!RTEST(RUBY_INVOKE(range, "respond_to?", rb_intern("end")))) return Qfalse_int_const;
166202
}
167-
else {
168-
if (!polyglot_as_boolean(polyglot_invoke(range, "respond_to?", rb_intern("begin")))) return Qfalse_int_const;
169-
if (!polyglot_as_boolean(polyglot_invoke(range, "respond_to?", rb_intern("end")))) return Qfalse_int_const;
170203

171-
*begp = polyglot_invoke(range, "begin");
172-
*endp = polyglot_invoke(range, "end");
173-
*exclp = (int) RTEST(polyglot_invoke(range, "exclude_end?"));
174-
}
204+
*begp = RUBY_INVOKE(range, "begin");
205+
*endp = RUBY_INVOKE(range, "end");
206+
*exclp = (int) RTEST(RUBY_INVOKE(range, "exclude_end?"));
175207
return Qtrue_int_const;
176208
}
177209

@@ -194,7 +226,7 @@ MUST_INLINE char *rb_string_value_ptr(VALUE *value_pointer) {
194226
MUST_INLINE char *rb_string_value_cstr(VALUE *value_pointer) {
195227
VALUE string = rb_string_value(value_pointer);
196228

197-
polyglot_invoke(RUBY_CEXT, "rb_string_value_cstr_check", string);
229+
RUBY_CEXT_INVOKE("rb_string_value_cstr_check", string);
198230

199231
return RSTRING_PTR(string);
200232
}
@@ -270,7 +302,7 @@ MUST_INLINE int rb_tr_scan_args(int argc, VALUE *argv, const char *format, VALUE
270302
bool erased_kwargs = false;
271303
bool found_kwargs = false;
272304

273-
if (rest && kwargs && !polyglot_as_boolean(polyglot_invoke(RUBY_CEXT, "test_kwargs", argv[argc - 1], Qfalse))) {
305+
if (rest && kwargs && !polyglot_as_boolean(RUBY_CEXT_INVOKE_NO_WRAP("test_kwargs", argv[argc - 1], Qfalse))) {
274306
kwargs = false;
275307
erased_kwargs = true;
276308
}
@@ -313,7 +345,7 @@ MUST_INLINE int rb_tr_scan_args(int argc, VALUE *argv, const char *format, VALUE
313345
} else if (kwargs && !taken_kwargs) {
314346
if (argn < argc) {
315347
arg = argv[argn];
316-
polyglot_invoke(RUBY_CEXT, "test_kwargs", arg, Qtrue);
348+
RUBY_CEXT_INVOKE_NO_WRAP("test_kwargs", arg, Qtrue);
317349
argn++;
318350
found_kwargs = true;
319351
} else {

0 commit comments

Comments
 (0)