Skip to content

Commit 465cb44

Browse files
authored
Merge pull request #4 from dr-bonez/derive
support for rust fns
2 parents 8299955 + b1a7219 commit 465cb44

File tree

7 files changed

+1312
-11
lines changed

7 files changed

+1312
-11
lines changed

Cargo.toml

+16-4
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,33 @@ 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"
36+
serde = { version = "1.0", features = ["derive"] }
2637

2738
[features]
2839
debug = ["duk-sys/debug"]
29-
default = ["debug", "logging"]
40+
default = ["debug", "logging", "derive"]
3041
logging = ["log"]
3142
spam = ["duk-sys/spam"]
3243
trace = ["duk-sys/trace"]
44+
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.36"
18+
quote = "1.0.15"
19+
syn = { version = "1.0.86", features = ["full", "extra-traits"] }

duk-derive/src/lib.rs

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
extern crate proc_macro;
2+
3+
use proc_macro::TokenStream;
4+
use quote::quote;
5+
use syn::spanned::Spanned;
6+
7+
#[proc_macro_attribute]
8+
pub fn duktape_fn(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
9+
let duk_path = if std::env::var("CARGO_PKG_NAME").unwrap() == "duk" {
10+
quote! { crate }
11+
} else {
12+
quote! { ::duk }
13+
};
14+
let ast: syn::ItemFn = syn::parse(item.clone()).expect("failed to parse token stream as fn");
15+
let fn_name = ast.sig.ident;
16+
let fn_vis = ast.vis;
17+
let fn_name_str = syn::LitStr::new(&format!("{}", fn_name), fn_name.span());
18+
let mut arg_names = Vec::new();
19+
let mut args = Vec::new();
20+
for arg in ast.sig.inputs {
21+
let arg = match arg {
22+
syn::FnArg::Receiver(s) => {
23+
return syn::Error::new(
24+
s.span(),
25+
"cannot derive `duktape_fn` on function that takes `self` as an argument",
26+
)
27+
.to_compile_error()
28+
.into();
29+
}
30+
syn::FnArg::Typed(a) => a,
31+
};
32+
let name = format!("arg_{}", args.len());
33+
let name_ident = syn::Ident::new(&name, arg.span());
34+
let arg_idx = args.len() as i32;
35+
arg_names.push(name_ident.clone());
36+
args.push(quote! {
37+
let #name_ident = match #duk_path::deserialize_from_stack(ctx, #arg_idx) {
38+
Ok(a) => a,
39+
Err(e) => {
40+
#duk_path::duk_sys::duk_push_error_object(
41+
ctx,
42+
#duk_path::duk_sys::DUK_ERR_TYPE_ERROR as i32,
43+
std::ffi::CString::new(format!("{}", e)).unwrap_or_default().into_raw(),
44+
);
45+
#duk_path::duk_sys::duk_throw_raw(ctx);
46+
return 0;
47+
}
48+
};
49+
});
50+
}
51+
let fn_arg_len = args.len();
52+
let ret = match ast.sig.output {
53+
syn::ReturnType::Type(_, _) => quote! {
54+
match #duk_path::serialize_to_stack(ctx, &res) {
55+
Ok(_) => {
56+
1
57+
},
58+
Err(e) => {
59+
#duk_path::duk_sys::duk_push_error_object(
60+
ctx,
61+
#duk_path::duk_sys::DUK_ERR_TYPE_ERROR as i32,
62+
std::ffi::CString::new(format!("{}", e)).unwrap_or_default().into_raw(),
63+
);
64+
#duk_path::duk_sys::duk_throw_raw(ctx);
65+
0
66+
}
67+
}
68+
},
69+
_ => quote! {
70+
0
71+
},
72+
};
73+
let gen = quote! {
74+
#fn_vis mod #fn_name {
75+
use super::#fn_name as fn_impl;
76+
77+
pub struct DukFnImpl;
78+
79+
unsafe impl #duk_path::DukFunction for DukFnImpl {
80+
const NARGS: usize = #fn_arg_len;
81+
const NAME: &'static str = #fn_name_str;
82+
unsafe extern "C" fn duk_call(ctx: *mut #duk_path::duk_sys::duk_context) -> i32 {
83+
#(
84+
#args
85+
)*
86+
let res = match std::panic::catch_unwind(|| fn_impl(#(
87+
#arg_names,
88+
)*)) {
89+
Ok(res) => res,
90+
Err(e) => {
91+
let error = if let Some(msg) = e.downcast_ref::<&str>() {
92+
format!("panic: {}", *msg)
93+
} else if let Some(msg) = e.downcast_ref::<String>() {
94+
format!("panic: {}", msg)
95+
} else {
96+
"panic: unknown error".into()
97+
};
98+
#duk_path::duk_sys::duk_push_error_object(
99+
ctx,
100+
#duk_path::duk_sys::DUK_ERR_ERROR as i32,
101+
std::ffi::CString::new(error).unwrap_or_default().into_raw(),
102+
);
103+
#duk_path::duk_sys::duk_throw_raw(ctx);
104+
return 0;
105+
}
106+
};
107+
#ret
108+
}
109+
}
110+
}
111+
};
112+
item.extend(TokenStream::from(gen));
113+
item
114+
}

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)