Skip to content

Commit f34d4e8

Browse files
committed
add macros bind
1 parent c439385 commit f34d4e8

File tree

9 files changed

+345
-29
lines changed

9 files changed

+345
-29
lines changed

crates/byondapi-macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "byondapi-macros"
3-
version = "0.2.3"
3+
version = "0.3.3"
44
edition = "2021"
55
description = "Macros for byondapi"
66
license = "MIT"

crates/byondapi-macros/src/lib.rs

Lines changed: 156 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub fn bind(attr: TokenStream, item: TokenStream) -> TokenStream {
129129
func_name: #func_name_ffi_disp,
130130
func_arguments: #arg_names_disp,
131131
docs: #all_docs,
132-
is_variadic: false,
132+
function_type: ::byondapi::binds::FunctionType::Default,
133133
}
134134
});
135135
}
@@ -152,7 +152,7 @@ pub fn bind(attr: TokenStream, item: TokenStream) -> TokenStream {
152152
func_name: #func_name_ffi_disp,
153153
func_arguments: #arg_names_disp,
154154
docs: #all_docs,
155-
is_variadic: false,
155+
function_type: ::byondapi::binds::FunctionType::Default,
156156
}
157157
});
158158
}
@@ -286,7 +286,7 @@ pub fn bind_raw_args(attr: TokenStream, item: TokenStream) -> TokenStream {
286286
func_name: #func_name_ffi_disp,
287287
func_arguments: "",
288288
docs: #all_docs,
289-
is_variadic: true,
289+
function_type: ::byondapi::binds::FunctionType::Variadic,
290290
}
291291
});
292292
}
@@ -309,7 +309,7 @@ pub fn bind_raw_args(attr: TokenStream, item: TokenStream) -> TokenStream {
309309
func_name: #func_name_ffi_disp,
310310
func_arguments: "",
311311
docs: #all_docs,
312-
is_variadic: true,
312+
function_type: ::byondapi::binds::FunctionType::Variadic,
313313
}
314314
});
315315
}
@@ -352,6 +352,158 @@ pub fn bind_raw_args(attr: TokenStream, item: TokenStream) -> TokenStream {
352352
result.into()
353353
}
354354

