plugger_ruby/
shims.rs

1use rurust::Value;
2use libc;
3use std::mem;
4
5/// A placeholder for any Rust struct.
6pub struct Receiver;
7
8/// A method which takes a receiver.
9pub type Method = fn(&mut Receiver) -> Value;
10pub type Method1 = fn(&mut Receiver, Value) -> Value;
11pub type Method2 = fn(&mut Receiver, Value, Value) -> Value;
12pub type Method3 = fn(&mut Receiver, Value, Value, Value) -> Value;
13pub type Method4 = fn(&mut Receiver, Value, Value, Value, Value) -> Value;
14pub type Method5 = fn(&mut Receiver, Value, Value, Value, Value, Value) -> Value;
15pub type Method6 = fn(&mut Receiver, Value, Value, Value, Value, Value, Value) -> Value;
16pub type Method7 = fn(&mut Receiver, Value, Value, Value, Value, Value, Value, Value) -> Value;
17pub type Method8 = fn(&mut Receiver, Value, Value, Value, Value, Value, Value, Value, Value) -> Value;
18pub type Method9 = fn(&mut Receiver, Value, Value, Value, Value, Value, Value, Value, Value, Value) -> Value;
19
20/// A function.
21pub type Function = fn() -> Value;
22pub type Function1 = fn(Value) -> Value;
23pub type Function2 = fn(Value, Value) -> Value;
24pub type Function3 = fn(Value, Value, Value) -> Value;
25pub type Function4 = fn(Value, Value, Value, Value) -> Value;
26pub type Function5 = fn(Value, Value, Value, Value, Value) -> Value;
27pub type Function6 = fn(Value, Value, Value, Value, Value, Value) -> Value;
28pub type Function7 = fn(Value, Value, Value, Value, Value, Value, Value) -> Value;
29pub type Function8 = fn(Value, Value, Value, Value, Value, Value, Value, Value) -> Value;
30pub type Function9 = fn(Value, Value, Value, Value, Value, Value, Value, Value, Value) -> Value;
31
32/// Dispatches arguments to a method call.
33macro_rules! dispatch {
34    ( $method:expr => $method_type:ty => ( $( $arg:expr ),* ) ) => {
35        {
36            let method: $method_type = unsafe { mem::transmute($method) };
37            method ( $( $arg ),* )
38        }
39    }
40}
41
42/// Shim to call a Rust method (taking `&self`) from Ruby.
43pub extern fn ruby_method(argc: libc::c_int, argv: *const Value, object: Value) -> Value {
44    let (method, arguments) = helpers::process_method_arguments(argc, argv);
45
46    let receiver = helpers::reference_to_struct(object);
47
48    let a = arguments;
49    match arguments.len() {
50        0 => dispatch!(method => Method  => (receiver)),
51        1 => dispatch!(method => Method1 => (receiver, a[0])),
52        2 => dispatch!(method => Method2 => (receiver, a[0], a[1])),
53        3 => dispatch!(method => Method3 => (receiver, a[0], a[1], a[2])),
54        4 => dispatch!(method => Method4 => (receiver, a[0], a[1], a[2], a[3])),
55        5 => dispatch!(method => Method5 => (receiver, a[0], a[1], a[2], a[3], a[4])),
56        6 => dispatch!(method => Method6 => (receiver, a[0], a[1], a[2], a[3], a[4], a[5])),
57        7 => dispatch!(method => Method7 => (receiver, a[0], a[1], a[2], a[3], a[4], a[5], a[6])),
58        8 => dispatch!(method => Method8 => (receiver, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])),
59        9 => dispatch!(method => Method9 => (receiver, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])),
60        _ => panic!("too many arguments: {}", arguments.len()),
61    }
62}
63
64/// Shim to call a Rust function (doesn't take `self`) from Ruby.
65pub extern fn ruby_singleton_method(argc: libc::c_int, argv: *const Value, _class: Value) -> Value {
66    let (function, arguments) = helpers::process_function_arguments(argc, argv);
67
68    let a = arguments;
69    match arguments.len() {
70        0 => dispatch!(function => Function  => ()),
71        1 => dispatch!(function => Function1 => (a[0])),
72        2 => dispatch!(function => Function2 => (a[0], a[1])),
73        3 => dispatch!(function => Function3 => (a[0], a[1], a[2])),
74        4 => dispatch!(function => Function4 => (a[0], a[1], a[2], a[3])),
75        5 => dispatch!(function => Function5 => (a[0], a[1], a[2], a[3], a[4])),
76        6 => dispatch!(function => Function6 => (a[0], a[1], a[2], a[3], a[4], a[5])),
77        7 => dispatch!(function => Function7 => (a[0], a[1], a[2], a[3], a[4], a[5], a[6])),
78        8 => dispatch!(function => Function8 => (a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])),
79        9 => dispatch!(function => Function9 => (a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])),
80        _ => panic!("too many arguments: {}", arguments.len()),
81    }
82}
83
84mod helpers {
85    use super::{Receiver, Method, Function};
86    use rurust::Value;
87    use std::{mem, slice};
88    use libc;
89
90    /// Stores the arguments there were passed to the shim.
91    struct Arguments<'a> {
92        function_ptr: Value,
93        full_arguments: &'a [Value],
94    }
95
96    /// Processes the arguments given to a method shim.
97    pub fn process_method_arguments<'a>(argc: libc::c_int, argv: *const Value) -> (Method, &'a [Value]) {
98        let arguments = self::separate_arguments(argc, argv);
99        (self::method(arguments.function_ptr), arguments.full_arguments)
100    }
101
102    /// Processes the arguments given to a function shim.
103    pub fn process_function_arguments<'a>(argc: libc::c_int, argv: *const Value) -> (Function, &'a [Value]) {
104        let arguments = self::separate_arguments(argc, argv);
105        (self::function(arguments.function_ptr), arguments.full_arguments)
106    }
107
108    /// Given an `argc` and `argv` pair, create a new `Arguments` object.
109    fn separate_arguments<'a>(argc: libc::c_int, argv: *const Value) -> Arguments<'a> {
110        let all_arguments = unsafe { slice::from_raw_parts(argv, argc as usize) };
111
112        Arguments {
113            function_ptr: all_arguments[0],
114            full_arguments: &all_arguments[1..],
115        }
116    }
117
118    /// Gets a reference to the Rust struct from the associated Ruby object.
119    pub fn reference_to_struct<'a>(ruby_object: Value) -> &'a mut Receiver {
120        let ptr = ruby_object.call_no_args("object_pointer").to_u64() as usize;
121        let receiver: &mut Receiver = unsafe { mem::transmute(ptr) };
122        receiver
123    }
124
125    /// Creates a method from a function pointer given from Ruby.
126    fn method(function_pointer: Value) -> Method {
127        unsafe { mem::transmute(self::function(function_pointer)) }
128    }
129
130    /// Creates a function from a function pointer given from Ruby.
131    fn function(function_pointer: Value) -> Function {
132        let ptr = function_pointer.to_u64() as usize;
133
134        unsafe { mem::transmute(ptr) }
135    }
136}
137