Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: CGClassConstructHook into Handwritten Helpers #31949

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 14 additions & 47 deletions components/script/dom/bindings/codegen/CodegenRust.py
Original file line number Diff line number Diff line change
Expand Up @@ -6109,9 +6109,6 @@ def generate_code(self):


class CGClassConstructHook(CGAbstractExternMethod):
"""
JS-visible constructor for our objects
"""
def __init__(self, descriptor, constructor=None):
args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')]
name = CONSTRUCT_HOOK_NAME
Expand All @@ -6125,51 +6122,21 @@ def __init__(self, descriptor, constructor=None):
self.exposureSet = descriptor.interface.exposureSet

def definition_body(self):
preamble = """let cx = SafeJSContext::from_ptr(cx);
let args = CallArgs::from_vp(vp, argc);
let global = GlobalScope::from_object(JS_CALLEE(*cx, vp).to_object());
"""
preamble = "let cx = SafeJSContext::from_ptr(cx);\n"
preamble += "let args = CallArgs::from_vp(vp, argc);\n"
preamble += "let global = GlobalScope::from_object(JS_CALLEE(*cx, vp).to_object());\n"

rust_function_call = ""
if len(self.exposureSet) == 1:
preamble += """\
let global = DomRoot::downcast::<dom::types::%s>(global).unwrap();
""" % list(self.exposureSet)[0]
if self.constructor.isHTMLConstructor():
signatures = self.constructor.signatures()
assert len(signatures) == 1
constructorCall = CGGeneric("""dom::bindings::htmlconstructor::call_html_constructor::<dom::types::%s>(
cx,
&args,
&global,
PrototypeList::ID::%s,
CreateInterfaceObjects,
)""" % (self.descriptor.name, MakeNativeName(self.descriptor.name)))
else:
ctorName = GetConstructorNameForReporting(self.descriptor, self.constructor)
preamble += """
if !callargs_is_constructing(&args) {
throw_constructor_without_new(*cx, "%s");
return false;
}
if self.constructor.isHTMLConstructor():
rust_function_call = f"construct_html_custom(&global, cx, &args, \"{self.descriptor.name}\");\n"
else:
ctorName = GetConstructorNameForReporting(self.descriptor, self.constructor)
rust_function_call = f"construct_default_custom(&global, cx, &args, \"{self.descriptor.name}\", \"{ctorName}\");\n"

# Assuming CGList and CGGeneric can handle the combination of Rust code and preamble
return CGList([CGGeneric(preamble), CGGeneric(rust_function_call)])

rooted!(in(*cx) let mut desired_proto = ptr::null_mut::<JSObject>());
let proto_result = get_desired_proto(
cx,
&args,
PrototypeList::ID::%s,
CreateInterfaceObjects,
desired_proto.handle_mut(),
);
assert!(proto_result.is_ok());
if proto_result.is_err() {
return false;
}
""" % (ctorName, MakeNativeName(self.descriptor.name))
name = self.constructor.identifier.name
nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
args = ["&global", "Some(desired_proto.handle())"]
constructorCall = CGMethodCall(args, nativeName, True,
self.descriptor, self.constructor)
return CGList([CGGeneric(preamble), constructorCall])


class CGClassFinalizeHook(CGAbstractClassHook):
Expand Down Expand Up @@ -7999,4 +7966,4 @@ def SupportedDomApis(config):
interfaces += [interface_template.replace('${interface}', name)]

return CGGeneric((base_template.replace('${apis}', '\n'.join(apis))
.replace('${interfaces}', '\n'.join(interfaces))))
.replace('${interfaces}', '\n'.join(interfaces))))
59 changes: 59 additions & 0 deletions components/script/dom/bindings/construct_hook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// construct_hook.rs

