Skip to content

Commit c568a2d

Browse files
committed
feat(macros): Allow function/constructor callback to get generic parameters.
This patch allows `#[function_callback]` and `#[constructor_callback]` to be defined where generic parameters. This patch adds a test to ensure it works correctly. Rust will monomorphize the functions automatically, the macro doesn't have to handle that.
1 parent e04beed commit c568a2d

File tree

2 files changed

+86
-6
lines changed

2 files changed

+86
-6
lines changed

javascriptcore-macros/src/lib.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,20 @@ pub fn function_callback(_attributes: TokenStream, item: TokenStream) -> TokenSt
2323
.expect("#[function_callback] must apply on a valid function");
2424
let function_visibility = &function.vis;
2525
let function_name = &function.sig.ident;
26+
let function_generics = &function.sig.generics.params;
27+
let function_where_clause = &function.sig.generics.where_clause;
2628

2729
quote! {
28-
#function_visibility unsafe extern "C" fn #function_name(
30+
#function_visibility unsafe extern "C" fn #function_name < #function_generics > (
2931
raw_ctx: javascriptcore::sys::JSContextRef,
3032
function: javascriptcore::sys::JSObjectRef,
3133
this_object: javascriptcore::sys::JSObjectRef,
3234
argument_count: usize,
3335
arguments: *const javascriptcore::sys::JSValueRef,
3436
exception: *mut javascriptcore::sys::JSValueRef,
35-
) -> *const javascriptcore::sys::OpaqueJSValue {
37+
) -> *const javascriptcore::sys::OpaqueJSValue
38+
#function_where_clause
39+
{
3640
use core::{mem::ManuallyDrop, option::Option, ops::Not, ptr, result::Result, slice};
3741
use std::vec::Vec;
3842
use javascriptcore::{sys::JSValueRef, JSContext, JSObject, JSValue};
@@ -80,7 +84,7 @@ pub fn function_callback(_attributes: TokenStream, item: TokenStream) -> TokenSt
8084
) -> Result<JSValue, JSException> = {
8185
#function
8286

83-
#function_name
87+
#function_name ::< #function_generics >
8488
};
8589

8690
// Second, call the original function.
@@ -130,15 +134,19 @@ pub fn constructor_callback(_attributes: TokenStream, item: TokenStream) -> Toke
130134
.expect("#[constructor_callback] must apply on a valid function");
131135
let constructor_visibility = &constructor.vis;
132136
let constructor_name = &constructor.sig.ident;
137+
let constructor_generics = &constructor.sig.generics.params;
138+
let constructor_where_clause = &constructor.sig.generics.where_clause;
133139

134140
quote! {
135-
#constructor_visibility unsafe extern "C" fn #constructor_name(
141+
#constructor_visibility unsafe extern "C" fn #constructor_name < #constructor_generics >(
136142
raw_ctx: javascriptcore::sys::JSContextRef,
137143
constructor: javascriptcore::sys::JSObjectRef,
138144
argument_count: usize,
139145
arguments: *const javascriptcore::sys::JSValueRef,
140146
exception: *mut javascriptcore::sys::JSValueRef,
141-
) -> *mut javascriptcore::sys::OpaqueJSValue {
147+
) -> *mut javascriptcore::sys::OpaqueJSValue
148+
#constructor_where_clause
149+
{
142150
use core::{mem::ManuallyDrop, option::Option, ops::Not, ptr, result::Result, slice};
143151
use std::vec::Vec;
144152
use javascriptcore::{sys::JSValueRef, JSContext, JSObject, JSValue};
@@ -172,7 +180,7 @@ pub fn constructor_callback(_attributes: TokenStream, item: TokenStream) -> Toke
172180
) -> Result<JSValue, JSException> = {
173181
#constructor
174182

175-
#constructor_name
183+
#constructor_name ::< #constructor_generics >
176184
};
177185

178186
// Second, call the original constructor.

src/value.rs

+72
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,78 @@ mod tests {
10911091
Ok(())
10921092
}
10931093

1094+
#[test]
1095+
fn function_with_macros_and_generics() -> Result<(), JSException> {
1096+
use crate as javascriptcore;
1097+
1098+
let ctx = JSContext::default();
1099+
1100+
trait Math {
1101+
fn double(x: f64) -> f64;
1102+
}
1103+
1104+
struct Double;
1105+
struct NoDouble;
1106+
1107+
impl Math for Double {
1108+
fn double(x: f64) -> f64 {
1109+
x * 2.
1110+
}
1111+
}
1112+
1113+
impl Math for NoDouble {
1114+
fn double(x: f64) -> f64 {
1115+
x
1116+
}
1117+
}
1118+
1119+
#[function_callback]
1120+
fn do_double<M>(
1121+
ctx: &JSContext,
1122+
_function: Option<&JSObject>,
1123+
_this_object: Option<&JSObject>,
1124+
arguments: &[JSValue],
1125+
) -> Result<JSValue, JSException>
1126+
where
1127+
M: Math,
1128+
{
1129+
if arguments.len() != 1 {
1130+
return Err(JSValue::new_string(ctx, "must receive 1 argument").into());
1131+
}
1132+
1133+
let x = arguments[0].as_number()?;
1134+
1135+
Ok(JSValue::new_number(ctx, M::double(x)))
1136+
}
1137+
1138+
// Let's try with `Double` as the generic parameter.
1139+
let do_math = JSValue::new_function(&ctx, "do_math_with_double", Some(do_double::<Double>));
1140+
let do_math_as_object = do_math.as_object()?;
1141+
1142+
// Correct call.
1143+
{
1144+
let result =
1145+
do_math_as_object.call_as_function(None, &[JSValue::new_number(&ctx, 2.)])?;
1146+
1147+
assert_eq!(result.as_number()?, 4.);
1148+
}
1149+
1150+
// Let's try with `NoDouble` as the generic parameter.
1151+
let do_math =
1152+
JSValue::new_function(&ctx, "do_math_with_no_double", Some(do_double::<NoDouble>));
1153+
let do_math_as_object = do_math.as_object()?;
1154+
1155+
// Correct call.
1156+
{
1157+
let result =
1158+
do_math_as_object.call_as_function(None, &[JSValue::new_number(&ctx, 2.)])?;
1159+
1160+
assert_eq!(result.as_number()?, 2.);
1161+
}
1162+
1163+
Ok(())
1164+
}
1165+
10941166
#[test]
10951167
fn json_boolean_true() {
10961168
let ctx = JSContext::default();

0 commit comments

Comments
 (0)