helix/macros/
init.rs

1#[macro_export]
2macro_rules! codegen_init {
3    { [ $($class:tt)* ] } => {
4        #[allow(non_snake_case)]
5        #[no_mangle]
6        pub extern "C" fn Init_native() {
7            $crate::sys::check_version();
8
9            $(
10                codegen_class_binding!($class, $class);
11            )*
12        }
13    }
14}
15
16#[macro_export]
17macro_rules! codegen_class_binding {
18    { $class:tt, {
19        type: class,
20        rust_name: $rust_name:tt,
21        ruby_name: { $($ruby_name:tt)* },
22        meta: { pub: $pub:tt, reopen: false },
23        struct: (),
24        methods: [ $($method:tt)* ]
25    } } => ({
26        use ::std::mem::transmute;
27        let def = $crate::ClassDefinition::new(cstr!($($ruby_name)*));
28
29        $(
30            codegen_define_method!(def, $class, $method);
31        )*
32
33        unsafe { $rust_name = transmute(def.class) };
34    });
35
36    { $class:tt, {
37        type: class,
38        rust_name: $rust_name:tt,
39        ruby_name: { $($ruby_name:tt)* },
40        meta: { pub: $pub:tt, reopen: true },
41        struct: (),
42        methods: [ $($method:tt)* ]
43    } } => ({
44        use ::std::mem::transmute;
45        let def = $crate::ClassDefinition::reopen(cstr!($($ruby_name)*));
46
47        $(
48            codegen_define_method!(def, $class, $method);
49        )*
50
51        unsafe { $rust_name = transmute(def.class) };
52    });
53
54    { $class:tt, {
55        type: class,
56        rust_name: $rust_name:tt,
57        ruby_name: { $($ruby_name:tt)* },
58        meta: { pub: $pub:tt, reopen: $reopen:tt },
59        struct: { $($struct:tt)* },
60        methods: [ $($method:tt)* ]
61    } } => ({
62        use ::std::mem::transmute;
63
64        extern "C" fn __alloc__(_klass: $crate::sys::VALUE) -> $crate::sys::VALUE {
65            $rust_name::__alloc_with__(None)
66        }
67
68        let def = $crate::ClassDefinition::wrapped(cstr!($($ruby_name)*), __alloc__);
69
70        $(
71            codegen_define_method!(def, $class, $method);
72        )*
73
74        unsafe { $rust_name = transmute(def.class) }
75    });
76
77}
78
79#[macro_export]
80macro_rules! codegen_define_method {
81    ($def:tt, {
82        type: class,
83        rust_name: $cls_rust_name:tt,
84        $($rest:tt)*
85    }, {
86        type: class_method,
87        rust_name: $rust_name:tt,
88        ruby_name: { $($ruby_name:tt)* },
89        self: (),
90        args: [ $($arg:tt : $argty:ty),* ],
91        ret: { $($ret:tt)* },
92        body: $body:tt
93    }) => ({
94        use $crate::sys::{VALUE};
95        use $crate::{Error};
96
97        extern "C" fn __ruby_method__(_: VALUE, $($arg : VALUE),*) -> VALUE {
98            let result = __rust_method__($($arg),*);
99
100            match result {
101                Ok(value) => return value,
102                Err(exception) => unsafe { exception.raise() }
103            }
104        }
105
106        #[inline]
107        fn __rust_method__($($arg : $crate::sys::VALUE),*) -> Result<VALUE, Error> {
108            #[allow(unused_imports)]
109            use $crate::{FromRuby, ToRuby};
110
111            $(
112                let $arg = try!(<$argty>::from_ruby($arg));
113            )*
114
115            $(
116                let $arg = <$argty>::from_checked($arg);
117            )*
118
119            let result: Result<$($ret)*, Error> = handle_exception! {
120                $cls_rust_name::$rust_name($($arg),*)
121            };
122
123            result.and_then(ToRuby::to_ruby)
124        }
125
126        let name = cstr!($($ruby_name)*);
127        let method = __ruby_method__ as *const $crate::libc::c_void;
128        let arity = method_arity!($($arg)*);
129
130        $def.define_method($crate::MethodDefinition::class(name, method, arity));
131    });
132
133    ($def:tt, {
134        type: class,
135        rust_name: $cls_rust_name:tt,
136        ruby_name: $cls_ruby_name:tt,
137        meta: $meta:tt,
138        struct: $struct:tt,
139        $($rest:tt)*
140    }, {
141        type: instance_method,
142        rust_name: $rust_name:tt,
143        ruby_name: { $($ruby_name:tt)* },
144        self: { ownership: { $($ownership:tt)* }, name: $self:tt },
145        args: [ $($arg:tt : $argty:ty),* ],
146        ret: { $($ret:tt)* },
147        body: $body:tt
148    }) => ({
149        use $crate::sys::{VALUE};
150        use $crate::{Error};
151
152        extern "C" fn __ruby_method__(rb_self: VALUE, $($arg : VALUE),*) -> VALUE {
153            let result = __rust_method__(rb_self, $($arg),*);
154
155            match result {
156                Ok(value) => return value,
157                Err(exception) => unsafe { exception.raise() }
158            }
159        }
160
161        #[inline]
162        fn __rust_method__(rb_self: VALUE, $($arg : VALUE),*) -> Result<VALUE, Error> {
163            #[allow(unused_imports)]
164            use $crate::{FromRuby, ToRuby};
165
166            let rust_self = try!(<codegen_self_pointer_type! { struct: $struct, ownership: { $($ownership)* }, type: $cls_rust_name }>::from_ruby(rb_self));
167
168            $(
169                let $arg = try!(<$argty>::from_ruby($arg));
170            )*
171
172            let rust_self = <codegen_self_pointer_type! { struct: $struct, ownership: { $($ownership)* }, type: $cls_rust_name }>::from_checked(rust_self);
173
174            $(
175                let $arg = <$argty>::from_checked($arg);
176            )*
177
178            let result: Result<$($ret)*, Error> = handle_exception! {
179                rust_self.$rust_name($($arg),*)
180            };
181
182            result.and_then(ToRuby::to_ruby)
183        }
184
185        let name = cstr!($($ruby_name)*);
186        let method = __ruby_method__ as *const $crate::libc::c_void;
187        let arity = method_arity!($($arg)*);
188
189        $def.define_method($crate::MethodDefinition::instance(name, method, arity))
190    });
191
192    ($def:tt, {
193        type: class,
194        rust_name: $cls_rust_name:tt,
195        ruby_name: $cls_ruby_name:tt,
196        meta: $meta:tt,
197        struct: $struct:tt,
198        $($rest:tt)*
199    }, {
200        type: initializer,
201        rust_name: $rust_name:tt,
202        ruby_name: { $($ruby_name:tt)* },
203        self: { ownership: {}, name: $self:tt },
204        args: [ $($arg:tt : $argty:ty),* ],
205        ret: { $($ret:tt)* },
206        body: $body:tt
207    }) => ({
208        use $crate::sys::{VALUE};
209        use $crate::{Error};
210
211        impl $cls_rust_name {
212            pub fn new($($arg : $argty),*) -> $($ret)* {
213                $cls_rust_name::$rust_name(unsafe { $crate::sys::Qnil } , $($arg),*)
214            }
215        }
216
217        extern "C" fn __ruby_initialize__(rb_self: VALUE, $($arg : VALUE),*) -> VALUE {
218            let result = __rust_initialize__(rb_self $(, $arg)*);
219
220            match result {
221                Ok(value) => return value,
222                Err(exception) => unsafe { exception.raise() }
223            }
224        }
225
226        #[inline]
227        fn __rust_initialize__(rb_self: VALUE, $($arg : VALUE),*) -> Result<VALUE, Error> {
228            #[allow(unused_imports)]
229            use $crate::{FromRuby};
230            use $crate::sys::{Data_Set_Struct_Value};
231
232            $(
233                let $arg = try!(<$argty>::from_ruby($arg));
234            )*
235
236            $(
237                let $arg = <$argty>::from_checked($arg);
238            )*
239
240            let rust_self = Box::new($cls_rust_name::initialize(rb_self, $($arg),*));
241
242            unsafe { Data_Set_Struct_Value(rb_self, ::std::mem::transmute(rust_self)) };
243
244            Ok(rb_self)
245        }
246
247        let arity = method_arity!($($arg)*);
248        let method = __ruby_initialize__ as *const $crate::libc::c_void;
249
250        $def.define_method($crate::MethodDefinition::instance(cstr!($($ruby_name)*), method, arity));
251    });
252}
253
254
255#[macro_export]
256macro_rules! codegen_self_pointer_type {
257    {
258        struct: (),
259        ownership: $ownership:tt,
260        type: $type:tt
261    } => {
262        $type
263    };
264
265    {
266        struct: $struct:tt,
267        ownership: { $($ownership:tt)* },
268        type: $type:tt
269    } => {
270        $($ownership)* $type
271    };
272}
273
274#[macro_export]
275macro_rules! method_arity {
276  ( $($arg:tt)* ) => {
277    { 0isize $(+ replace_expr!($arg 1isize))* }
278  }
279}
280
281#[macro_export]
282macro_rules! replace_expr {
283    ($_t:tt $sub:expr) => {$sub};
284}
285
286#[macro_export]
287macro_rules! handle_exception {
288    { $($body:tt)* } => {
289        {
290            let hide_err = ::std::env::var("RUST_BACKTRACE").is_err();
291
292            if hide_err {
293                ::std::panic::set_hook(Box::new(|_| {
294                    // Silence
295                }));
296            }
297
298            // TODO: Poison any objects that cross the boundary to prevent them
299            // from being used in Ruby and triggering panics over and over again.
300            let res = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
301                $($body)*
302            }));
303
304            if hide_err {
305                let _ = ::std::panic::take_hook();
306            }
307
308            res.map_err(|e| $crate::Error::from_any(e))
309        }
310    }
311}