From 3cde83447ec5f24cd8155a87e175ff479ca86366 Mon Sep 17 00:00:00 2001 From: Sam <16504129+sagudev@users.noreply.github.com> Date: Thu, 19 Feb 2026 09:57:40 +0100 Subject: [PATCH] script: always `let cx = &mut cx` in codegen and support cx in Constructor (#42712) #42681 did not actually allowed using cx in Constructor, but it did most heavy lifting. Now we need to pass cx to Constructor call (and skip CanGc) if cx is requested in Bindings.conf. This PR also test this on testbindings. Also we now always use `let cx = &mut cx`, so we can always use just cx (compiler will automatically deref it into &cx if needed). This is important because codegen stuff is called from many places to it make sense/easier to just unify this. Testing: It compiles Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- components/script/dom/testing/testbinding.rs | 20 +++++----- .../script_bindings/codegen/Bindings.conf | 1 + components/script_bindings/codegen/codegen.py | 39 +++++++++++-------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/components/script/dom/testing/testbinding.rs b/components/script/dom/testing/testbinding.rs index a39b31a1f0a..5c33e78c8b8 100644 --- a/components/script/dom/testing/testbinding.rs +++ b/components/script/dom/testing/testbinding.rs @@ -42,7 +42,7 @@ use crate::dom::bindings::codegen::UnionTypes::{ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::TrustedPromise; -use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto}; +use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto_and_cx}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::{ByteString, DOMString, USVString}; use crate::dom::bindings::trace::RootedTraceableBox; @@ -72,46 +72,46 @@ impl TestBinding { } fn new( + cx: &mut js::context::JSContext, global: &GlobalScope, proto: Option, - can_gc: CanGc, ) -> DomRoot { - reflect_dom_object_with_proto( + reflect_dom_object_with_proto_and_cx( Box::new(TestBinding::new_inherited()), global, proto, - can_gc, + cx, ) } } impl TestBindingMethods for TestBinding { fn Constructor( + cx: &mut js::context::JSContext, global: &GlobalScope, proto: Option, - can_gc: CanGc, ) -> Fallible> { - Ok(TestBinding::new(global, proto, can_gc)) + Ok(TestBinding::new(cx, global, proto)) } #[expect(unused_variables)] fn Constructor_( + cx: &mut js::context::JSContext, global: &GlobalScope, proto: Option, - can_gc: CanGc, nums: Vec, ) -> Fallible> { - Ok(TestBinding::new(global, proto, can_gc)) + Ok(TestBinding::new(cx, global, proto)) } #[expect(unused_variables)] fn Constructor__( + cx: &mut js::context::JSContext, global: &GlobalScope, proto: Option, - can_gc: CanGc, num: f64, ) -> Fallible> { - Ok(TestBinding::new(global, proto, can_gc)) + Ok(TestBinding::new(cx, global, proto)) } fn BooleanAttribute(&self) -> bool { diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index dd339592911..0909379ef7f 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -720,6 +720,7 @@ DOMInterfaces = { 'inRealms': ['PromiseAttribute', 'PromiseNativeHandler'], 'canGc': ['InterfaceAttribute', 'GetInterfaceAttributeNullable', 'ReceiveInterface', 'ReceiveInterfaceSequence', 'ReceiveNullableInterface', 'PromiseAttribute', 'PromiseNativeHandler', 'PromiseResolveNative', 'PromiseRejectNative', 'PromiseRejectWithTypeError'], 'additionalTraits': ['crate::interfaces::TestBindingHelpers'], + 'cx': ['Constructor', 'Constructor_', 'Constructor__'], }, 'TestWorklet': { diff --git a/components/script_bindings/codegen/codegen.py b/components/script_bindings/codegen/codegen.py index 036cab7e288..f3a9ab2e4ec 100644 --- a/components/script_bindings/codegen/codegen.py +++ b/components/script_bindings/codegen/codegen.py @@ -4143,15 +4143,14 @@ class CGCallGenerator(CGThing): # Build up our actual call self.cgRoot = CGList([], "\n") - if nativeMethodName in descriptor.cx_no_gcMethods: - args.prepend(CGGeneric("&cx")) - elif nativeMethodName in descriptor.cxMethods: - args.prepend(CGGeneric("&mut cx")) + if nativeMethodName in descriptor.cx_no_gcMethods or nativeMethodName in descriptor.cxMethods: + args.prepend(CGGeneric("cx")) elif nativeMethodName in descriptor.realmMethods: self.cgRoot.append(CGList([ - CGGeneric("let mut realm = CurrentRealm::assert(&mut cx);") + CGGeneric("let mut realm = CurrentRealm::assert(cx);"), + CGGeneric("let cx = &mut realm;"), ])) - args.prepend(CGGeneric("&mut realm")) + args.prepend(CGGeneric("cx")) else: if "cx" not in argsPre and needsCx: args.prepend(CGGeneric("SafeJSContext::from_ptr(cx.raw_cx())")) @@ -4471,6 +4470,7 @@ class CGSpecializedMethod(CGAbstractExternMethod): return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(), self.descriptor, self.method), pre="let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\n" + "let cx = &mut cx;\n" f"let this = &*(this as *const {self.descriptor.concreteType});\n" "let args = &*args;\n" "let argc = args.argc_;\n") @@ -4612,7 +4612,7 @@ class CGStaticMethod(CGAbstractStaticBindingMethod): def generate_code(self) -> CGThing: nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method) - safeContext = CGGeneric("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\n") + safeContext = CGGeneric("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\nlet cx = &mut cx;\n") setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n") call = CGMethodCall(["&global"], nativeName, True, self.descriptor, self.method) return CGList([safeContext, setupArgs, call]) @@ -4639,6 +4639,7 @@ class CGSpecializedGetter(CGAbstractExternMethod): return CGWrapper(CGGetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), pre="let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\n" + "let cx = &mut cx;\n" f"let this = &*(this as *const {self.descriptor.concreteType});\n") @staticmethod @@ -4668,7 +4669,7 @@ class CGStaticGetter(CGAbstractStaticBindingMethod): def generate_code(self) -> CGThing: nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr) - safeContext = CGGeneric("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\n") + safeContext = CGGeneric("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\nlet cx = &mut cx;\n") setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n") call = CGGetterCall(["&global"], self.attr.type, nativeName, self.descriptor, self.attr) @@ -4695,6 +4696,7 @@ class CGSpecializedSetter(CGAbstractExternMethod): return CGWrapper( CGSetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), pre=("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\n" + "let cx = &mut cx;\n" f"let this = &*(this as *const {self.descriptor.concreteType});\n") ) @@ -4719,7 +4721,7 @@ class CGStaticSetter(CGAbstractStaticBindingMethod): def generate_code(self) -> CGThing: nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr) - safeContext = CGGeneric("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\n") + safeContext = CGGeneric("let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());\nlet cx = &mut cx;\n") checkForArg = CGGeneric( "let args = CallArgs::from_vp(vp, argc);\n" "if argc == 0 {\n" @@ -4748,6 +4750,7 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter): assert all(ord(c) < 128 for c in forwardToAttrName) return CGGeneric(f""" let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()); +let cx = &mut cx; rooted!(&in(cx) let mut v = UndefinedValue()); if !JS_GetProperty(cx.raw_cx(), HandleObject::from_raw(obj), {str_to_cstr_ptr(attrName)}, v.handle_mut()) {{ return false; @@ -6856,6 +6859,7 @@ class CGClassConstructHook(CGAbstractExternMethod): def definition_body(self) -> CGThing: preamble = """let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()); +let cx = &mut cx; let args = CallArgs::from_vp(vp, argc); let global = D::GlobalScope::from_object(JS_CALLEE(cx.raw_cx(), vp).to_object()); """ @@ -6864,7 +6868,7 @@ let global = D::GlobalScope::from_object(JS_CALLEE(cx.raw_cx(), vp).to_object()) assert len(signatures) == 1 constructorCall = f""" >::call_html_constructor::( - &mut cx, + cx, &args, &global, PrototypeList::ID::{MakeNativeName(self.descriptor.name)}, @@ -6879,20 +6883,21 @@ let global = D::GlobalScope::from_object(JS_CALLEE(cx.raw_cx(), vp).to_object()) if len(self.exposureSet) == 1: args = [ f"global.downcast::().unwrap()", - "Some(desired_proto)", - "CanGc::note()" + "Some(desired_proto)" ] else: args = [ "global", "Some(desired_proto)", - "CanGc::note()" ] + if nativeName not in self.descriptor.cxMethods: + args += ['CanGc::from_cx(cx)'] + constructor = CGMethodCall(args, nativeName, True, self.descriptor, self.constructor) constructorCall = f""" call_default_constructor::( - &mut cx, + cx, &args, &global, PrototypeList::ID::{MakeNativeName(self.descriptor.name)}, @@ -7113,12 +7118,14 @@ class CGInterfaceTrait(CGThing): infallible = 'infallible' in descriptor.getExtendedAttributes(ctor) for (i, (rettype, arguments)) in enumerate(ctor.signatures()): name = (baseName or ctor.identifier.name) + ('_' * i) - args = list(method_arguments(descriptor, rettype, arguments)) + cx = name in descriptor.cxMethods + args = list(method_arguments(descriptor, rettype, arguments, cx=cx)) extra = [ ("global", f"&D::{exposedGlobal}"), ("proto", "Option"), - ("can_gc", "CanGc"), ] + if not cx: + extra += [("can_gc", "CanGc")] if args and args[0][0] == "cx": args = [args[0]] + extra + args[1:] else: