1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#![feature(proc_macro)]
#![recursion_limit = "128"]
extern crate proc_macro;
extern crate syntex;
extern crate syntex_syntax;
#[macro_use] extern crate quote;
use proc_macro::TokenStream;
use syntex_syntax::print::pprust::*;
#[proc_macro_attribute]
pub fn godot_export(_: TokenStream, input: TokenStream) -> TokenStream {
let parse_sess = syntex_syntax::parse::ParseSess::new();
let ast = syntex_syntax::parse::parse_item_from_source_str(String::new(), input.to_string(), &parse_sess).unwrap().unwrap();
match &ast.node {
&syntex_syntax::ast::ItemKind::Impl(_, _, _, _, ref ty, ref implitems) => {
let ty = quote::Ident::from(ty_to_string(ty));
let export_items = implitems.iter()
.filter(|&item| { if let syntex_syntax::ast::Visibility::Public = item.vis {true} else {false} })
.filter(|&item| { if let syntex_syntax::ast::ImplItemKind::Method(_,_) = item.node {true} else {false} })
.map(|item| { if let syntex_syntax::ast::ImplItemKind::Method(ref signature, _) = item.node {(item.ident, signature.decl.clone())} else {unreachable!()} }).collect::<Vec<_>>();
let mut gen = quote::Tokens::new();
for item in &export_items {
if item.1.inputs.len() == 0 {
gen.append(impl_godot_export(format!("{}", item.0).as_str(), None, &Vec::new()).as_str());
} else if item.1.inputs[0].is_self() {
let (self_arg, args) = item.1.inputs.split_first().unwrap();
gen.append(impl_godot_export(format!("{}", item.0).as_str(), Some(self_arg), &args.to_vec()).as_str());
} else {
gen.append(impl_godot_export(format!("{}", item.0).as_str(), None, &item.1.inputs).as_str());
}
}
let register_methods = export_items.iter()
.map(|i| (ident_to_string(i.0)+"_godot_wrapper", ident_to_string(i.0)))
.fold(quote::Tokens::new(), |mut t, i| {
let function_name = quote::Ident::new(i.1);
let wrapper_name = quote::Ident::new(i.0);
t.append(
quote!{
gdnative::gdnative_sys::godot_script_add_method(
std::ffi::CString::new(stringify!(#ty)).unwrap().as_ptr(),
std::ffi::CString::new(stringify!(#function_name)).unwrap().as_ptr(),
&mut gdnative::gdnative_sys::godot_method_attributes{rpc_type: gdnative::gdnative_sys::GODOT_METHOD_RPC_MODE_DISABLED as i32} as *mut gdnative::gdnative_sys::godot_method_attributes,
Some(#ty::#wrapper_name),
);
}.as_str());
t
});
("#[allow(warnings)]".to_string() + &(input.to_string() +
&("#[allow(warnings)]".to_string() + quote!{
impl #ty {
#gen
}
impl gdnative::GodotObjectRegisterMethods for #ty {
fn register_methods() {
unsafe {
#register_methods
}
}
}
}.as_str()))).replace("#[godot_export]", "")
.parse().unwrap()
},
_ => panic!("not an impl block!")
}
}
fn impl_godot_export(function_name: &str, self_arg: Option<&syntex_syntax::ast::Arg>, function_args: &Vec<syntex_syntax::ast::Arg>) -> quote::Tokens {
let wrapper_name = quote::Ident::new(function_name.to_owned() + "_godot_wrapper");
let function_name_ident = quote::Ident::new(function_name);
let num_args = function_args.len() as i32;
let mut args = quote::Tokens::new();
if let Some(arg) = self_arg {
args.append(&format!("{},", arg_to_string(&arg)).replace("self", "this"));
};
let args: quote::Tokens = (0..function_args.len()).fold(args, |mut t, i| {
t.append(&format!("(*(*p_args.offset({}))).into(),", i));
t
});
quote! {
pub unsafe extern "C" fn #wrapper_name(
p_instance: *mut gdnative::gdnative_sys::godot_object,
p_method_data: *mut std::os::raw::c_void,
p_data: *mut std::os::raw::c_void,
p_args: *mut *mut gdnative::gdnative_sys::godot_variant,
p_num_args: std::os::ffi::) -> gdnative::gdnative_sys::godot_variant {
#![allow(unused_mut)]
#![allow(unused_variables)]
use std::panic::catch_unwind;
let mut this: *mut Self = p_data as *mut Self;
let mut this = std::boxed::Box::from_raw(this);
if p_num_args != #num_args {
println!("wrong number of arguments");
std::process::exit(1);
};
let ret_val = if let Ok(r) = catch_unwind(std::panic::AssertUnwindSafe(|| {
Self::#function_name_ident(#args)
})) {
r
} else {
println!("function {} panicked!", #function_name);
std::process::exit(1);
};
std::mem::forget(this);
ret_val.into()
}
}
}