From fa1578f2677aa90e0914c2f9642890f9e7275b74 Mon Sep 17 00:00:00 2001 From: Taym Haddadi Date: Wed, 10 Jul 2024 17:51:22 +0200 Subject: Create safe wrapper for JSFunctions (#32620) * Create safe wrapper for JSFunctions Signed-off-by: Bentaimia Haddadi * Add assert to check if the name ends in a null character Signed-off-by: Bentaimia Haddadi * Create macro to wrap unsafe extern "C" function calls Signed-off-by: Bentaimia Haddadi * Remove WRAPPER_FN Signed-off-by: Bentaimia Haddadi * Add macro example documentation Signed-off-by: Bentaimia Haddadi * Use C-string literals Signed-off-by: Bentaimia Haddadi * Ensure name is Cstr type Signed-off-by: Bentaimia Haddadi * Scope #[allow(unsafe_code)] Signed-off-by: Bentaimia Haddadi --------- Signed-off-by: Bentaimia Haddadi --- components/script/dom/bindings/function.rs | 48 ++++++++++++++ components/script/dom/bindings/mod.rs | 1 + components/script/dom/bytelengthqueuingstrategy.rs | 41 ++++-------- components/script/dom/countqueuingstrategy.rs | 74 ++++++---------------- 4 files changed, 81 insertions(+), 83 deletions(-) create mode 100644 components/script/dom/bindings/function.rs diff --git a/components/script/dom/bindings/function.rs b/components/script/dom/bindings/function.rs new file mode 100644 index 00000000000..a60e0d9f4ba --- /dev/null +++ b/components/script/dom/bindings/function.rs @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/// Defines a macro `native_fn!` to create a JavaScript function from a Rust function pointer. +/// # Example +/// ``` +/// let js_function: Rc = native_fn!(my_rust_function, c"myFunction", 2, 0); +/// ``` +#[macro_export] +macro_rules! native_fn { + ($call:expr, $name:expr, $nargs:expr, $flags:expr) => {{ + let cx = crate::dom::types::GlobalScope::get_cx(); + let fun_obj = crate::native_raw_obj_fn!(cx, $call, $name, $nargs, $flags); + #[allow(unsafe_code)] + unsafe { + Function::new(cx, fun_obj) + } + }}; +} + +/// Defines a macro `native_raw_obj_fn!` to create a raw JavaScript function object. +/// # Example +/// ``` +/// let raw_function_obj: *mut JSObject = native_raw_obj_fn!(cx, my_rust_function, c"myFunction", 2, 0); +/// ``` +#[macro_export] +macro_rules! native_raw_obj_fn { + ($cx:expr, $call:expr, $name:expr, $nargs:expr, $flags:expr) => {{ + #[allow(unsafe_code)] + unsafe extern "C" fn wrapper(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool { + $call(cx, argc, vp) + } + #[allow(unsafe_code)] + unsafe { + let name: &std::ffi::CStr = $name; + let raw_fun = crate::dom::bindings::import::module::jsapi::JS_NewFunction( + *$cx, + Some(wrapper), + $nargs, + $flags, + name.as_ptr() as *const std::ffi::c_char, + ); + assert!(!raw_fun.is_null()); + crate::dom::bindings::import::module::jsapi::JS_GetFunctionObject(raw_fun) + } + }}; +} diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index e31d79cc494..81b3b403947 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -141,6 +141,7 @@ pub mod constant; pub mod conversions; pub mod error; pub mod finalize; +pub mod function; pub mod guard; pub mod htmlconstructor; pub mod import; diff --git a/components/script/dom/bytelengthqueuingstrategy.rs b/components/script/dom/bytelengthqueuingstrategy.rs index 07cdcbef710..88f65c363bf 100644 --- a/components/script/dom/bytelengthqueuingstrategy.rs +++ b/components/script/dom/bytelengthqueuingstrategy.rs @@ -2,12 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::ffi::c_char; use std::rc::Rc; use dom_struct::dom_struct; use js::gc::{HandleValue, MutableHandleValue}; -use js::jsapi::{CallArgs, JSContext, JS_GetFunctionObject, JS_NewFunction}; +use js::jsapi::{CallArgs, JSContext}; use js::jsval::JSVal; use js::rust::HandleObject; @@ -15,11 +14,11 @@ use super::bindings::codegen::Bindings::FunctionBinding::Function; use super::bindings::codegen::Bindings::QueuingStrategyBinding::{ ByteLengthQueuingStrategyMethods, QueuingStrategyInit, }; -use super::bindings::import::module::{ - get_dictionary_property, DomObject, DomRoot, Fallible, Reflector, -}; +use super::bindings::import::module::{DomObject, DomRoot, Fallible, Reflector}; use super::bindings::reflector::reflect_dom_object_with_proto; use super::types::GlobalScope; +use crate::dom::bindings::import::module::get_dictionary_property; +use crate::native_fn; #[dom_struct] pub struct ByteLengthQueuingStrategy { @@ -56,11 +55,9 @@ impl ByteLengthQueuingStrategyMethods for ByteLengthQueuingStrategy { self.high_water_mark } - #[allow(unsafe_code)] /// fn GetSize(&self) -> Fallible> { let global = self.reflector_.global(); - let cx = GlobalScope::get_cx(); // Return this's relevant global object's byte length queuing strategy // size function. if let Some(fun) = global.get_byte_length_queuing_strategy_size() { @@ -70,32 +67,20 @@ impl ByteLengthQueuingStrategyMethods for ByteLengthQueuingStrategy { // Step 1. Let steps be the following steps, given chunk // Note: See ByteLengthQueuingStrategySize instead. - unsafe { - // Step 2. Let F be !CreateBuiltinFunction(steps, 1, "size", « », - // globalObject’s relevant Realm). - let raw_fun = JS_NewFunction( - *cx, - Some(byte_length_queuing_strategy_size), - 1, - 0, - b"size\0".as_ptr() as *const c_char, - ); - assert!(!raw_fun.is_null()); - - // Step 3. Set globalObject’s byte length queuing strategy size function to - // a Function that represents a reference to F, - // with callback context equal to globalObject’s relevant settings object. - let fun_obj = JS_GetFunctionObject(raw_fun); - let fun = Function::new(cx, fun_obj); - global.set_byte_length_queuing_strategy_size(fun.clone()); - Ok(fun) - } + // Step 2. Let F be !CreateBuiltinFunction(steps, 1, "size", « », + // globalObject’s relevant Realm). + let fun = native_fn!(byte_length_queuing_strategy_size, c"size", 1, 0); + // Step 3. Set globalObject’s byte length queuing strategy size function to + // a Function that represents a reference to F, + // with callback context equal to globalObject’s relevant settings object. + global.set_byte_length_queuing_strategy_size(fun.clone()); + Ok(fun) } } /// #[allow(unsafe_code)] -unsafe extern "C" fn byte_length_queuing_strategy_size( +pub unsafe fn byte_length_queuing_strategy_size( cx: *mut JSContext, argc: u32, vp: *mut JSVal, diff --git a/components/script/dom/countqueuingstrategy.rs b/components/script/dom/countqueuingstrategy.rs index 96b8bd699c1..01090b9a6c6 100644 --- a/components/script/dom/countqueuingstrategy.rs +++ b/components/script/dom/countqueuingstrategy.rs @@ -2,11 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::ffi::c_char; use std::rc::Rc; use dom_struct::dom_struct; -use js::jsapi::{CallArgs, JSContext, JS_GetFunctionObject, JS_NewFunction}; +use js::jsapi::{CallArgs, JSContext}; use js::jsval::{Int32Value, JSVal}; use js::rust::HandleObject; @@ -16,7 +15,9 @@ use super::bindings::codegen::Bindings::QueuingStrategyBinding::{ }; use super::bindings::import::module::{DomObject, DomRoot, Error, Fallible, Reflector}; use super::bindings::reflector::reflect_dom_object_with_proto; +use super::bytelengthqueuingstrategy::byte_length_queuing_strategy_size; use super::types::GlobalScope; +use crate::{native_fn, native_raw_obj_fn}; #[dom_struct] pub struct CountQueuingStrategy { @@ -53,51 +54,32 @@ impl CountQueuingStrategyMethods for CountQueuingStrategy { self.high_water_mark } - #[allow(unsafe_code)] /// fn GetSize(&self) -> Fallible> { let global = self.reflector_.global(); - let cx = GlobalScope::get_cx(); // Return this's relevant global object's count queuing strategy // size function. if let Some(fun) = global.get_count_queuing_strategy_size() { return Ok(fun); } - // Step 1. Let steps be the following steps: - // Note: See count_queuing_strategy_size instead. - - unsafe { - // Step 2. Let F be - // ! CreateBuiltinFunction(steps, 0, "size", « », - // globalObject’s relevant Realm). - let raw_fun = JS_NewFunction( - *cx, - Some(count_queuing_strategy_size), - 0, - 0, - b"size\0".as_ptr() as *const c_char, - ); - assert!(!raw_fun.is_null()); - - // Step 3. Set globalObject’s count queuing strategy size function to - // a Function that represents a reference to F, - // with callback context equal to globalObject’s relevant settings object. - let fun_obj = JS_GetFunctionObject(raw_fun); - let fun = Function::new(cx, fun_obj); - global.set_count_queuing_strategy_size(fun.clone()); - Ok(fun) - } + // Step 1. Let steps be the following steps, given chunk + // Note: See ByteLengthQueuingStrategySize instead. + + // Step 2. Let F be !CreateBuiltinFunction(steps, 1, "size", « », + // globalObject’s relevant Realm). + let fun = native_fn!(byte_length_queuing_strategy_size, c"size", 0, 0); + // Step 3. Set globalObject’s count queuing strategy size function to + // a Function that represents a reference to F, + // with callback context equal to globalObject’s relevant settings object. + global.set_count_queuing_strategy_size(fun.clone()); + Ok(fun) } } /// #[allow(unsafe_code)] -unsafe extern "C" fn count_queuing_strategy_size( - _cx: *mut JSContext, - argc: u32, - vp: *mut JSVal, -) -> bool { +pub unsafe fn count_queuing_strategy_size(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool { let args = CallArgs::from_vp(vp, argc); // Step 1.1. Return 1. args.rval().set(Int32Value(1)); @@ -129,30 +111,12 @@ pub fn extract_high_water_mark(strategy: &QueuingStrategy, default_hwm: f64) -> /// pub fn extract_size_algorithm(strategy: &QueuingStrategy) -> Rc { if strategy.size.is_none() { - #[allow(unsafe_code)] - unsafe extern "C" fn fallback_strategy_size( - _cx: *mut JSContext, - argc: u32, - vp: *mut JSVal, - ) -> bool { - let args = CallArgs::from_vp(vp, argc); - args.rval().set(Int32Value(1)); - true - } + let cx = GlobalScope::get_cx(); + let fun_obj = native_raw_obj_fn!(cx, count_queuing_strategy_size, c"size", 0, 0); #[allow(unsafe_code)] unsafe { - let cx = GlobalScope::get_cx(); - let raw_fun = JS_NewFunction( - *cx, - Some(fallback_strategy_size), - 0, - 0, - b"size\0".as_ptr() as *const c_char, - ); - assert!(!raw_fun.is_null()); - let fun_obj = JS_GetFunctionObject(raw_fun); - return QueuingStrategySize::new(cx, fun_obj).clone(); - } + QueuingStrategySize::new(cx, fun_obj).clone() + }; } strategy.size.as_ref().unwrap().clone() } -- cgit v1.2.3