Skip to content

Commit 6d6114f

Browse files
committed
support for rust fns
1 parent 8299955 commit 6d6114f

File tree

7 files changed

+1232
-11
lines changed

7 files changed

+1232
-11
lines changed

Cargo.toml

+15-4
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,32 @@ version = "0.3.1-alpha.0"
1212
edition = "2018"
1313

1414
[dependencies]
15-
failure = "0.1.6"
15+
failure = "0.1.8"
1616

1717
[dependencies.duk-sys]
18-
version = "^0.3.0"
18+
path = "./duk-sys"
19+
version = "0.3.1-alpha.0"
20+
21+
[dependencies.duk-derive]
22+
optional = true
23+
path = "./duk-derive"
24+
version = "0.3.1-alpha.0"
1925

2026
[dependencies.log]
2127
optional = true
22-
version = "0.4.10"
28+
version = "0.4.8"
29+
30+
[dependencies.serde]
31+
optional = true
32+
version = "1.0"
2333

2434
[dev-dependencies]
2535
env_logger = "0.7.1"
2636

2737
[features]
2838
debug = ["duk-sys/debug"]
29-
default = ["debug", "logging"]
39+
default = ["debug", "logging", "derive"]
3040
logging = ["log"]
3141
spam = ["duk-sys/spam"]
3242
trace = ["duk-sys/trace"]
43+
derive = ["duk-derive", "serde"]

duk-derive/Cargo.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "duk-derive"
3+
version = "0.3.1-alpha.0"
4+
authors = ["Aiden McClelland <[email protected]>"]
5+
edition = "2018"
6+
description = "derive macros for adding rust fns to duktape context"
7+
documentation = "https://dflemstr.github.io/duk/duktape_sys"
8+
homepage = "https://dflemstr.github.io/duk/duktape_sys"
9+
keywords = ["javascript", "js", "ecmascript", "duktape"]
10+
license = "MIT"
11+
repository = "https://github.com/dflemstr/duk"
12+
13+
[lib]
14+
proc-macro = true
15+
16+
[dependencies]
17+
proc-macro2 = "1.0.18"
18+
quote = "1.0.6"
19+
syn = { version = "1.0.30", features = ["full", "extra-traits"] }

