Move more foundational types to script_bindings (#35280)

* script: Move DOMClass to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Move DOMJSClass and get_dom_class to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Move Castable/DerivedFrom/IDLInterface to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-02-04 05:36:30 -05:00 committed by GitHub
parent eaaad757e8
commit c0cef69108
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 291 additions and 207 deletions

View file

@ -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 }

View file

@ -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)
@ -8368,19 +8371,57 @@ class GlobalGenRoots():
if downcast:
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

View file

@ -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"),

View file

@ -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)
}
}

View file

@ -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
}
}
}

View file

@ -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,

View file

@ -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));
}

View file

@ -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 {}