aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2024-12-06 02:34:24 -0500
committerGitHub <noreply@github.com>2024-12-06 07:34:24 +0000
commit3515b83a95cb65da30c1e3988f7a47f79fd7b9bd (patch)
tree861d73bb4464f88bfe124460a81f9e4b231b346f /components/script
parentfaefed9869d3e2718cb33fa7e0e1f6443f81c814 (diff)
downloadservo-3515b83a95cb65da30c1e3988f7a47f79fd7b9bd.tar.gz
servo-3515b83a95cb65da30c1e3988f7a47f79fd7b9bd.zip
Create two-phase initialization for generated JS engine bindings (#34366)
* script: Generate a runtime initialization for static JS binding information. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Replace dummy static initializers with OnceLock. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Fix clippy warnings. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Only initialize statics for DOM interfaces with interface objects. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Remove one unnecessary Box::leak usage. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Tidy. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Hide thread-unsafe OnceLock usage inside of a wrapper type. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Mark ThreadUnsafeOnceLock::get unsafe. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Simplify ThreadUnsafeOnceLock internals. Signed-off-by: Josh Matthews <josh@joshmatthews.net> --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py364
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py2
-rw-r--r--components/script/dom/bindings/import.rs5
-rw-r--r--components/script/dom/bindings/utils.rs32
-rw-r--r--components/script/init.rs1
5 files changed, 296 insertions, 108 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 83f183283ec..fdb01a67a35 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -1644,7 +1644,7 @@ class PropertyDefiner:
assert len(array) != 0
specs = []
prefableSpecs = []
- prefableTemplate = ' Guard::new(%s, %s[%d])'
+ prefableTemplate = ' Guard::new(%s, (%s)[%d])'
origTemplate = specTemplate
if isinstance(specTemplate, str):
specTemplate = lambda _: origTemplate # noqa
@@ -1654,22 +1654,29 @@ class PropertyDefiner:
if specTerminator:
currentSpecs.append(specTerminator)
joinedCurrentSpecs = ',\n'.join(currentSpecs)
- specs.append(f"&[\n{joinedCurrentSpecs}]\n")
+ specs.append(f"&Box::leak(Box::new([\n{joinedCurrentSpecs}]))[..]\n")
conds = ','.join(cond) if isinstance(cond, list) else cond
prefableSpecs.append(
- prefableTemplate % (f"&[{conds}]", f"{name}_specs", len(specs) - 1)
+ prefableTemplate % (f"&[{conds}]", f"unsafe {{ {name}_specs.get() }}", len(specs) - 1)
)
joinedSpecs = ',\n'.join(specs)
- specsArray = (f"const {name}_specs: &[&[{specType}]] = &[\n"
- f"{joinedSpecs}\n"
- "];\n")
+ specsArray = f"static {name}_specs: ThreadUnsafeOnceLock<&[&[{specType}]]> = ThreadUnsafeOnceLock::new();\n"
+
+ initSpecs = f"""
+pub(crate) fn init_{name}_specs() {{
+ {name}_specs.set(Box::leak(Box::new([{joinedSpecs}])));
+}}"""
joinedPrefableSpecs = ',\n'.join(prefableSpecs)
- prefArray = (f"const {name}: &[Guard<&[{specType}]>] = &[\n"
- f"{joinedPrefableSpecs}\n"
- "];\n")
- return f"{specsArray}{prefArray}"
+ prefArray = f"static {name}: ThreadUnsafeOnceLock<&[Guard<&[{specType}]>]> = ThreadUnsafeOnceLock::new();\n"
+
+ initPrefs = f"""
+pub(crate) fn init_{name}_prefs() {{
+ {name}.set(Box::leak(Box::new([{joinedPrefableSpecs}])));
+}}"""
+
+ return f"{specsArray}{initSpecs}{prefArray}{initPrefs}"
def generateUnguardedArray(self, array, name, specTemplate, specTerminator,
specType, getCondition, getDataTuple):
@@ -1692,12 +1699,12 @@ class PropertyDefiner:
specsArray.append(specTerminator)
joinedSpecs = ',\n'.join(specsArray)
- return dedent(
- f"""
- const {name}: &[{specType}] = &[
- {joinedSpecs}
- ];
- """)
+ initialSpecs = f"static {name}: ThreadUnsafeOnceLock<&[{specType}]> = ThreadUnsafeOnceLock::new();\n"
+ initSpecs = f"""
+pub(crate) fn init_{name}() {{
+ {name}.set(Box::leak(Box::new([{joinedSpecs}])));
+}}"""
+ return dedent(f"{initialSpecs}{initSpecs}")
# The length of a method is the minimum of the lengths of the
@@ -1850,7 +1857,7 @@ class MethodDefiner(PropertyDefiner):
accessor = "None"
jitinfo = "ptr::null()"
else:
- selfHostedName = "0 as *const libc::c_char"
+ selfHostedName = "ptr::null()"
if m.get("methodInfo", True):
if m.get("returnsPromise", False):
exceptionToRejection = "true"
@@ -1861,11 +1868,12 @@ class MethodDefiner(PropertyDefiner):
# easy to tell whether the methodinfo is a JSJitInfo or
# a JSTypedMethodJitInfo here. The compiler knows, though,
# so let it do the work.
- jitinfo = f"&{identifier}_methodinfo as *const _ as *const JSJitInfo"
+ jitinfo = (f"unsafe {{ {identifier}_methodinfo.get() }}"
+ " as *const _ as *const JSJitInfo")
accessor = f"Some(generic_method::<{exceptionToRejection}>)"
else:
if m.get("returnsPromise", False):
- jitinfo = f"&{m.get('nativeName', m['name'])}_methodinfo"
+ jitinfo = f"unsafe {{ {m.get('nativeName', m['name'])}_methodinfo.get() }}"
accessor = "Some(generic_static_promise_method)"
else:
jitinfo = "ptr::null()"
@@ -1956,11 +1964,11 @@ class AttrDefiner(PropertyDefiner):
attr = attr['attr']
if self.crossorigin and not attr.getExtendedAttribute("CrossOriginReadable"):
- return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }"
+ return "JSNativeWrapper { op: None, info: ptr::null() }"
if self.static:
accessor = f'get_{self.descriptor.internalNameFor(attr.identifier.name)}'
- jitinfo = "0 as *const JSJitInfo"
+ jitinfo = "ptr::null()"
else:
if attr.type.isPromise():
exceptionToRejection = "true"
@@ -1970,7 +1978,8 @@ class AttrDefiner(PropertyDefiner):
accessor = f"generic_lenient_getter::<{exceptionToRejection}>"
else:
accessor = f"generic_getter::<{exceptionToRejection}>"
- jitinfo = f"&{self.descriptor.internalNameFor(attr.identifier.name)}_getterinfo"
+ internalName = self.descriptor.internalNameFor(attr.identifier.name)
+ jitinfo = f"unsafe {{ {internalName}_getterinfo.get() }}"
return f"JSNativeWrapper {{ op: Some({accessor}), info: {jitinfo} }}"
@@ -1981,17 +1990,18 @@ class AttrDefiner(PropertyDefiner):
or (attr.readonly
and not attr.getExtendedAttribute("PutForwards")
and not attr.getExtendedAttribute("Replaceable"))):
- return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }"
+ return "JSNativeWrapper { op: None, info: ptr::null() }"
if self.static:
accessor = f'set_{self.descriptor.internalNameFor(attr.identifier.name)}'
- jitinfo = "0 as *const JSJitInfo"
+ jitinfo = "ptr::null()"
else:
if attr.hasLegacyLenientThis():
accessor = "generic_lenient_setter"
else:
accessor = "generic_setter"
- jitinfo = f"&{self.descriptor.internalNameFor(attr.identifier.name)}_setterinfo"
+ internalName = self.descriptor.internalNameFor(attr.identifier.name)
+ jitinfo = f"unsafe {{ {internalName}_setterinfo.get() }}"
return f"JSNativeWrapper {{ op: Some({accessor}), info: {jitinfo} }}"
@@ -2345,7 +2355,7 @@ def DOMClass(descriptor):
# padding.
protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList)))
prototypeChainString = ', '.join(protoList)
- mallocSizeOf = f'malloc_size_of_including_raw_self::<{descriptor.concreteType}>'
+ mallocSizeOf = f"malloc_size_of_including_raw_self::<{descriptor.concreteType}>"
if descriptor.isGlobal():
globals_ = camel_to_upper_snake(descriptor.name)
else:
@@ -2393,32 +2403,41 @@ class CGDOMJSClass(CGThing):
elif self.descriptor.weakReferenceable:
args["slots"] = "2"
return f"""
-static CLASS_OPS: JSClassOps = JSClassOps {{
- addProperty: None,
- delProperty: None,
- enumerate: None,
- newEnumerate: {args['enumerateHook']},
- resolve: {args['resolveHook']},
- mayResolve: None,
- finalize: Some({args['finalizeHook']}),
- call: None,
- construct: None,
- trace: Some({args['traceHook']}),
-}};
+static CLASS_OPS: ThreadUnsafeOnceLock<JSClassOps> = ThreadUnsafeOnceLock::new();
+
+pub(crate) fn init_class_ops() {{
+ CLASS_OPS.set(JSClassOps {{
+ addProperty: None,
+ delProperty: None,
+ enumerate: None,
+ newEnumerate: {args['enumerateHook']},
+ resolve: {args['resolveHook']},
+ mayResolve: None,
+ finalize: Some({args['finalizeHook']}),
+ call: None,
+ construct: None,
+ trace: Some({args['traceHook']}),
+ }});
+}}
-static Class: DOMJSClass = DOMJSClass {{
- base: JSClass {{
- name: {args['name']},
- flags: JSCLASS_IS_DOMJSCLASS | {args['flags']} |
- ((({args['slots']}) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT)
- /* JSCLASS_HAS_RESERVED_SLOTS({args['slots']}) */,
- cOps: &CLASS_OPS,
- spec: ptr::null(),
- ext: ptr::null(),
- oOps: ptr::null(),
- }},
- dom_class: {args['domClass']},
-}};
+static Class: ThreadUnsafeOnceLock<DOMJSClass> = ThreadUnsafeOnceLock::new();
+
+pub(crate) fn init_domjs_class() {{
+ init_class_ops();
+ Class.set(DOMJSClass {{
+ base: JSClass {{
+ name: {args['name']},
+ flags: JSCLASS_IS_DOMJSCLASS | {args['flags']} |
+ ((({args['slots']}) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT)
+ /* JSCLASS_HAS_RESERVED_SLOTS({args['slots']}) */,
+ cOps: unsafe {{ CLASS_OPS.get() }},
+ spec: ptr::null(),
+ ext: ptr::null(),
+ oOps: ptr::null(),
+ }},
+ dom_class: {args['domClass']},
+ }});
+}}
"""
@@ -2490,7 +2509,7 @@ static PrototypeClass: JSClass = JSClass {{
flags:
// JSCLASS_HAS_RESERVED_SLOTS()
({slotCountStr} ) << JSCLASS_RESERVED_SLOTS_SHIFT,
- cOps: 0 as *const _,
+ cOps: ptr::null(),
spec: ptr::null(),
ext: ptr::null(),
oOps: ptr::null(),
@@ -2523,8 +2542,10 @@ static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe {{
name = self.descriptor.interface.identifier.name
representation = f'b"function {name}() {{\\n [native code]\\n}}"'
return f"""
-static INTERFACE_OBJECT_CLASS: NonCallbackInterfaceObjectClass =
- NonCallbackInterfaceObjectClass::new(
+static INTERFACE_OBJECT_CLASS: ThreadUnsafeOnceLock<NonCallbackInterfaceObjectClass> = ThreadUnsafeOnceLock::new();
+
+pub(crate) fn init_interface_object() {{
+ INTERFACE_OBJECT_CLASS.set(NonCallbackInterfaceObjectClass::new(
{{
// Intermediate `const` because as of nightly-2018-10-05,
// rustc is conservative in promotion to `'static` of the return values of `const fn`s:
@@ -2535,7 +2556,9 @@ static INTERFACE_OBJECT_CLASS: NonCallbackInterfaceObjectClass =
}},
{representation},
PrototypeList::ID::{name},
- {self.descriptor.prototypeDepth});
+ {self.descriptor.prototypeDepth},
+ ));
+}}
"""
@@ -2971,7 +2994,7 @@ def InitLegacyUnforgeablePropertiesOnHolder(descriptor, properties):
]
for template, array in unforgeableMembers:
if array.length() > 0:
- unforgeables.append(CGGeneric(template % array.variableName()))
+ unforgeables.append(CGGeneric(template % f"unsafe {{ {array.variableName()}.get() }}"))
return CGList(unforgeables, "\n")
@@ -3072,7 +3095,7 @@ if let Some(given) = given_proto {
}
rooted!(in(*cx) let obj = JS_NewObjectWithGivenProto(
*cx,
- &Class.base,
+ &Class.get().base,
proto.handle(),
));
assert!(!obj.is_null());
@@ -3129,7 +3152,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
("define_guarded_methods", self.properties.methods),
("define_guarded_constants", self.properties.consts)
]
- members = [f"{function}(cx, obj.handle(), {array.variableName()}, obj.handle());"
+ members = [f"{function}(cx, obj.handle(), {array.variableName()}.get(), obj.handle());"
for (function, array) in pairs if array.length() > 0]
membersStr = "\n".join(members)
@@ -3140,7 +3163,7 @@ let origin = (*raw.as_ptr()).upcast::<GlobalScope>().origin();
rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>());
create_global_object(
cx,
- &Class.base,
+ &Class.get().base,
raw.as_ptr() as *const libc::c_void,
_trace,
obj.handle_mut(),
@@ -3189,7 +3212,7 @@ class CGIDLInterface(CGThing):
elif self.descriptor.proxy:
check = "ptr::eq(class, &Class)"
else:
- check = "ptr::eq(class, &Class.dom_class)"
+ check = "ptr::eq(class, unsafe { &Class.get().dom_class })"
return f"""
impl IDLInterface for {name} {{
#[inline]
@@ -3308,11 +3331,14 @@ class CGCrossOriginProperties(CGThing):
def define(self):
return f"{self.methods}{self.attributes}" + dedent(
"""
- const CROSS_ORIGIN_PROPERTIES: proxyhandler::CrossOriginProperties =
- proxyhandler::CrossOriginProperties {
- attributes: sCrossOriginAttributes,
- methods: sCrossOriginMethods,
- };
+ static CROSS_ORIGIN_PROPERTIES: ThreadUnsafeOnceLock<CrossOriginProperties> = ThreadUnsafeOnceLock::new();
+
+ pub(crate) fn init_cross_origin_properties() {
+ CROSS_ORIGIN_PROPERTIES.set(CrossOriginProperties {
+ attributes: unsafe { sCrossOriginAttributes.get() },
+ methods: unsafe { sCrossOriginMethods.get() },
+ });
+ }
"""
)
@@ -3387,11 +3413,11 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
else:
proto = "JS_NewPlainObject(*cx)"
if self.properties.static_methods.length():
- methods = self.properties.static_methods.variableName()
+ methods = f"{self.properties.static_methods.variableName()}.get()"
else:
methods = "&[]"
if self.descriptor.interface.hasConstants():
- constants = "sConstants"
+ constants = "sConstants.get()"
else:
constants = "&[]"
id = MakeNativeName(name)
@@ -3413,7 +3439,7 @@ assert!((*cache)[PrototypeList::Constructor::{id} as usize].is_null());
cName = str_to_cstr(name)
return CGGeneric(f"""
rooted!(in(*cx) let mut interface = ptr::null_mut::<JSObject>());
-create_callback_interface_object(cx, global, sConstants, {cName}, interface.handle_mut());
+create_callback_interface_object(cx, global, sConstants.get(), {cName}, interface.handle_mut());
assert!(!interface.is_null());
assert!((*cache)[PrototypeList::Constructor::{name} as usize].is_null());
(*cache)[PrototypeList::Constructor::{name} as usize] = interface.get();
@@ -3456,7 +3482,7 @@ assert!(!prototype_proto.is_null());"""))
for arrayName in self.properties.arrayNames():
array = getattr(self.properties, arrayName)
if array.length():
- properties[arrayName] = array.variableName()
+ properties[arrayName] = f"{array.variableName()}.get()"
else:
properties[arrayName] = "&[]"
@@ -3512,7 +3538,7 @@ rooted!(in(*cx) let mut interface = ptr::null_mut::<JSObject>());
create_noncallback_interface_object(cx,
global,
interface_proto.handle(),
- &INTERFACE_OBJECT_CLASS,
+ INTERFACE_OBJECT_CLASS.get(),
{properties['static_methods']},
{properties['static_attrs']},
{properties['consts']},
@@ -3614,7 +3640,7 @@ assert!((*cache)[PrototypeList::Constructor::{properties['id']} as usize].is_nul
holderClass = "ptr::null()"
holderProto = "HandleObject::null()"
else:
- holderClass = "&Class.base as *const JSClass"
+ holderClass = "&Class.get().base as *const JSClass"
holderProto = "prototype.handle()"
code.append(CGGeneric(f"""
rooted!(in(*cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
@@ -4482,7 +4508,7 @@ class CGMemberJITInfo(CGThing):
"""
JSJitInfo {
__bindgen_anon_1: JSJitInfo__bindgen_ty_1 {
- ${opKind}: Some(${opName})
+ ${opKind}: ${opValue}
},
__bindgen_anon_2: JSJitInfo__bindgen_ty_2 {
protoID: PrototypeList::ID::${name} as u16,
@@ -4505,7 +4531,7 @@ class CGMemberJITInfo(CGThing):
),
}
""",
- opName=opName,
+ opValue=f"Some({opName})",
name=self.descriptor.name,
depth=self.descriptor.interface.inheritanceDepth(),
opType=opType,
@@ -4531,17 +4557,26 @@ class CGMemberJITInfo(CGThing):
return fill(
"""
$*{argTypesDecl}
- const ${infoName}: JSTypedMethodJitInfo = JSTypedMethodJitInfo {
- base: ${jitInfo},
- argTypes: &${argTypes} as *const _ as *const JSJitInfo_ArgType,
- };
+ static ${infoName}: ThreadUnsafeOnceLock<JSTypedMethodJitInfo> = ThreadUnsafeOnceLock::new();
+
+ pub(crate) fn init_${infoName}() {
+ ${infoName}.set(JSTypedMethodJitInfo {
+ base: ${jitInfoInit},
+ argTypes: &${argTypes} as *const _ as *const JSJitInfo_ArgType,
+ });
+ }
""",
argTypesDecl=argTypesDecl,
infoName=infoName,
- jitInfo=indent(jitInfoInitializer(True)),
+ jitInfoInit=indent(jitInfoInitializer(True)),
argTypes=argTypes)
- return f"\nconst {infoName}: JSJitInfo = {jitInfoInitializer(False)};\n"
+ return f"""
+static {infoName}: ThreadUnsafeOnceLock<JSJitInfo> = ThreadUnsafeOnceLock::new();
+
+pub(crate) fn init_{infoName}() {{
+ {infoName}.set({jitInfoInitializer(False)});
+}}"""
def define(self):
if self.member.isAttr():
@@ -4815,30 +4850,34 @@ class CGStaticMethodJitinfo(CGGeneric):
CGGeneric.__init__(
self,
f"""
- const {method.identifier.name}_methodinfo: JSJitInfo = JSJitInfo {{
- __bindgen_anon_1: JSJitInfo__bindgen_ty_1 {{
- staticMethod: Some({method.identifier.name})
- }},
- __bindgen_anon_2: JSJitInfo__bindgen_ty_2 {{
- protoID: PrototypeList::ID::Last as u16,
- }},
- __bindgen_anon_3: JSJitInfo__bindgen_ty_3 {{ depth: 0 }},
- _bitfield_align_1: [],
- _bitfield_1: __BindgenBitfieldUnit::new(
- new_jsjitinfo_bitfield_1!(
- JSJitInfo_OpType::StaticMethod as u8,
- JSJitInfo_AliasSet::AliasEverything as u8,
- JSValueType::JSVAL_TYPE_OBJECT as u8,
- false,
- false,
- false,
- false,
- false,
- false,
- 0,
- ).to_ne_bytes()
- ),
- }};
+ static {method.identifier.name}_methodinfo: ThreadUnsafeOnceLock<JSJitInfo> = ThreadUnsafeOnceLock::new();
+
+ pub(crate) fn init_{method.identifier.name}_methodinfo() {{
+ {method.identifier.name}_methodinfo.set(JSJitInfo {{
+ __bindgen_anon_1: JSJitInfo__bindgen_ty_1 {{
+ staticMethod: Some({method.identifier.name})
+ }},
+ __bindgen_anon_2: JSJitInfo__bindgen_ty_2 {{
+ protoID: PrototypeList::ID::Last as u16,
+ }},
+ __bindgen_anon_3: JSJitInfo__bindgen_ty_3 {{ depth: 0 }},
+ _bitfield_align_1: [],
+ _bitfield_1: __BindgenBitfieldUnit::new(
+ new_jsjitinfo_bitfield_1!(
+ JSJitInfo_OpType::StaticMethod as u8,
+ JSJitInfo_AliasSet::AliasEverything as u8,
+ JSValueType::JSVAL_TYPE_OBJECT as u8,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ ).to_ne_bytes()
+ ),
+ }});
+ }}
"""
)
@@ -5790,7 +5829,7 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
"""
if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
if !proxyhandler::cross_origin_get_own_property_helper(
- cx, proxy, &CROSS_ORIGIN_PROPERTIES, id, desc, &mut *is_none
+ cx, proxy, CROSS_ORIGIN_PROPERTIES.get(), id, desc, &mut *is_none
) {
return false;
}
@@ -6008,7 +6047,9 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
body += dedent(
"""
if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
- return proxyhandler::cross_origin_own_property_keys(cx, proxy, &CROSS_ORIGIN_PROPERTIES, props);
+ return proxyhandler::cross_origin_own_property_keys(
+ cx, proxy, CROSS_ORIGIN_PROPERTIES.get(), props
+ );
}
// Safe to enter the Realm of proxy now.
@@ -6130,7 +6171,9 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
indexed += dedent(
"""
if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
- return proxyhandler::cross_origin_has_own(cx, proxy, &CROSS_ORIGIN_PROPERTIES, id, bp);
+ return proxyhandler::cross_origin_has_own(
+ cx, proxy, CROSS_ORIGIN_PROPERTIES.get(), id, bp
+ );
}
// Safe to enter the Realm of proxy now.
@@ -6643,6 +6686,95 @@ class CGWeakReferenceableTrait(CGThing):
return self.code
+class CGInitStatics(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ def internal(method):
+ return descriptor.internalNameFor(method.identifier.name)
+
+ properties = PropertyArrays(descriptor)
+ all_names = PropertyArrays.arrayNames()
+ arrays = [getattr(properties, name) for name in all_names]
+ nonempty = map(lambda x: x.variableName(), filter(lambda x: x.length() != 0, arrays))
+ specs = [[
+ f'init_{name}_specs();',
+ f'init_{name}_prefs();',
+ ] for name in nonempty]
+ flat_specs = [x for xs in specs for x in xs]
+ specs = '\n'.join(flat_specs)
+ module = f"crate::dom::bindings::codegen::Bindings::{toBindingPath(descriptor)}"
+ relevantMethods = [
+ m for m in descriptor.interface.members if m.isMethod()
+ ] if not descriptor.interface.isCallback() else []
+ allOperations = descriptor.operations.keys()
+ relevantOperations = list(map(lambda x: f"__{x.lower()}", filter(lambda o: o != "Stringifier", allOperations)))
+ relevantMethods = filter(
+ lambda m: internal(m) not in relevantOperations,
+ relevantMethods,
+ )
+ relevantMethods = filter(
+ lambda x: (
+ not x.isStatic()
+ or any([r.isPromise() for r, _ in x.signatures()])
+ ),
+ relevantMethods
+ )
+
+ methods = [f'{module}::init_{internal(m)}_methodinfo();' for m in relevantMethods]
+ getters = [
+ f'init_{internal(m)}_getterinfo();'
+ for m in descriptor.interface.members if m.isAttr() and not m.isStatic()
+ ]
+ setters = [
+ f'init_{internal(m)}_setterinfo();'
+ for m in descriptor.interface.members
+ if m.isAttr() and (
+ not m.readonly
+ or m.getExtendedAttribute("PutForwards")
+ or m.getExtendedAttribute("Replaceable")
+ ) and not m.isStatic()
+ ]
+ methods = '\n'.join(methods)
+ getters = '\n'.join(getters)
+ setters = '\n'.join(setters)
+ crossorigin = [
+ "init_sCrossOriginMethods();",
+ "init_sCrossOriginAttributes();",
+ "init_cross_origin_properties();"
+ ] if descriptor.isMaybeCrossOriginObject() else []
+ crossorigin_joined = '\n'.join(crossorigin)
+ interface = (
+ "init_interface_object();"
+ if descriptor.interface.hasInterfaceObject()
+ and not descriptor.interface.isNamespace()
+ and not descriptor.interface.isCallback()
+ and not descriptor.interface.getExtendedAttribute("Inline")
+ else ""
+ )
+ nonproxy = (
+ "init_domjs_class();"
+ if not descriptor.proxy
+ and descriptor.concrete
+ else ""
+ )
+
+ self.code = f"""
+ pub(crate) fn init_statics() {{
+ {interface}
+ {nonproxy}
+ {methods}
+ {getters}
+ {setters}
+ {crossorigin_joined}
+ {specs}
+ }}
+ """
+
+ def define(self):
+ return self.code
+
+
class CGDescriptor(CGThing):
def __init__(self, descriptor, config, soleDescriptor):
CGThing.__init__(self)
@@ -6837,6 +6969,8 @@ class CGDescriptor(CGThing):
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables,
haveLegacyWindowAliases))
+ cgThings.append(CGInitStatics(descriptor))
+
cgThings = CGList(cgThings, '\n')
# Add imports
@@ -7205,6 +7339,23 @@ class CGDictionary(CGThing):
return deps
+class CGInitAllStatics(CGAbstractMethod):
+ def __init__(self, config):
+ docs = "Initialize the static data used by the SpiderMonkey DOM bindings to implement JS interfaces."
+ descriptors = (config.getDescriptors(isCallback=False, register=True)
+ + config.getDescriptors(isCallback=True, hasInterfaceObject=True, register=True))
+ CGAbstractMethod.__init__(self, None, 'InitAllStatics', 'void', [],
+ pub=True, docs=docs)
+ self.descriptors = descriptors
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric(f" Bindings::{toBindingModuleFileFromDescriptor(desc)}::{toBindingNamespace(desc.name)}"
+ "::init_statics();")
+ for desc in self.descriptors
+ ], "\n")
+
+
class CGRegisterProxyHandlersMethod(CGAbstractMethod):
def __init__(self, descriptors):
docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies."
@@ -7238,6 +7389,7 @@ class CGRegisterProxyHandlers(CGThing):
f"{body}}}\n"
),
CGRegisterProxyHandlersMethod(descriptors),
+ CGInitAllStatics(config),
], "\n")
def define(self):
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
index b887fe91931..07822eac1b3 100644
--- a/components/script/dom/bindings/codegen/Configuration.py
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -384,7 +384,7 @@ class Descriptor(DescriptorProvider):
filename = getIdlFileName(self.interface)
# if interface name is not same as webidl file
# webidl is super module for interface
- if filename.lower() != self.interface.identifier.name.lower() and not self.interface.isIteratorInterface():
+ if filename.lower() != self.interface.identifier.name.lower():
return filename
return None
diff --git a/components/script/dom/bindings/import.rs b/components/script/dom/bindings/import.rs
index c4c7a460ca9..0d9f0d70fdf 100644
--- a/components/script/dom/bindings/import.rs
+++ b/components/script/dom/bindings/import.rs
@@ -34,11 +34,14 @@ pub mod base {
pub use crate::dom::bindings::error::Error::JSFailed;
pub use crate::dom::bindings::error::{throw_dom_exception, Fallible};
pub use crate::dom::bindings::num::Finite;
+ pub use crate::dom::bindings::proxyhandler::CrossOriginProperties;
pub use crate::dom::bindings::reflector::DomObject;
pub use crate::dom::bindings::root::DomRoot;
pub use crate::dom::bindings::str::{ByteString, DOMString, USVString};
pub use crate::dom::bindings::trace::RootedTraceableBox;
- pub use crate::dom::bindings::utils::{get_dictionary_property, set_dictionary_property};
+ pub use crate::dom::bindings::utils::{
+ get_dictionary_property, set_dictionary_property, ThreadUnsafeOnceLock,
+ };
pub use crate::dom::globalscope::GlobalScope;
pub use crate::script_runtime::JSContext as SafeJSContext;
}
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index cd7d8d4e6e5..2817803504b 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -7,6 +7,7 @@
use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::ptr::NonNull;
+use std::sync::OnceLock;
use std::{ptr, slice, str};
use js::conversions::ToJSValConvertible;
@@ -48,6 +49,37 @@ use crate::dom::bindings::trace::trace_object;
use crate::dom::windowproxy::WindowProxyHandler;
use crate::script_runtime::JSContext as SafeJSContext;
+/// A OnceLock wrapping a type that is not considered threadsafe by the Rust compiler, but
+/// will be used in a threadsafe manner (it will not be mutated, after being initialized).
+///
+/// This is needed to allow using JS API types (which usually involve raw pointers) in static initializers,
+/// when Servo guarantees through the use of OnceLock that only one thread will ever initialize
+/// the value.
+pub struct ThreadUnsafeOnceLock<T>(OnceLock<T>);
+
+impl<T> ThreadUnsafeOnceLock<T> {
+ pub const fn new() -> Self {
+ Self(OnceLock::new())
+ }
+
+ /// Initialize the value inside this lock. Panics if the lock has been previously initialized.
+ pub fn set(&self, val: T) {
+ assert!(self.0.set(val).is_ok());
+ }
+
+ /// Get a reference to the value inside this lock. Panics if the lock has not been initialized.
+ ///
+ /// SAFETY:
+ /// The caller must ensure that it does not mutate value contained inside this lock
+ /// (using interior mutability).
+ pub unsafe fn get(&self) -> &T {
+ self.0.get().unwrap()
+ }
+}
+
+unsafe impl<T> Sync for ThreadUnsafeOnceLock<T> {}
+unsafe impl<T> Send for ThreadUnsafeOnceLock<T> {}
+
#[derive(JSTraceable, MallocSizeOf)]
/// Static data associated with a global object.
pub struct GlobalStaticData {
diff --git a/components/script/init.rs b/components/script/init.rs
index ec49c3f2953..324714a3831 100644
--- a/components/script/init.rs
+++ b/components/script/init.rs
@@ -70,6 +70,7 @@ pub fn init() -> JSEngineSetup {
// Create the global vtables used by the (generated) DOM
// bindings to implement JS proxies.
RegisterBindings::RegisterProxyHandlers();
+ RegisterBindings::InitAllStatics();
js::glue::InitializeMemoryReporter(Some(is_dom_object));
}