duk-derive/src/lib.rs

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
extern crate proc_macro;
2+
3+
use proc_macro::TokenStream;
4+
use quote::quote;
5+
use syn::parse::Parse;
6+
use syn::spanned::Spanned;
7+
8+
#[proc_macro_attribute]
9+
pub fn duktape_fn(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
10+
let ast: syn::ItemFn = syn::parse(item.clone()).expect("failed to parse token stream as fn");
11+
let fn_name = ast.sig.ident;
12+
let duk_fn_name = syn::Ident::new(
13+
&format!("duk_derive_{}", fn_name),
14+
proc_macro2::Span::call_site(),
15+
);
16+
let fn_arg_len_name = syn::Ident::new(
17+
&format!("{}_arg_len", duk_fn_name),
18+
proc_macro2::Span::call_site(),
19+
);
20+
let mut arg_names = Vec::new();
21+
let mut args = Vec::new();
22+
for arg in ast.sig.inputs {
23+
let arg = match arg {
24+
syn::FnArg::Receiver(_) => {
25+
panic!("cannot derive `duktape_fn` on function that takes `self` as an argument")
26+
}
27+
syn::FnArg::Typed(a) => a,
28+
};
29+
let name = format!("arg_{}", args.len());
30+
let name_ident = syn::Ident::new(&name, arg.span());
31+
let arg_idx = args.len() as i32;
32+
arg_names.push(name_ident.clone());
33+
args.push(quote! {
34+
let #name_ident = match duk::deserialize_from_stack(ctx, #arg_idx) {
35+
Ok(a) => a,
36+
Err(e) => {
37+
duk::duk_sys::duk_push_error_object(
38+
ctx,
39+
duk::duk_sys::DUK_ERR_TYPE_ERROR as i32,
40+
format!("{}", e).as_ptr().cast()
41+
);
42+
duk::duk_sys::duk_throw_raw(ctx);
43+
return 0;
44+
}
45+
};
46+
});
47+
}
48+
let fn_arg_len = args.len();
49+
let ret = match ast.sig.output {
50+
syn::ReturnType::Type(_, _) => quote! {
51+
match duk::serialize_to_stack(ctx, &res) {
52+
Ok(_) => {
53+
1
54+
},
55+
Err(e) => {
56+
duk::duk_sys::duk_push_error_object(
57+
ctx,
58+
duk::duk_sys::DUK_ERR_TYPE_ERROR as i32,
59+
format!("{}", e).as_ptr().cast()
60+
);
61+
duk::duk_sys::duk_throw_raw(ctx);
62+
0
63+
}
64+
}
65+
},
66+
_ => quote! {
67+
0
68+
},
69+
};
70+
let gen = quote! {
71+
unsafe extern "C" fn #duk_fn_name(ctx: *mut duk_sys::duk_context) -> i32 {
72+
#(
73+
#args
74+
)*
75+
76+
let res = #fn_name(#(
77+
#arg_names,
78+
)*);
79+
80+
#ret
81+
}
82+
const #fn_arg_len_name: usize = #fn_arg_len;
83+
};
84+
item.extend(TokenStream::from(gen));
85+
item
86+
}
87+
88+
struct AddGlobalFn {
89+
ctx: syn::Expr,
90+
func: syn::Path,
91+
}
92+
impl Parse for AddGlobalFn {
93+
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
94+
let ctx = input.parse()?;
95+
input.parse::<syn::Token![,]>()?;
96+
let func = input.parse()?;
97+
Ok(AddGlobalFn { ctx, func })
98+
}
99+
}
100+
101+
#[proc_macro]
102+
pub fn add_global_fn(input: TokenStream) -> TokenStream {
103+
let AddGlobalFn { ctx, func } = syn::parse_macro_input!(input as AddGlobalFn);
104+
105+
let leaf = func.segments.iter().cloned().next_back().unwrap().ident;
106+
107+
let derived = syn::Path {
108+
leading_colon: func.leading_colon,
109+
segments: func
110+
.segments
111+
.iter()
112+
.cloned()
113+
.take(func.segments.len() - 1)
114+
.chain(std::iter::once(syn::PathSegment {
115+
ident: syn::Ident::new(&format!("duk_derive_{}", leaf), leaf.span()),
116+
arguments: syn::PathArguments::None,
117+
}))
118+
.collect(),
119+
};
120+
let derived_arg_len = syn::Path {
121+
leading_colon: func.leading_colon,
122+
segments: func
123+
.segments
124+
.iter()
125+
.cloned()
126+
.take(func.segments.len() - 1)
127+
.chain(std::iter::once(syn::PathSegment {
128+
ident: syn::Ident::new(&format!("duk_derive_{}_arg_len", leaf), leaf.span()),
129+
arguments: syn::PathArguments::None,
130+
}))
131+
.collect(),
132+
};
133+
134+
(quote! {
135+
unsafe {
136+
#ctx.add_global_fn(stringify!(#leaf), #derived, #derived_arg_len)
137+
}
138+
})
139+
.into()
140+
}

duk-sys/Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ repository = "https://github.com/dflemstr/duk"
1111
version = "0.3.1-alpha.0"
1212

1313
[build-dependencies]
14-
cc = "1.0.48"
14+
cc = "1.0.54"
1515

1616
[dependencies]
17-
libc = "0.2.66"
17+
libc = "0.2.71"
1818

1919
[dependencies.log]
2020
optional = true
21-
version = "0.4.10"
21+
version = "0.4.8"
2222

2323
[dev-dependencies]
24-
bindgen = "0.52.0"
24+
bindgen = "0.54.0"
2525

2626
[features]
2727
debug = ["log"]

0 commit comments

Comments
 (0)