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 }));
296 }
297
298 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}