355+
#[proc_macro_attribute]
356+
pub fn bind_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
357+
let input = syn::parse_macro_input!(item as syn::ItemFn);
358+
let proc = syn::parse_macro_input!(attr as Option<syn::Lit>);
359+
360+
let func_name = &input.sig.ident;
361+
let func_name_disp = quote!(#func_name).to_string();
362+
363+
let func_name_ffi = format!("{func_name_disp}_ffi");
364+
let func_name_ffi = Ident::new(&func_name_ffi, func_name.span());
365+
let func_name_ffi_disp = quote!(#func_name_ffi).to_string();
366+
367+
let args = &input.sig.inputs;
368+
369+
let all_docs = input
370+
.attrs
371+
.iter()
372+
.filter(|attr| matches!(attr.style, syn::AttrStyle::Outer))
373+
.filter_map(|attr| match &attr.meta {
374+
syn::Meta::NameValue(nameval) => {
375+
let ident = nameval.path.get_ident()?;
376+
if *ident == "doc" {
377+
match &nameval.value {
378+
syn::Expr::Lit(literal) => match &literal.lit {
379+
syn::Lit::Str(docstring) => {
380+
Some(format!("///{}\n", docstring.value(),))
381+
}
382+
_ => None,
383+
},
384+
_ => None,
385+
}
386+
} else {
387+
None
388+
}
389+
}
390+
_ => None,
391+
})
392+
.collect::<String>();
393+
394+
//Check for returns
395+
let func_return = match &input.sig.output {
396+
syn::ReturnType::Default => {
397+
return syn::Error::new(
398+
input.span(),
399+
"Empty returns are not allowed, please return a Result",
400+
)
401+
.to_compile_error()
402+
.into()
403+
}
404+
405+
syn::ReturnType::Type(_, ty) => match ty.as_ref() {
406+
&syn::Type::Path(_) => &input.sig.output,
407+
_ => {
408+
return syn::Error::new(input.span(), "Invalid return type, please return a Result")
409+
.to_compile_error()
410+
.into()
411+
}
412+
},
413+
};
414+
415+
let signature = quote! {
416+
#[no_mangle]
417+
pub unsafe extern "C-unwind" fn #func_name_ffi (
418+
__argc: ::byondapi::sys::u4c,
419+
__argv: *mut ::byondapi::value::ByondValue
420+
) -> ::byondapi::value::ByondValue
421+
};
422+
423+
let body = &input.block;
424+
let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> =
425+
syn::punctuated::Punctuated::new();
426+
let mut proc_arg_unpacker: syn::punctuated::Punctuated<
427+
proc_macro2::TokenStream,
428+
syn::Token![,],
429+
> = syn::punctuated::Punctuated::new();
430+
431+
for arg in args.iter().map(extract_args) {
432+
if let syn::Pat::Ident(p) = &*arg.pat {
433+
arg_names.push(p.ident.clone());
434+
let index = arg_names.len() - 1;
435+
proc_arg_unpacker.push(quote! {
436+
args.get(#index).map(::byondapi::value::ByondValue::clone).unwrap_or_default()
437+
});
438+
}
439+
}
440+
441+
let arg_names_disp = quote!(#arg_names).to_string();
442+
443+
//Submit to inventory
444+
let cthook_prelude = match &proc {
445+
Some(something) => {
446+
return syn::Error::new(
447+
something.span(),
448+
"Function rename is not supported for macros",
449+
)
450+
.to_compile_error()
451+
.into()
452+
}
453+
None => {
454+
let func_name_disp = func_name_disp.clone();
455+
quote! {
456+
::byondapi::inventory::submit!({
457+
::byondapi::binds::Bind{
458+
proc_path: #func_name_disp,
459+
func_name: #func_name_ffi_disp,
460+
func_arguments: #arg_names_disp,
461+
docs: #all_docs,
462+
function_type: ::byondapi::binds::FunctionType::Macro,
463+
}
464+
});
465+
}
466+
}
467+
};
468+
469+
let crash_syntax = if cfg!(feature = "old-crash-workaround") {
470+
quote! {
471+
let error_string = ::byondapi::value::ByondValue::try_from(error_string).unwrap();
472+
::byondapi::global_call::call_global_id({
473+
static STACK_TRACE: ::std::sync::OnceLock<u32> = ::std::sync::OnceLock::new();
474+
*STACK_TRACE.get_or_init(|| ::byondapi::byond_string::str_id_of("byondapi_stack_trace")
475+
.expect("byondapi-rs implicitly expects byondapi_stack_trace to exist as a proc for error reporting purposes, this proc doesn't exist!")
476+
)
477+
}
478+
,&[error_string]).unwrap();
479+
::byondapi::value::ByondValue::null()
480+
}
481+
} else {
482+
quote! {
483+
unsafe { ::byondapi::runtime::byond_runtime(error_string) }
484+
}
485+
};
486+
487+
let result = quote! {
488+
#cthook_prelude
489+
#signature {
490+
let args = unsafe { ::byondapi::parse_args(__argc, __argv) };
491+
match #func_name(#proc_arg_unpacker) {
492+
Ok(val) => val,
493+
Err(e) => {
494+
let error_string = ::std::format!("{e:?}");
495+
::std::mem::drop(e);
496+
#crash_syntax
497+
}
498+
}
499+
500+
}
501+
fn #func_name(#args) #func_return
502+
#body
503+
};
504+
result.into()
505+
}
506+
355507
#[proc_macro_attribute]
356508
pub fn init(_: TokenStream, item: TokenStream) -> TokenStream {
357509
let input = syn::parse_macro_input!(item as syn::ItemFn);

crates/byondapi-rs-test/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ crate-type = ["cdylib"]
1010

1111
[dependencies]
1212
byondapi = { path = "../byondapi-rs", default-features = false }
13-
tempfile = "3.17.1"
14-
cargo_metadata = "0.19.1"
13+
tempfile = "3.20.0"
14+
cargo_metadata = "0.19.2"
1515
eyre = "0.6.12"
1616

1717
[features]
1818
default = ["byond-516-1651"]
1919
byond-515-1621 = ["byondapi/byond-515-1621",]
20-
byond-516-1651 = ["byondapi/byond-516-1651"]
20+
byond-516-1651 = ["byondapi/byond-516-1651"]
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//THIS FILE IS AUTOMATICALLY GENERATED BY BYONDAPI_TEST, PLEASE DO NOT TOUCH IT
2+
//PROC DEFINITIONS MAY MOVE AROUND, THIS IS NORMAL
3+
4+
/* This comment bypasses grep checks */ /var/__byondapi_test
5+
6+
/proc/__detect_byondapi_test()
7+
if (world.system_type == UNIX)
8+
return __byondapi_test = "libbyondapi_test"
9+
else
10+
return __byondapi_test = "byondapi_test"
11+
12+
#define BYONDAPI_TEST (__byondapi_test || __detect_byondapi_test())
13+
14+
///Tests macro style binds
15+
var/static/loaded_BYONDAPI_TEST_test_new_obj_macro_ffi = load_ext(BYONDAPI_TEST, "byond:test_new_obj_macro_ffi")
16+
#define test_new_obj_macro_ffi(object, number, thing) call_ext(loaded_BYONDAPI_TEST_test_new_obj_macro_ffi)(object, number, thing)
17+
18+
///Tests new
19+
/proc/test_new_obj()
20+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_new_obj_ffi")
21+
return call_ext(loaded)()
22+
23+
///Tests lists read
24+
/proc/test_list_read(list)
25+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_list_read_ffi")
26+
return call_ext(loaded)(list)
27+
28+
///Tests non-assoc lists
29+
/proc/test_non_assoc_list(list)
30+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_non_assoc_list_ffi")
31+
return call_ext(loaded)(list)
32+
33+
///Tests ref
34+
/proc/test_ref(turf)
35+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_ref_ffi")
36+
return call_ext(loaded)(turf)
37+
38+
///Tests lists lookup
39+
/proc/test_list_key_lookup(list)
40+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_list_key_lookup_ffi")
41+
return call_ext(loaded)(list)
42+
43+
///Tests length with strings
44+
/proc/test_length_with_str(object)
45+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_length_with_str_ffi")
46+
return call_ext(loaded)(object)
47+
48+
///Tests block
49+
/proc/test_block()
50+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_block_ffi")
51+
return call_ext(loaded)()
52+
53+
///Tests lists length
54+
/proc/test_length_with_list(list)
55+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_length_with_list_ffi")
56+
return call_ext(loaded)(list)
57+
58+
///Tests lists popping
59+
/proc/test_list_pop(list)
60+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_list_pop_ffi")
61+
return call_ext(loaded)(list)
62+
63+
///Tests lists indexing
64+
/proc/test_list_index(list)
65+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_list_index_ffi")
66+
return call_ext(loaded)(list)
67+
68+
///Tests lists
69+
/proc/test_list_double(list)
70+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_list_double_ffi")
71+
return call_ext(loaded)(list)
72+
73+
///Tests list pushes
74+
/proc/test_list_push(list)
75+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_list_push_ffi")
76+
return call_ext(loaded)(list)
77+
78+
///Tests readwrite vars
79+
/proc/test_readwrite_var(object)
80+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_readwrite_var_ffi")
81+
return call_ext(loaded)(object)
82+
83+
///Tests proccalls
84+
/proc/test_proc_call(object)
85+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_proc_call_ffi")
86+
return call_ext(loaded)(object)
87+
88+
///Tests pointers
89+
/proc/test_ptr(ptr)
90+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_ptr_ffi")
91+
return call_ext(loaded)(ptr)
92+
93+
///Tests raw args binds
94+
/proc/test_args(...)
95+
var/list/args_copy = args.Copy()
96+
args_copy.Insert(1, src)
97+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_args_ffi")
98+
return call_ext(loaded)(arglist(args_copy))
99+
100+
///Tests main lib connection
101+
/proc/test_connection()
102+
var/static/loaded = load_ext(BYONDAPI_TEST, "byond:test_connection_ffi")
103+
return call_ext(loaded)()
104+

crates/byondapi-rs-test/dm_project/dm_project.dme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@
158158
if(fuck.test_name != "dust")
159159
throw EXCEPTION("Did not create a new object!")
160160

161+
/test/proc/test_byondapi_new_macro()
162+
var/datum/testobject/fuck = test_new_obj_macro()
163+
if(fuck.test_name != "dust")
164+
throw EXCEPTION("Did not create a new object!")
165+
161166
// BEGIN_INTERNALS
162167
// END_INTERNALS
163168
// BEGIN_FILE_DIR

crates/byondapi-rs-test/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,16 @@ fn test_new_obj() -> Result<ByondValue> {
243243
&[],
244244
)?)
245245
}
246+
247+
///Tests macro style binds
248+
#[byondapi::bind_macro]
249+
fn test_new_obj_macro(
250+
object: ByondValue,
251+
number: ByondValue,
252+
thing: ByondValue,
253+
) -> Result<ByondValue> {
254+
Ok(ByondValue::builtin_new(
255+
ByondValue::try_from("/datum/testobject")?,
256+
&[],
257+
)?)
258+
}

crates/byondapi-rs/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "byondapi"
3-
version = "0.5.14"
3+
version = "0.6.14"
44
authors = ["tigercat2000 <[email protected]>"]
55
edition = "2021"
66
description = "Idiomatic Rust bindings for BYONDAPI"
@@ -14,9 +14,9 @@ exclude = [".vscode/*"]
1414

1515
[dependencies]
1616
byondapi-sys = { path = "../byondapi-sys", version = "0.12.3", default-features = false }
17-
byondapi-macros = { path = "../byondapi-macros", version = "0.2.3" }
18-
libloading = "0.8.6"
19-
inventory = "0.3.19"
17+
byondapi-macros = { path = "../byondapi-macros", version = "0.3.3" }
18+
libloading = "0.8.7"
19+
inventory = "0.3.20"
2020
num_enum = "0.7.3"
2121

2222
[features]

0 commit comments

Comments
 (0)