use std::ptr;
use crate::dom::bindings::htmlconstructor;
use crate::dom::bindings::utils::{GetConstructorNameForReporting, MakeNativeName};
use crate::dom::bindings::root::DomRoot;
use crate::jsapi::{JSContext, JSObject, CallArgs, GlobalScope, SafeJSContext};
use crate::dom::bindings::import::module::callargs_is_constructing;
use crate::dom::bindings::import::module::throw_constructor_without_new;
use crate::dom::bindings::codegen::PrototypeList::ID;
// Function for HTML constructor case
pub unsafe fn construct_html_custom(global: &GlobalScope, cx: *mut JSContext, args: &CallArgs, descriptor_name: &str) -> bool {
let global = DomRoot::downcast::<dom::types::Global>(global).unwrap(); // Adjust type as needed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this needs to be a downcast to Window, not Global, for the call to call_html_constructor to succeed.

let prototype_id = match descriptor_name {
"HTMLDivElement" => PrototypeList::ID::HTMLDivElement,
Comment on lines +12 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of taking string argument, we should take a PrototypeList::ID argument that we can use directly. The python code can construct the required value.

"HTMLSpanElement" => PrototypeList::ID::HTMLSpanElement,
// Add cases for other descriptor names as needed
_ => panic!("Unsupported descriptor name: {}", descriptor_name),
};
htmlconstructor::call_html_constructor::<dom::types::Global>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using <dom::types::Global> here, we need to add a generic type to this function (pub unsafe fn construct_html_custom<T: DerivedFrom<Element> + DomObject>) and use that generic type (htmlconstructor::call_html_constructor::<T>).

cx,
args,
&global,
prototype_id, // Adjust as needed
descriptor_name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing the creator argument here. We should accept that as an argument in construct_html_custom and let the python code pass the appropriate function value like it does before the changes in this PR.

)
}

// Function for non-HTML constructor case
pub unsafe fn construct_default_custom(global: &GlobalScope, cx: *mut JSContext, args: &CallArgs, descriptor_name: &str, ctor_name: &str) -> bool {
let prototype_id = match descriptor_name {
"HTMLDivElement" => PrototypeList::ID::HTMLDivElement,
Comment on lines +30 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about using a PrototypeList::ID argument instead of a string.

"HTMLSpanElement" => PrototypeList::ID::HTMLSpanElement,
// Add cases for other descriptor names as needed
_ => panic!("Unsupported descriptor name: {}", descriptor_name),
};
if !callargs_is_constructing(args) {
throw_constructor_without_new(cx, ctor_name);
return false;
}

rooted!(in(cx) let mut desired_proto = ptr::null_mut::<JSObject>());
let proto_result = get_desired_proto(
cx,
args,
prototype_id, // Adjust as needed
descriptor_name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the creator argument here as earlier. This should be passed in by the python code.

desired_proto.handle_mut(),
);
assert!(proto_result.is_ok());
if proto_result.is_err() {
return false;
}

let nativeName = MakeNativeName(ctor_name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MakeNativeName is a python function. This is going to be a bit tricky; I think we're going to need to leave the actual constructor call in the generated python code, because there's a lot of unique logic in there. What we can do is something like this:

pub unsafe fn construct_default_custom(..., constructor: impl FnOnce(*mut JSContext, &CallArgs, &GlobalScope) -> bool) -> bool {
  ...
  constructor(cx, args, global)
}

Then modify the python code to call f"construct_default_custom(&global, cx, &args, \"{ctorName}\", |cx, args, global| {{ {constructorInvoke} }});\n" where constructorInvoke is the result of calling .define() on the constructorCall CGMethodCall value.

let args = vec![&global, Some(desired_proto.handle())];
// Placeholder for actual constructor method call
true // Return true to indicate success, adjust as per actual logic
}
3 changes: 3 additions & 0 deletions components/script/dom/bindings/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ pub mod module {
pub use crate::dom::bindings::finalize::{
finalize_common, finalize_global, finalize_weak_referenceable,
};
pub use crate::dom::bindings::construct_hook::{
construct_html_custom,construct_default_custom
};
pub use crate::dom::bindings::guard::{Condition, Guard};
pub use crate::dom::bindings::htmlconstructor::{
pop_current_element_queue, push_new_element_queue,
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ pub mod cell;
pub mod constant;
pub mod conversions;
pub mod error;
pub mod construct_hook;
pub mod finalize;
pub mod guard;
pub mod htmlconstructor;
Expand Down