Skip to main content

script_bindings/
constructor.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::ffi::CStr;
6use std::ptr;
7
8use js::gc::RootedGuard;
9use js::jsapi::{CallArgs, JSFunctionSpec, JSObject, JSPropertySpec};
10use js::rooted;
11use js::rust::HandleObject;
12use js::rust::wrappers2::{GetRealmObjectPrototype, JS_NewPlainObject};
13
14use crate::DomTypes;
15use crate::codegen::PrototypeList::{self};
16use crate::constant::ConstantSpec;
17use crate::error::throw_constructor_without_new;
18use crate::guard::Guard;
19use crate::interface::{create_callback_interface_object, get_desired_proto};
20use crate::js::rust::GCMethods;
21use crate::namespace::{NamespaceObjectClass, create_namespace_object};
22use crate::utils::ProtoOrIfaceArray;
23
24pub(crate) unsafe fn call_default_constructor<D: crate::DomTypes>(
25    cx: &mut js::context::JSContext,
26    args: &CallArgs,
27    global: &D::GlobalScope,
28    proto_id: PrototypeList::ID,
29    ctor_name: &str,
30    creator: unsafe fn(&mut js::context::JSContext, HandleObject, *mut ProtoOrIfaceArray),
31    constructor: impl FnOnce(
32        &mut js::context::JSContext,
33        &CallArgs,
34        &D::GlobalScope,
35        HandleObject,
36    ) -> bool,
37) -> bool {
38    if !args.is_constructing() {
39        throw_constructor_without_new(cx.into(), ctor_name);
40        return false;
41    }
42
43    rooted!(&in(cx) let mut desired_proto = ptr::null_mut::<JSObject>());
44    let proto_result = get_desired_proto(cx, args, proto_id, creator, desired_proto.handle_mut());
45    if proto_result.is_err() {
46        return false;
47    }
48
49    constructor(cx, args, global, desired_proto.handle())
50}
51
52/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
53unsafe fn post_barrier(
54    constructor: PrototypeList::Constructor,
55    cache: *mut ProtoOrIfaceArray,
56    object: RootedGuard<'_, *mut JSObject>,
57) {
58    unsafe {
59        assert!((*cache)[constructor as usize].is_null());
60        (*cache)[constructor as usize] = object.get();
61        <*mut JSObject>::post_barrier(
62            (*cache).as_mut_ptr().offset(constructor as isize),
63            ptr::null_mut(),
64            object.get(),
65        );
66    }
67}
68
69pub(crate) struct NamespaceInit {
70    pub(crate) is_proto_hack: bool,
71    pub(crate) static_methods: &'static [Guard<&'static [JSFunctionSpec]>],
72    pub(crate) namespace_object_class: &'static NamespaceObjectClass,
73    pub(crate) constructor_name: PrototypeList::Constructor,
74    pub(crate) constants: &'static [Guard<&'static [ConstantSpec]>],
75    pub(crate) attributes: &'static [Guard<&'static [JSPropertySpec]>],
76    pub(crate) name: &'static CStr,
77}
78
79pub(crate) struct CallbackInit {
80    pub(crate) constants: &'static [Guard<&'static [ConstantSpec]>],
81    pub(crate) constructor_name: PrototypeList::Constructor,
82    pub(crate) name: &'static CStr,
83}
84
85/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
86pub(crate) unsafe fn create_namespace_interface_objects<D: DomTypes>(
87    cx: &mut js::context::JSContext,
88    init: NamespaceInit,
89    global: HandleObject,
90    cache: *mut ProtoOrIfaceArray,
91) {
92    rooted!(&in(cx) let mut proto: *mut JSObject = std::ptr::null_mut());
93    unsafe {
94        if init.is_proto_hack {
95            proto.set(GetRealmObjectPrototype(cx))
96        } else {
97            proto.set(JS_NewPlainObject(cx))
98        };
99    }
100
101    assert!(!proto.is_null());
102    rooted!(&in(cx) let mut namespace = ptr::null_mut::<JSObject>());
103    create_namespace_object::<D>(
104        cx,
105        global,
106        proto.handle(),
107        init.namespace_object_class,
108        init.static_methods,
109        init.attributes,
110        init.constants,
111        init.name,
112        namespace.handle_mut(),
113    );
114    assert!(!namespace.is_null());
115
116    unsafe {
117        post_barrier(init.constructor_name, cache, namespace);
118    }
119}
120
121/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
122pub(crate) unsafe fn create_callback_interface_objects<D: DomTypes>(
123    cx: &mut js::context::JSContext,
124    init: CallbackInit,
125    global: HandleObject,
126    cache: *mut ProtoOrIfaceArray,
127) {
128    rooted!(&in(cx) let mut interface = ptr::null_mut::<JSObject>());
129    create_callback_interface_object::<D>(
130        cx,
131        global,
132        init.constants,
133        init.name,
134        interface.handle_mut(),
135    );
136    unsafe {
137        post_barrier(init.constructor_name, cache, interface);
138    }
139}