Skip to content

Commit 67d7eb1

Browse files
committed
feat: Implement JSValue::new_function.
This patch implements `JSValue::new_function`. In addition, it creates a new `javascriptcore_macros` crate, to expose a `function_callback` procedural macro. It's helpful to easily create a function callback.
1 parent d271add commit 67d7eb1

File tree

11 files changed

+313
-52
lines changed

11 files changed

+313
-52
lines changed

Diff for: Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["javascriptcore-sys"]
2+
members = ["javascriptcore-sys", "javascriptcore-macros"]
33

44
[package]
55
name = "javascriptcore"
@@ -17,4 +17,5 @@ categories = ["api-bindings"]
1717
exclude = ["javascript_core/**"]
1818

1919
[dependencies]
20+
javascriptcore-macros = { path = "javascriptcore-macros", version = "0.0.1" }
2021
javascriptcore-sys = { path = "javascriptcore-sys", version = "0.0.5" }

Diff for: javascriptcore-macros/Cargo.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "javascriptcore-macros"
3+
version = "0.0.1"
4+
edition = "2021"
5+
authors = ["Bruce Mitchener <[email protected]>", "Ivan Enderlin <[email protected]>"]
6+
license = "MIT OR Apache-2.0"
7+
description = "Procedural macros for the `javascriptcore` crate."
8+
keywords = ["javascript", "jsc", "scripting"]
9+
documentation = "https://docs.rs/javascriptcore-sys"
10+
homepage = "https://github.com/endoli/javascriptcore.rs"
11+
repository = "https://github.com/endoli/javascriptcore.rs"
12+
categories = ["external-ffi-bindings"]
13+
14+
[lib]
15+
proc-macro = true
16+
17+
[dependencies]
18+
quote = "1.0.33"
19+
syn = { version = "2.0.38", features = ["full"] }

Diff for: javascriptcore-macros/src/lib.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use proc_macro::TokenStream;
2+
use quote::quote;
3+
4+
#[proc_macro_attribute]
5+
pub fn function_callback(_attributes: TokenStream, item: TokenStream) -> TokenStream {
6+
let function = syn::parse::<syn::ItemFn>(item)
7+
.expect("#[function_callback] must apply on a valid function");
8+
let function_name = &function.sig.ident;
9+
10+
quote! {
11+
unsafe extern "C" fn #function_name(
12+
__raw_ctx: javascriptcore_sys::JSContextRef,
13+
__raw_function: javascriptcore_sys::JSObjectRef,
14+
__raw_this_object: javascriptcore_sys::JSObjectRef,
15+
__raw_argument_count: usize,
16+
__raw_arguments: *const javascriptcore_sys::JSValueRef,
17+
__raw_exception: *mut javascriptcore_sys::JSValueRef,
18+
) -> *const javascriptcore_sys::OpaqueJSValue {
19+
use core::{mem, ptr, slice};
20+
use javascriptcore::{JSContext, JSObject, JSValue};
21+
22+
let __ctx = JSContext::from_raw(__raw_ctx as *mut _);
23+
let __function = JSObject::from_raw(__raw_ctx, __raw_function);
24+
let __this_object = JSObject::from_raw(__raw_ctx, __raw_this_object);
25+
26+
let __function = if __raw_function.is_null() {
27+
None
28+
} else {
29+
Some(&__function)
30+
};
31+
32+
let __this_object = if __raw_this_object.is_null() {
33+
None
34+
} else {
35+
Some(&__this_object)
36+
};
37+
38+
let __arguments = if __raw_argument_count == 0 {
39+
Vec::new()
40+
} else {
41+
unsafe { slice::from_raw_parts(__raw_arguments, __raw_argument_count) }
42+
.iter()
43+
.map(|value| JSValue::from_raw(__raw_ctx, *value))
44+
.collect::<Vec<_>>()
45+
};
46+
47+
#function
48+
49+
let func: fn(
50+
&JSContext,
51+
Option<&JSObject>,
52+
Option<&JSObject>,
53+
arguments: &[JSValue],
54+
) -> Result<JSValue, JSException> = #function_name;
55+
let result = func(&__ctx, __function, __this_object, __arguments.as_slice());
56+
57+
mem::forget(__ctx);
58+
59+
match result {
60+
Ok(value) => value.into(),
61+
Err(exception) => {
62+
let raw_exception: javascriptcore_sys::JSValueRef = exception.into();
63+
*__raw_exception = raw_exception as *mut _;
64+
65+
ptr::null()
66+
}
67+
}
68+
}
69+
}
70+
.into()
71+
}

