/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/script_bindings/cell.rs
277 строк
9 KB
Sam
script: Support `no_gc` in codegen, add `safe_borrow_mut` and use it in webgpu (#46050)
28 июн 2026, 22:42
Не верифицирован
28 июн 2026, 22:42
2bb4ff5
Код
Авторство
О чём код?
/* 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 https://mozilla.org/MPL/2.0/. */ //! A shareable mutable container for the DOM. use std::cell::{BorrowError, BorrowMutError}; pub use std::cell::{Ref, RefCell, RefMut}; use js::context::NoGC; use js::jsapi::JSTracer; use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOfOps}; use crate::CustomTraceable; use crate::assert::{assert_in_layout, assert_in_script}; /// A mutable field in the DOM. /// /// This extends the API of `std::cell::RefCell` to allow unsafe access in /// certain situations, with dynamic checking in debug builds. #[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)] pub struct DomRefCell<T> { value: RefCell<T>, } impl<T: MallocConditionalSizeOf> MallocConditionalSizeOf for DomRefCell<T> { fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.value.borrow().conditional_size_of(ops) } } // Functionality specific to Servo's `DomRefCell` type // =================================================== impl<T> DomRefCell<T> { /// Return a reference to the contents. For use in layout only. /// /// # Safety /// /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by /// this method is alive is undefined behaviour. /// /// # Panics /// /// Panics if this is called from anywhere other than the layout thread /// /// Panics if the value is currently mutably borrowed. #[expect(unsafe_code)] pub unsafe fn borrow_for_layout(&self) -> &T { assert_in_layout(); unsafe { self.value .try_borrow_unguarded() .expect("cell is mutably borrowed") } } /// Borrow the contents for the purpose of script deallocation. /// /// # Safety /// /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by /// this method is alive is undefined behaviour. /// /// # Panics /// /// Panics if this is called from anywhere other than the script thread. #[expect(unsafe_code)] #[allow(clippy::mut_from_ref)] pub unsafe fn borrow_for_script_deallocation(&self) -> &mut T { assert_in_script(); unsafe { &mut *self.value.as_ptr() } } /// Mutably borrow a cell for layout. Ideally this would use /// `RefCell::try_borrow_mut_unguarded` but that doesn't exist yet. /// /// # Safety /// /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by /// this method is alive is undefined behaviour. /// /// # Panics /// /// Panics if this is called from anywhere other than the layout thread. #[expect(unsafe_code)] #[allow(clippy::mut_from_ref)] pub unsafe fn borrow_mut_for_layout(&self) -> &mut T { assert_in_layout(); unsafe { &mut *self.value.as_ptr() } } /// Mutably borrows the wrapped value. /// /// The borrow lasts until the returned `RefMut` exits scope. The value /// cannot be borrowed while this borrow is active. /// /// By passing a `&NoGC` we statically prevent GC from being run while the borrow is active, /// to prevent panic when tracing (which calls `borrow`). /// /// # Example /// /// In simple cases one can use `NoGC` to statically ensure no GC can happen in the whole DOM method: /// /// ``` /// use js::context::{JSContext, NoGC}; /// use script_bindings::cell::DomRefCell; /// fn DomMethod(no_gc: &NoGC, cell: &DomRefCell<usize>) { /// let mut mutably_borrowed = cell.safe_borrow_mut(no_gc); /// } /// ``` /// /// But in more complex cases, method might trigger a GC, and thus require a `&mut JSContext`. /// In that case `&JSContext` can be used in place of `NoGC`, /// which will make `RefMut` bounded to the lifetime of the `&JSContext` /// and thus prevent any GC from happening while it is alive. /// /// ``` /// use js::context::{JSContext, NoGC}; /// use script_bindings::cell::DomRefCell; /// fn GC(cx: &mut JSContext) {} /// /// fn DomMethod(cell: &DomRefCell<usize>, cx: &mut JSContext) { /// { /// let mut mutably_borrowed = cell.safe_borrow_mut(cx); /// // do something with mutably_borrowed /// /// // only &JSContext is available here /// } // mutably_borrowed goes out of scope here /// // so one can now use &mut JSContext /// GC(cx); /// } /// ``` /// /// ```compile_fail /// use js::context::{JSContext, NoGC}; /// use script_bindings::cell::DomRefCell; /// fn GC(cx: &mut JSContext) {} /// /// fn DomMethod(cell: &DomRefCell<usize>, cx: &mut JSContext) { /// { /// let mut mutably_borrowed = cell.safe_borrow_mut(cx); /// // do something with mutably_borrowed /// /// // here one cannot use anything that might trigger a GC /// // as that would require &mut JSContext /// // but there is already existing &JSContext bounded at RefMut /// GC(cx); /// } // mutably_borrowed goes out of scope here /// } /// ``` /// /// # Panics /// /// Panics if the value is currently borrowed. #[track_caller] pub fn safe_borrow_mut<'a: 'r, 'no_cx: 'r, 'r>( &'a self, _no_gc: &'no_cx NoGC, ) -> RefMut<'r, T> { self.value.borrow_mut() } /// Mutably borrows the wrapped value. /// /// The borrow lasts until the returned `RefMut` exits scope. The value /// cannot be borrowed while this borrow is active. /// /// By passing a `&NoGC` we statically prevent GC from being run while the borrow is active, /// to prevent panic when tracing (which calls `borrow`). /// /// Returns `None` if the value is currently borrowed. pub fn safe_try_borrow_mut<'a: 'r, 'no_cx: 'r, 'r>( &'a self, _no_gc: &'no_cx NoGC, ) -> Result<RefMut<'r, T>, BorrowMutError> { self.value.try_borrow_mut() } } // Functionality duplicated with `std::cell::RefCell` // =================================================== impl<T> DomRefCell<T> { /// Create a new `DomRefCell` containing `value`. pub fn new(value: T) -> DomRefCell<T> { DomRefCell { value: RefCell::new(value), } } /// Immutably borrows the wrapped value. /// /// The borrow lasts until the returned `Ref` exits scope. Multiple /// immutable borrows can be taken out at the same time. /// /// # Panics /// /// Panics if the value is currently mutably borrowed. /// Panics if this is called from anywhere other than the script thread. /// Use borrow_for_layout if the borrowed data might used during layout. #[track_caller] pub fn borrow(&self) -> Ref<'_, T> { assert_in_script(); self.value.borrow() } /// Mutably borrows the wrapped value. /// /// The borrow lasts until the returned `RefMut` exits scope. The value /// cannot be borrowed while this borrow is active. /// /// # Panics /// /// Panics if the value is currently borrowed. /// Panics if this is called from anywhere other than the script thread. #[track_caller] pub fn borrow_mut(&self) -> RefMut<'_, T> { assert_in_script(); self.value.borrow_mut() } /// Attempts to immutably borrow the wrapped value. /// /// The borrow lasts until the returned `Ref` exits scope. Multiple /// immutable borrows can be taken out at the same time. /// /// Returns `None` if the value is currently mutably borrowed. /// /// # Panics /// /// Panics if this is called off the script thread. pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> { assert_in_script(); self.value.try_borrow() } /// Mutably borrows the wrapped value. /// /// The borrow lasts until the returned `RefMut` exits scope. The value /// cannot be borrowed while this borrow is active. /// /// Returns `None` if the value is currently borrowed. /// /// # Panics /// /// Panics if this is called off the script thread. pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> { assert_in_script(); self.value.try_borrow_mut() } } impl<T: Default> DomRefCell<T> { /// Takes the wrapped value, leaving `Default::default()` in its place. /// /// # Panics /// /// Panics if the value is currently borrowed. pub fn take(&self) -> T { self.value.take() } } unsafe impl<T: CustomTraceable> CustomTraceable for DomRefCell<T> { unsafe fn trace(&self, trc: *mut JSTracer) { unsafe { (*self).borrow().trace(trc) } } } unsafe impl<T: js::gc::Traceable> js::gc::Traceable for DomRefCell<T> { unsafe fn trace(&self, trc: *mut JSTracer) { unsafe { (*self).borrow().trace(trc) }; } }