diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/script/dom/bindings/conversions.rs | 45 | ||||
-rw-r--r-- | components/script/dom/bindings/inheritance.rs | 52 | ||||
-rw-r--r-- | components/script/dom/bindings/mod.rs | 16 | ||||
-rw-r--r-- | components/script/dom/bindings/utils.rs | 45 | ||||
-rw-r--r-- | components/script/dom/node.rs | 18 | ||||
-rw-r--r-- | components/script/layout_dom/node.rs | 4 | ||||
-rw-r--r-- | components/script/script_runtime.rs | 17 | ||||
-rw-r--r-- | components/script_bindings/Cargo.toml | 1 | ||||
-rw-r--r-- | components/script_bindings/codegen/CodegenRust.py | 109 | ||||
-rw-r--r-- | components/script_bindings/codegen/run.py | 2 | ||||
-rw-r--r-- | components/script_bindings/conversions.rs | 52 | ||||
-rw-r--r-- | components/script_bindings/inheritance.rs | 53 | ||||
-rw-r--r-- | components/script_bindings/lib.rs | 18 | ||||
-rw-r--r-- | components/script_bindings/script_runtime.rs | 17 | ||||
-rw-r--r-- | components/script_bindings/utils.rs | 48 |
16 files changed, 291 insertions, 207 deletions
diff --git a/Cargo.lock b/Cargo.lock index e325ff78acf..f60b7c585c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6321,6 +6321,7 @@ dependencies = [ name = "script_bindings" version = "0.0.1" dependencies = [ + "bitflags 2.8.0", "cssparser", "html5ever", "jstraceable_derive", diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 35a2dd1fc47..a0ce1eb70fe 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -42,15 +42,11 @@ use js::glue::{GetProxyReservedSlot, IsWrapper, JS_GetReservedSlot, UnwrapObject use js::jsapi::{Heap, IsWindowProxy, JSContext, JSObject, JS_IsExceptionPending}; use js::jsval::UndefinedValue; use js::rust::wrappers::{IsArrayObject, JS_GetProperty, JS_HasProperty}; -use js::rust::{ - get_object_class, is_dom_class, is_dom_object, HandleId, HandleObject, HandleValue, - MutableHandleValue, -}; +use js::rust::{is_dom_object, HandleId, HandleObject, HandleValue, MutableHandleValue}; use num_traits::Float; pub(crate) use script_bindings::conversions::*; use crate::dom::bindings::error::{Error, Fallible}; -use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::DomRoot; @@ -64,15 +60,6 @@ use crate::dom::htmloptionscollection::HTMLOptionsCollection; use crate::dom::nodelist::NodeList; use crate::dom::windowproxy::WindowProxy; -/// A trait to check whether a given `JSObject` implements an IDL interface. -pub(crate) trait IDLInterface { - /// Returns whether the given DOM class derives that interface. - fn derives(_: &'static DOMClass) -> bool; -} - -/// A trait to mark an IDL interface as deriving from another one. -pub(crate) trait DerivedFrom<T: Castable>: Castable {} - impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { #[inline] unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { @@ -169,14 +156,7 @@ pub(crate) unsafe fn jsid_to_string(cx: *mut JSContext, id: HandleId) -> Option< None } -/// Returns whether `obj` is a DOM object implemented as a proxy. -pub(crate) fn is_dom_proxy(obj: *mut JSObject) -> bool { - use js::glue::IsProxyHandlerFamily; - unsafe { - let clasp = get_object_class(obj); - ((*clasp).flags & js::JSCLASS_IS_PROXY) != 0 && IsProxyHandlerFamily(obj) - } -} +pub(crate) use script_bindings::conversions::is_dom_proxy; /// The index of the slot wherein a pointer to the reflected DOM object is /// stored for non-proxy bindings. @@ -200,27 +180,6 @@ pub(crate) unsafe fn private_from_object(obj: *mut JSObject) -> *const libc::c_v } } -/// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object. -pub(crate) unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> { - use js::glue::GetProxyHandlerExtra; - - use crate::dom::bindings::utils::DOMJSClass; - - let clasp = get_object_class(obj); - if is_dom_class(&*clasp) { - trace!("plain old dom object"); - let domjsclass: *const DOMJSClass = clasp as *const DOMJSClass; - return Ok(&(*domjsclass).dom_class); - } - if is_dom_proxy(obj) { - trace!("proxy dom object"); - let dom_class: *const DOMClass = GetProxyHandlerExtra(obj) as *const DOMClass; - return Ok(&*dom_class); - } - trace!("not a dom object"); - Err(()) -} - pub(crate) enum PrototypeCheck { Derive(fn(&'static DOMClass) -> bool), Depth { depth: usize, proto_id: u16 }, diff --git a/components/script/dom/bindings/inheritance.rs b/components/script/dom/bindings/inheritance.rs index f226dbb617b..ef60378d55b 100644 --- a/components/script/dom/bindings/inheritance.rs +++ b/components/script/dom/bindings/inheritance.rs @@ -2,56 +2,8 @@ * 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/. */ -//! The `Castable` trait. - -use std::mem; - -pub(crate) use crate::dom::bindings::codegen::InheritTypes::*; -use crate::dom::bindings::conversions::{get_dom_class, DerivedFrom, IDLInterface}; -use crate::dom::bindings::reflector::DomObject; -use crate::script_runtime::runtime_is_alive; - -/// A trait to hold the cast functions of IDL interfaces that either derive -/// or are derived from other interfaces. -pub(crate) trait Castable: IDLInterface + DomObject + Sized { - /// Check whether a DOM object implements one of its deriving interfaces. - fn is<T>(&self) -> bool - where - T: DerivedFrom<Self>, - { - // This is a weird place for this check to live, but it should catch any - // attempts to interact with DOM objects from Drop implementations that run - // as a result of the runtime shutting down and finalizing all remaining objects. - debug_assert!( - runtime_is_alive(), - "Attempting to interact with DOM objects after JS runtime has shut down." - ); - - let class = unsafe { get_dom_class(self.reflector().get_jsobject().get()).unwrap() }; - T::derives(class) - } - - /// Cast a DOM object upwards to one of the interfaces it derives from. - fn upcast<T>(&self) -> &T - where - T: Castable, - Self: DerivedFrom<T>, - { - unsafe { mem::transmute::<&Self, &T>(self) } - } - - /// Cast a DOM object downwards to one of the interfaces it might implement. - fn downcast<T>(&self) -> Option<&T> - where - T: DerivedFrom<Self>, - { - if self.is::<T>() { - Some(unsafe { mem::transmute::<&Self, &T>(self) }) - } else { - None - } - } -} +pub(crate) use script_bindings::codegen::InheritTypes::*; +pub(crate) use script_bindings::inheritance::Castable; #[allow(missing_docs)] pub(crate) trait HasParent { diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 65629fda46b..ee934ca2af1 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -184,15 +184,17 @@ pub(crate) mod codegen { } pub(crate) mod InterfaceObjectMap { include!(concat!(env!("BINDINGS_OUT_DIR"), "/InterfaceObjectMap.rs")); + pub(crate) use script_bindings::codegen::Globals::Globals; } - #[allow(dead_code, unused_imports, clippy::enum_variant_names)] - pub(crate) mod InheritTypes { - include!(concat!(env!("BINDINGS_OUT_DIR"), "/InheritTypes.rs")); - } - #[allow(clippy::upper_case_acronyms)] - pub(crate) mod PrototypeList { - include!(concat!(env!("BINDINGS_OUT_DIR"), "/PrototypeList.rs")); + pub(crate) use script_bindings::codegen::InheritTypes; + #[allow(dead_code)] + pub(crate) mod ConcreteInheritTypes { + include!(concat!( + env!("BINDINGS_OUT_DIR"), + "/ConcreteInheritTypes.rs" + )); } + pub(crate) use script_bindings::codegen::PrototypeList; pub(crate) mod RegisterBindings { include!(concat!(env!("BINDINGS_OUT_DIR"), "/RegisterBindings.rs")); } diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index da583b2881b..7567ee00f78 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -5,7 +5,7 @@ //! Various utilities to glue JavaScript and the DOM implementation together. use std::ffi::CString; -use std::os::raw::{c_char, c_void}; +use std::os::raw::c_char; use std::ptr::NonNull; use std::sync::OnceLock; use std::{ptr, slice, str}; @@ -35,15 +35,13 @@ use js::rust::{ MutableHandleValue, ToString, }; use js::JS_CALLEE; -use malloc_size_of::MallocSizeOfOps; -use crate::dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH}; -use crate::dom::bindings::codegen::{InterfaceObjectMap, PrototypeList}; +use crate::dom::bindings::codegen::InterfaceObjectMap; +use crate::dom::bindings::codegen::PrototypeList::PROTO_OR_IFACE_LENGTH; use crate::dom::bindings::conversions::{ jsstring_to_str, private_from_proto_check, PrototypeCheck, }; use crate::dom::bindings::error::throw_invalid_this; -use crate::dom::bindings::inheritance::TopTypeId; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::trace_object; use crate::dom::windowproxy::WindowProxyHandler; @@ -111,42 +109,7 @@ pub(crate) const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT; // changes. pub(crate) const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1; -/// The struct that holds inheritance information for DOM object reflectors. -#[derive(Clone, Copy)] -pub(crate) struct DOMClass { - /// A list of interfaces that this object implements, in order of decreasing - /// derivedness. - pub(crate) interface_chain: [PrototypeList::ID; MAX_PROTO_CHAIN_LENGTH], - - /// The last valid index of `interface_chain`. - pub(crate) depth: u8, - - /// The type ID of that interface. - pub(crate) type_id: TopTypeId, - - /// The MallocSizeOf function wrapper for that interface. - pub(crate) malloc_size_of: unsafe fn(ops: &mut MallocSizeOfOps, *const c_void) -> usize, - - /// The `Globals` flag for this global interface, if any. - pub(crate) global: InterfaceObjectMap::Globals, -} -unsafe impl Sync for DOMClass {} - -/// The JSClass used for DOM object reflectors. -#[derive(Copy)] -#[repr(C)] -pub(crate) struct DOMJSClass { - /// The actual JSClass. - pub(crate) base: js::jsapi::JSClass, - /// Associated data for DOM object reflectors. - pub(crate) dom_class: DOMClass, -} -impl Clone for DOMJSClass { - fn clone(&self) -> DOMJSClass { - *self - } -} -unsafe impl Sync for DOMJSClass {} +pub(crate) use script_bindings::utils::{DOMClass, DOMJSClass}; /// Returns a JSVal representing the frozen JavaScript array pub(crate) fn to_frozen_array<T: ToJSValConvertible>( diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 709c2d39936..c23eaa6f680 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -3802,21 +3802,25 @@ impl UniqueId { } } -impl From<NodeTypeId> for LayoutNodeType { +pub(crate) struct NodeTypeIdWrapper(pub(crate) NodeTypeId); + +impl From<NodeTypeIdWrapper> for LayoutNodeType { #[inline(always)] - fn from(node_type: NodeTypeId) -> LayoutNodeType { - match node_type { - NodeTypeId::Element(e) => LayoutNodeType::Element(e.into()), + fn from(node_type: NodeTypeIdWrapper) -> LayoutNodeType { + match node_type.0 { + NodeTypeId::Element(e) => LayoutNodeType::Element(ElementTypeIdWrapper(e).into()), NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text, x => unreachable!("Layout should not traverse nodes of type {:?}", x), } } } -impl From<ElementTypeId> for LayoutElementType { +struct ElementTypeIdWrapper(ElementTypeId); + +impl From<ElementTypeIdWrapper> for LayoutElementType { #[inline(always)] - fn from(element_type: ElementTypeId) -> LayoutElementType { - match element_type { + fn from(element_type: ElementTypeIdWrapper) -> LayoutElementType { + match element_type.0 { ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => { LayoutElementType::HTMLBodyElement }, diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs index 1d9d5310a3b..841a396fc5f 100644 --- a/components/script/layout_dom/node.rs +++ b/components/script/layout_dom/node.rs @@ -36,7 +36,7 @@ use crate::dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId, TextTyp use crate::dom::bindings::root::LayoutDom; use crate::dom::characterdata::LayoutCharacterDataHelpers; use crate::dom::element::{Element, LayoutElementHelpers}; -use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags}; +use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags, NodeTypeIdWrapper}; use crate::dom::text::Text; /// A wrapper around a `LayoutDom<Node>` which provides a safe interface that @@ -181,7 +181,7 @@ impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> { } fn type_id(&self) -> LayoutNodeType { - self.script_type_id().into() + NodeTypeIdWrapper(self.script_type_id()).into() } unsafe fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) { diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 8ecf70a9f3b..28e7869bd4f 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -53,6 +53,7 @@ use malloc_size_of_derive::MallocSizeOf; use profile_traits::mem::{Report, ReportKind}; use profile_traits::path; use profile_traits::time::ProfilerCategory; +use script_bindings::script_runtime::{mark_runtime_dead, runtime_is_alive}; use servo_config::{opts, pref}; use style::thread_state::{self, ThreadState}; @@ -768,10 +769,8 @@ impl Drop for Runtime { unsafe { DeleteJobQueue(self.job_queue); } - THREAD_ACTIVE.with(|t| { - LiveDOMReferences::destruct(); - t.set(false); - }); + LiveDOMReferences::destruct(); + mark_runtime_dead(); } } @@ -892,17 +891,9 @@ unsafe extern "C" fn debug_gc_callback( } } -thread_local!( - static THREAD_ACTIVE: Cell<bool> = const { Cell::new(true) }; -); - -pub(crate) fn runtime_is_alive() -> bool { - THREAD_ACTIVE.with(|t| t.get()) -} - #[allow(unsafe_code)] unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, _data: *mut os::raw::c_void) { - if !THREAD_ACTIVE.with(|t| t.get()) { + if !runtime_is_alive() { return; } trace!("starting custom root handler"); diff --git a/components/script_bindings/Cargo.toml b/components/script_bindings/Cargo.toml index 88a68587b99..4da26f81160 100644 --- a/components/script_bindings/Cargo.toml +++ b/components/script_bindings/Cargo.toml @@ -21,6 +21,7 @@ phf_shared = "0.11" serde_json = { workspace = true } [dependencies] +bitflags = { workspace = true } cssparser = { workspace = true } html5ever = { workspace = true } js = { workspace = true } diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index de4adafe886..66a6b84848a 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -6982,7 +6982,7 @@ class CGNonNamespacedEnum(CGThing): # Build the enum body. joinedEntries = ',\n'.join(entries) - enumstr = f"{comment}pub(crate) enum {enumName} {{\n{joinedEntries}\n}}\n" + enumstr = f"{comment}pub enum {enumName} {{\n{joinedEntries}\n}}\n" if repr: enumstr = f"#[repr({repr})]\n{enumstr}" if deriving: @@ -8211,14 +8211,7 @@ class GlobalGenRoots(): """ @staticmethod - def InterfaceObjectMap(config): - mods = [ - "crate::dom::bindings::codegen", - "crate::script_runtime::JSContext", - "js::rust::HandleObject", - ] - imports = CGList([CGGeneric(f"use {mod};") for mod in mods], "\n") - + def Globals(config): global_descriptors = config.getDescriptors(isGlobal=True) flags = [("EMPTY", 0)] flags.extend( @@ -8228,14 +8221,28 @@ class GlobalGenRoots(): global_flags = CGWrapper(CGIndenter(CGList([ CGGeneric(f"const {args[0]} = {args[1]};") for args in flags - ], "\n")), pre="#[derive(Clone, Copy)]\npub(crate) struct Globals: u8 {\n", post="\n}") + ], "\n")), pre="#[derive(Clone, Copy)]\npub struct Globals: u8 {\n", post="\n}") globals_ = CGWrapper(CGIndenter(global_flags), pre="bitflags::bitflags! {\n", post="\n}") + return CGList([ + CGGeneric(AUTOGENERATED_WARNING_COMMENT), + globals_, + ]) + + @staticmethod + def InterfaceObjectMap(config): + mods = [ + "crate::dom::bindings::codegen", + "crate::script_runtime::JSContext", + "js::rust::HandleObject", + ] + imports = CGList([CGGeneric(f"use {mod};") for mod in mods], "\n") + phf = CGGeneric("include!(concat!(env!(\"BINDINGS_OUT_DIR\"), \"/InterfaceObjectMapPhf.rs\"));") return CGList([ CGGeneric(AUTOGENERATED_WARNING_COMMENT), - CGList([imports, globals_, phf], "\n\n") + CGList([imports, phf], "\n\n") ]) @staticmethod @@ -8270,8 +8277,8 @@ class GlobalGenRoots(): return CGList([ CGGeneric(AUTOGENERATED_WARNING_COMMENT), - CGGeneric(f"pub(crate) const PROTO_OR_IFACE_LENGTH: usize = {len(protos) + len(constructors)};\n"), - CGGeneric(f"pub(crate) const MAX_PROTO_CHAIN_LENGTH: usize = {config.maxProtoChainLength};\n\n"), + CGGeneric(f"pub const PROTO_OR_IFACE_LENGTH: usize = {len(protos) + len(constructors)};\n"), + CGGeneric(f"pub const MAX_PROTO_CHAIN_LENGTH: usize = {config.maxProtoChainLength};\n\n"), CGGeneric("#[allow(clippy::enum_variant_names, dead_code)]"), CGNonNamespacedEnum('ID', protos, 0, deriving="PartialEq, Copy, Clone", repr="u16"), CGNonNamespacedEnum('Constructor', constructors, len(protos), @@ -8281,7 +8288,7 @@ class GlobalGenRoots(): indentLevel=4), pre=f"static INTERFACES: [&str; {len(protos)}] = [\n", post="\n];\n\n"), - CGGeneric("pub(crate) fn proto_id_to_name(proto_id: u16) -> &'static str {\n" + CGGeneric("pub fn proto_id_to_name(proto_id: u16) -> &'static str {\n" " debug_assert!(proto_id < ID::Last as u16);\n" " INTERFACES[proto_id as usize]\n" "}\n\n"), @@ -8329,17 +8336,13 @@ class GlobalGenRoots(): return curr @staticmethod - def InheritTypes(config): - + def ConcreteInheritTypes(config): descriptors = config.getDescriptors(register=True, isCallback=False) imports = [CGGeneric("use crate::dom::types::*;\n"), + CGGeneric("use script_bindings::codegen::InheritTypes::*;\n"), CGGeneric("use crate::dom::bindings::conversions::{DerivedFrom, get_dom_class};\n"), CGGeneric("use crate::dom::bindings::inheritance::Castable;\n"), - CGGeneric("use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom};\n"), - CGGeneric("use crate::dom::bindings::trace::JSTraceable;\n"), - CGGeneric("use crate::dom::bindings::reflector::DomObject;\n"), - CGGeneric("use js::jsapi::JSTracer;\n\n"), - CGGeneric("use std::mem;\n\n")] + CGGeneric("use crate::dom::bindings::reflector::DomObject;\n\n")] allprotos = [] topTypes = [] hierarchy = defaultdict(list) @@ -8369,18 +8372,56 @@ class GlobalGenRoots(): hierarchy[descriptor.interface.parent.identifier.name].append(name) typeIdCode = [] + + for base, derived in hierarchy.items(): + if base in topTypes: + typeIdCode.append(CGGeneric(f""" +impl {base} {{ + pub(crate) fn type_id(&self) -> &'static {base}TypeId {{ + unsafe {{ + &get_dom_class(self.reflector().get_jsobject().get()) + .unwrap() + .type_id + .{base.lower()} + }} + }} +}} + +""")) + + curr = CGList(imports + typeIdCode + allprotos) + curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) + return curr + + @staticmethod + def InheritTypes(config): + descriptors = config.getDescriptors(register=True, isCallback=False) + topTypes = [] + hierarchy = defaultdict(list) + for descriptor in descriptors: + name = descriptor.name + upcast = descriptor.hasDescendants() + downcast = len(descriptor.prototypeChain) != 1 + + if upcast and not downcast: + topTypes.append(name) + + if downcast: + hierarchy[descriptor.interface.parent.identifier.name].append(name) + + typeIdCode = [] topTypeVariants = [ - ("ID used by abstract interfaces.", "pub(crate) abstract_: ()"), - ("ID used by interfaces that are not castable.", "pub(crate) alone: ()"), + ("ID used by abstract interfaces.", "pub abstract_: ()"), + ("ID used by interfaces that are not castable.", "pub alone: ()"), ] topTypeVariants += [ (f"ID used by interfaces that derive from {typeName}.", - f"pub(crate) {typeName.lower()}: {typeName}TypeId") + f"pub {typeName.lower()}: {typeName}TypeId") for typeName in topTypes ] topTypeVariantsAsStrings = [CGGeneric(f"/// {variant[0]}\n{variant[1]},") for variant in topTypeVariants] typeIdCode.append(CGWrapper(CGIndenter(CGList(topTypeVariantsAsStrings, "\n"), 4), - pre="#[derive(Copy)]\npub(crate) union TopTypeId {\n", + pre="#[derive(Copy)]\npub union TopTypeId {\n", post="\n}\n\n")) typeIdCode.append(CGGeneric("""\ @@ -8403,24 +8444,10 @@ impl Clone for TopTypeId { variants += [CGGeneric(type_id_variant(derivedName)) for derivedName in derived] derives = "Clone, Copy, Debug, PartialEq" typeIdCode.append(CGWrapper(CGIndenter(CGList(variants, ",\n"), 4), - pre=f"#[derive({derives})]\npub(crate) enum {base}TypeId {{\n", + pre=f"#[derive({derives})]\npub enum {base}TypeId {{\n", post="\n}\n\n")) - if base in topTypes: - typeIdCode.append(CGGeneric(f""" -impl {base} {{ - pub(crate) fn type_id(&self) -> &'static {base}TypeId {{ - unsafe {{ - &get_dom_class(self.reflector().get_jsobject().get()) - .unwrap() - .type_id - .{base.lower()} - }} - }} -}} - -""")) - curr = CGList(imports + typeIdCode + allprotos) + curr = CGList(typeIdCode) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr diff --git a/components/script_bindings/codegen/run.py b/components/script_bindings/codegen/run.py index dd812cd2d1e..3ef7f296715 100644 --- a/components/script_bindings/codegen/run.py +++ b/components/script_bindings/codegen/run.py @@ -52,10 +52,12 @@ def main(): for name, filename in [ ("PrototypeList", "PrototypeList.rs"), ("RegisterBindings", "RegisterBindings.rs"), + ("Globals", "Globals.rs"), ("InterfaceObjectMap", "InterfaceObjectMap.rs"), ("InterfaceObjectMapData", "InterfaceObjectMapData.json"), ("InterfaceTypes", "InterfaceTypes.rs"), ("InheritTypes", "InheritTypes.rs"), + ("ConcreteInheritTypes", "ConcreteInheritTypes.rs"), ("Bindings", "Bindings/mod.rs"), ("UnionTypes", "UnionTypes.rs"), ("DomTypes", "DomTypes.rs"), diff --git a/components/script_bindings/conversions.rs b/components/script_bindings/conversions.rs index c9f67605c5a..ec05d0c33f8 100644 --- a/components/script_bindings/conversions.rs +++ b/components/script_bindings/conversions.rs @@ -8,16 +8,30 @@ use js::conversions::{ latin1_to_string, ConversionResult, FromJSValConvertible, ToJSValConvertible, }; use js::error::throw_type_error; +use js::glue::{GetProxyHandlerExtra, IsProxyHandlerFamily}; use js::jsapi::{ - JSContext, JSString, JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, - JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN, + JSContext, JSObject, JSString, JS_DeprecatedStringHasLatin1Chars, + JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN, }; use js::jsval::{ObjectValue, StringValue}; -use js::rust::{maybe_wrap_value, HandleValue, MutableHandleValue, ToString}; +use js::rust::{ + get_object_class, is_dom_class, maybe_wrap_value, HandleValue, MutableHandleValue, ToString, +}; use servo_config::opts; +use crate::inheritance::Castable; use crate::reflector::Reflector; use crate::str::{ByteString, DOMString, USVString}; +use crate::utils::{DOMClass, DOMJSClass}; + +/// A trait to check whether a given `JSObject` implements an IDL interface. +pub trait IDLInterface { + /// Returns whether the given DOM class derives that interface. + fn derives(_: &'static DOMClass) -> bool; +} + +/// A trait to mark an IDL interface as deriving from another one. +pub trait DerivedFrom<T: Castable>: Castable {} // http://heycam.github.io/webidl/#es-USVString impl ToJSValConvertible for USVString { @@ -201,3 +215,35 @@ impl ToJSValConvertible for Reflector { maybe_wrap_value(cx, rval); } } + +/// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object. +/// +/// # Safety +/// obj must point to a valid, non-null JS object. +#[allow(clippy::result_unit_err)] +pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> { + let clasp = get_object_class(obj); + if is_dom_class(&*clasp) { + trace!("plain old dom object"); + let domjsclass: *const DOMJSClass = clasp as *const DOMJSClass; + return Ok(&(*domjsclass).dom_class); + } + if is_dom_proxy(obj) { + trace!("proxy dom object"); + let dom_class: *const DOMClass = GetProxyHandlerExtra(obj) as *const DOMClass; + return Ok(&*dom_class); + } + trace!("not a dom object"); + Err(()) +} + +/// Returns whether `obj` is a DOM object implemented as a proxy. +/// +/// # Safety +/// obj must point to a valid, non-null JS object. +pub unsafe fn is_dom_proxy(obj: *mut JSObject) -> bool { + unsafe { + let clasp = get_object_class(obj); + ((*clasp).flags & js::JSCLASS_IS_PROXY) != 0 && IsProxyHandlerFamily(obj) + } +} diff --git a/components/script_bindings/inheritance.rs b/components/script_bindings/inheritance.rs new file mode 100644 index 00000000000..d0ede1a42aa --- /dev/null +++ b/components/script_bindings/inheritance.rs @@ -0,0 +1,53 @@ +/* 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/. */ + +//! The `Castable` trait. + +use std::mem; + +use crate::conversions::{get_dom_class, DerivedFrom, IDLInterface}; +use crate::reflector::DomObject; +use crate::script_runtime::runtime_is_alive; + +/// A trait to hold the cast functions of IDL interfaces that either derive +/// or are derived from other interfaces. +pub trait Castable: IDLInterface + DomObject + Sized { + /// Check whether a DOM object implements one of its deriving interfaces. + fn is<T>(&self) -> bool + where + T: DerivedFrom<Self>, + { + // This is a weird place for this check to live, but it should catch any + // attempts to interact with DOM objects from Drop implementations that run + // as a result of the runtime shutting down and finalizing all remaining objects. + debug_assert!( + runtime_is_alive(), + "Attempting to interact with DOM objects after JS runtime has shut down." + ); + + let class = unsafe { get_dom_class(self.reflector().get_jsobject().get()).unwrap() }; + T::derives(class) + } + + /// Cast a DOM object upwards to one of the interfaces it derives from. + fn upcast<T>(&self) -> &T + where + T: Castable, + Self: DerivedFrom<T>, + { + unsafe { mem::transmute::<&Self, &T>(self) } + } + + /// Cast a DOM object downwards to one of the interfaces it might implement. + fn downcast<T>(&self) -> Option<&T> + where + T: DerivedFrom<Self>, + { + if self.is::<T>() { + Some(unsafe { mem::transmute::<&Self, &T>(self) }) + } else { + None + } + } +} diff --git a/components/script_bindings/lib.rs b/components/script_bindings/lib.rs index 551c0118950..1fb5a5f48b0 100644 --- a/components/script_bindings/lib.rs +++ b/components/script_bindings/lib.rs @@ -19,9 +19,27 @@ extern crate malloc_size_of_derive; pub mod callback; pub mod conversions; +pub mod inheritance; pub mod reflector; +pub mod script_runtime; pub mod str; mod trace; +pub mod utils; + +#[allow(non_snake_case)] +pub mod codegen { + pub mod Globals { + include!(concat!(env!("OUT_DIR"), "/Globals.rs")); + } + #[allow(dead_code, unused_imports, clippy::enum_variant_names)] + pub mod InheritTypes { + include!(concat!(env!("OUT_DIR"), "/InheritTypes.rs")); + } + #[allow(clippy::upper_case_acronyms)] + pub mod PrototypeList { + include!(concat!(env!("OUT_DIR"), "/PrototypeList.rs")); + } +} // These trait exports are public, because they are used in the DOM bindings. // Since they are used in derive macros, diff --git a/components/script_bindings/script_runtime.rs b/components/script_bindings/script_runtime.rs new file mode 100644 index 00000000000..e5cbde74c9e --- /dev/null +++ b/components/script_bindings/script_runtime.rs @@ -0,0 +1,17 @@ +/* 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/. */ + +use std::cell::Cell; + +thread_local!( + static THREAD_ACTIVE: Cell<bool> = const { Cell::new(true) }; +); + +pub fn runtime_is_alive() -> bool { + THREAD_ACTIVE.with(|t| t.get()) +} + +pub fn mark_runtime_dead() { + THREAD_ACTIVE.with(|t| t.set(false)); +} diff --git a/components/script_bindings/utils.rs b/components/script_bindings/utils.rs new file mode 100644 index 00000000000..fd307fd5ab3 --- /dev/null +++ b/components/script_bindings/utils.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 https://mozilla.org/MPL/2.0/. */ + +use std::os::raw::c_void; + +use malloc_size_of::MallocSizeOfOps; + +use crate::codegen::Globals::Globals; +use crate::codegen::InheritTypes::TopTypeId; +use crate::codegen::PrototypeList::{self, MAX_PROTO_CHAIN_LENGTH}; + +/// The struct that holds inheritance information for DOM object reflectors. +#[derive(Clone, Copy)] +pub struct DOMClass { + /// A list of interfaces that this object implements, in order of decreasing + /// derivedness. + pub interface_chain: [PrototypeList::ID; MAX_PROTO_CHAIN_LENGTH], + + /// The last valid index of `interface_chain`. + pub depth: u8, + + /// The type ID of that interface. + pub type_id: TopTypeId, + + /// The MallocSizeOf function wrapper for that interface. + pub malloc_size_of: unsafe fn(ops: &mut MallocSizeOfOps, *const c_void) -> usize, + + /// The `Globals` flag for this global interface, if any. + pub global: Globals, +} +unsafe impl Sync for DOMClass {} + +/// The JSClass used for DOM object reflectors. +#[derive(Copy)] +#[repr(C)] +pub struct DOMJSClass { + /// The actual JSClass. + pub base: js::jsapi::JSClass, + /// Associated data for DOM object reflectors. + pub dom_class: DOMClass, +} +impl Clone for DOMJSClass { + fn clone(&self) -> DOMJSClass { + *self + } +} +unsafe impl Sync for DOMJSClass {} |