Diff for: src/base.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ pub fn evaluate_script<S: Into<JSString>, U: Into<JSString>>(
5252
);
5353

5454
if result.is_null() {
55-
Err(JSValue::new_inner(ctx.raw, exception).into())
55+
Err(JSValue::from_raw(ctx.raw, exception).into())
5656
} else {
57-
Ok(JSValue::new_inner(ctx.raw, result))
57+
Ok(JSValue::from_raw(ctx.raw, result))
5858
}
5959
}
6060
}
@@ -103,7 +103,7 @@ pub fn check_script_syntax<S: Into<JSString>, U: Into<JSString>>(
103103
if result {
104104
Ok(())
105105
} else {
106-
Err(JSValue::new_inner(ctx.raw, exception).into())
106+
Err(JSValue::from_raw(ctx.raw, exception).into())
107107
}
108108
}
109109
}

Diff for: src/context.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ use std::ptr;
1111

1212
impl JSContext {
1313
/// Create a new [`Self`] from its raw pointer directly.
14-
pub(crate) fn new_inner(raw: sys::JSGlobalContextRef) -> Self {
14+
///
15+
/// # Safety
16+
///
17+
/// Ensure `raw` is valid.
18+
pub unsafe fn from_raw(raw: sys::JSGlobalContextRef) -> Self {
1519
Self { raw }
1620
}
1721

@@ -41,7 +45,7 @@ impl JSContext {
4145
/// * `global_object_class`: The class to use when creating the global
4246
/// object.
4347
pub fn new_with_class(global_object_class: &JSClass) -> Self {
44-
Self::new_inner(unsafe { sys::JSGlobalContextCreate(global_object_class.raw) })
48+
unsafe { Self::from_raw(sys::JSGlobalContextCreate(global_object_class.raw)) }
4549
}
4650

4751
/// Gets the context group to which a JavaScript execution context belongs.
@@ -107,9 +111,9 @@ impl JSContext {
107111
let global_object = unsafe { JSContextGetGlobalObject(self.raw) };
108112

109113
if global_object.is_null() {
110-
Err(JSValue::new_inner(self.raw, global_object).into())
114+
Err(unsafe { JSValue::from_raw(self.raw, global_object) }.into())
111115
} else {
112-
Ok(JSObject::new_inner(self.raw, global_object))
116+
Ok(unsafe { JSObject::from_raw(self.raw, global_object) })
113117
}
114118
}
115119
}
@@ -125,7 +129,7 @@ impl Default for JSContext {
125129
/// However, you may not use values created in the context in other
126130
/// contexts.
127131
fn default() -> Self {
128-
Self::new_inner(unsafe { sys::JSGlobalContextCreate(ptr::null_mut()) })
132+
unsafe { Self::from_raw(sys::JSGlobalContextCreate(ptr::null_mut())) }
129133
}
130134
}
131135

Diff for: src/contextgroup.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ impl JSContextGroup {
2222
///
2323
/// The created global context retains this group.
2424
pub fn new_context(&self) -> JSContext {
25-
JSContext::new_inner(unsafe {
26-
sys::JSGlobalContextCreateInGroup(self.raw, ptr::null_mut())
27-
})
25+
unsafe { JSContext::from_raw(sys::JSGlobalContextCreateInGroup(self.raw, ptr::null_mut())) }
2826
}
2927

3028
/// Creates a global JavaScript execution context in this context
@@ -39,9 +37,12 @@ impl JSContextGroup {
3937
/// * `global_object_class`: The class to use when creating the global
4038
/// object.
4139
pub fn new_context_with_class(&self, global_object_class: &JSClass) -> JSContext {
42-
JSContext::new_inner(unsafe {
43-
sys::JSGlobalContextCreateInGroup(self.raw, global_object_class.raw)
44-
})
40+
unsafe {
41+
JSContext::from_raw(sys::JSGlobalContextCreateInGroup(
42+
self.raw,
43+
global_object_class.raw,
44+
))
45+
}
4546
}
4647
}
4748

Diff for: src/exception.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// option. This file may not be copied, modified, or distributed
55
// except according to those terms.
66

7-
use crate::{JSException, JSString, JSValue};
7+
use crate::{sys, JSException, JSString, JSValue};
88

99
impl JSException {
1010
/// Return the underlying value backing the exception.
@@ -28,3 +28,9 @@ impl From<JSValue> for JSException {
2828
Self { value }
2929
}
3030
}
31+
32+
impl From<JSException> for sys::JSValueRef {
33+
fn from(value: JSException) -> Self {
34+
value.value.raw
35+
}
36+
}

Diff for: src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
unused_qualifications
1818
)]
1919

20-
extern crate javascriptcore_sys as sys;
20+
pub use javascriptcore_macros::function_callback;
21+
use javascriptcore_sys as sys;
2122

2223
mod base;
2324
mod class;

Diff for: src/object.rs

+15-10
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ use std::ops::Deref;
99
use std::ptr;
1010

1111
impl JSObject {
12-
pub(crate) fn new_inner(ctx: sys::JSContextRef, raw: sys::JSObjectRef) -> Self {
12+
/// Create a new [`Self`] from its raw pointer directly.
13+
///
14+
/// # Safety
15+
///
16+
/// Ensure `raw` is valid.
17+
pub unsafe fn from_raw(ctx: sys::JSContextRef, raw: sys::JSObjectRef) -> Self {
1318
Self {
1419
raw,
15-
value: JSValue::new_inner(ctx, raw),
20+
value: JSValue::from_raw(ctx, raw),
1621
}
1722
}
1823

@@ -87,7 +92,7 @@ impl JSObject {
8792
sys::JSObjectGetProperty(self.value.ctx, self.raw, name.into().raw, &mut exception)
8893
};
8994

90-
JSValue::new_inner(self.value.ctx, value)
95+
unsafe { JSValue::from_raw(self.value.ctx, value) }
9196
}
9297

9398
/// Gets a property from an object by numeric index.
@@ -136,7 +141,7 @@ impl JSObject {
136141
sys::JSObjectGetPropertyAtIndex(self.value.ctx, self.raw, index, &mut exception)
137142
};
138143

139-
JSValue::new_inner(self.value.ctx, value)
144+
unsafe { JSValue::from_raw(self.value.ctx, value) }
140145
}
141146

142147
/// Set a property onto an object.
@@ -174,7 +179,7 @@ impl JSObject {
174179
}
175180

176181
if !exception.is_null() {
177-
return Err(JSValue::new_inner(context, exception).into());
182+
return Err(unsafe { JSValue::from_raw(context, exception) }.into());
178183
}
179184

180185
Ok(())
@@ -209,7 +214,7 @@ impl JSObject {
209214
}
210215

211216
if !exception.is_null() {
212-
return Err(JSValue::new_inner(context, exception).into());
217+
return Err(unsafe { JSValue::from_raw(context, exception) }.into());
213218
}
214219

215220
Ok(())
@@ -247,7 +252,7 @@ impl JSObject {
247252
};
248253

249254
if !exception.is_null() {
250-
return Err(JSValue::new_inner(context, exception).into());
255+
return Err(unsafe { JSValue::from_raw(context, exception) }.into());
251256
}
252257

253258
if result.is_null() {
@@ -258,7 +263,7 @@ impl JSObject {
258263
.into());
259264
}
260265

261-
Ok(JSValue::new_inner(context, result))
266+
Ok(unsafe { JSValue::from_raw(context, result) })
262267
}
263268

264269
/// Call this object considering it is a valid function.
@@ -301,7 +306,7 @@ impl JSObject {
301306
};
302307

303308
if !exception.is_null() {
304-
return Err(JSValue::new_inner(context, exception).into());
309+
return Err(unsafe { JSValue::from_raw(context, exception).into() });
305310
}
306311

307312
if result.is_null() {
@@ -312,7 +317,7 @@ impl JSObject {
312317
.into());
313318
}
314319

315-
Ok(JSValue::new_inner(context, result))
320+
Ok(unsafe { JSValue::from_raw(context, result) })
316321
}
317322
}
318323

0 commit comments

Comments
 (0)