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::{MutableHandle, RootedGuard};
9use js::jsapi::{CallArgs, JSClass, JSFunctionSpec, JSObject, JSPropertySpec};
10use js::rooted;
11use js::rust::HandleObject;
12use js::rust::wrappers2::{
13    GetRealmErrorPrototype, GetRealmIteratorPrototype, GetRealmObjectPrototype, JS_NewPlainObject,
14};
15
16use crate::DomTypes;
17use crate::codegen::PrototypeList::{self};
18use crate::constant::ConstantSpec;
19use crate::error::throw_constructor_without_new;
20use crate::guard::Guard;
21use crate::interface::{
22    create_callback_interface_object, create_interface_prototype_object, get_desired_proto,
23};
24use crate::js::rust::GCMethods;
25use crate::namespace::{NamespaceObjectClass, create_namespace_object};
26use crate::utils::ProtoOrIfaceArray;
27
28pub(crate) unsafe fn call_default_constructor<D: crate::DomTypes>(
29    cx: &mut js::context::JSContext,
30    args: &CallArgs,
31    global: &D::GlobalScope,
32    proto_id: PrototypeList::ID,
33    ctor_name: &str,
34    creator: unsafe fn(&mut js::context::JSContext, HandleObject, *mut ProtoOrIfaceArray),
35    constructor: impl FnOnce(
36        &mut js::context::JSContext,
37        &CallArgs,
38        &D::GlobalScope,
39        HandleObject,
40    ) -> bool,
41) -> bool {
42    if !args.is_constructing() {
43        throw_constructor_without_new(cx.into(), ctor_name);
44        return false;
45    }
46
47    rooted!(&in(cx) let mut desired_proto = ptr::null_mut::<JSObject>());
48    let proto_result = get_desired_proto(cx, args, proto_id, creator, desired_proto.handle_mut());
49    if proto_result.is_err() {
50        return false;
51    }
52
53    constructor(cx, args, global, desired_proto.handle())
54}
55
56/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
57unsafe fn post_barrier(
58    constructor: usize,
59    cache: *mut ProtoOrIfaceArray,
60    object: RootedGuard<'_, *mut JSObject>,
61) -> RootedGuard<'_, *mut JSObject> {
62    unsafe {
63        assert!((*cache)[constructor].is_null());
64        (*cache)[constructor] = object.get();
65        <*mut JSObject>::post_barrier(
66            (*cache).as_mut_ptr().add(constructor),
67            ptr::null_mut(),
68            object.get(),
69        );
70    }
71    object
72}
73
74pub(crate) struct NamespaceInit {
75    pub(crate) is_proto_hack: bool,
76    pub(crate) static_methods: &'static [Guard<&'static [JSFunctionSpec]>],
77    pub(crate) namespace_object_class: &'static NamespaceObjectClass,
78    pub(crate) constructor_name: PrototypeList::Constructor,
79    pub(crate) constants: &'static [Guard<&'static [ConstantSpec]>],
80    pub(crate) attributes: &'static [Guard<&'static [JSPropertySpec]>],
81    pub(crate) name: &'static CStr,
82}
83
84pub(crate) struct CallbackInit {
85    pub(crate) constants: &'static [Guard<&'static [ConstantSpec]>],
86    pub(crate) constructor_name: PrototypeList::Constructor,
87    pub(crate) name: &'static CStr,
88}
89
90pub(crate) enum InitType {
91    RealmErrorPrototype,
92    RealmIteratorPrototype,
93    RealmObjectPrototype,
94    Parent(ParentFn),
95}
96
97type HasPropertyFn =
98    fn(&mut js::context::JSContext, HandleObject, MutableHandle<'_, *mut JSObject>);
99
100type ParentFn = fn(&mut js::context::JSContext, HandleObject, MutableHandle<'_, *mut JSObject>);
101
102pub(crate) struct InterfaceInit {
103    pub(crate) init_type: InitType,
104    pub(crate) has_named_properties_object: Option<HasPropertyFn>,
105    pub(crate) prototype_class: &'static JSClass,
106    pub(crate) methods: &'static [Guard<&'static [JSFunctionSpec]>],
107    pub(crate) attrs: &'static [Guard<&'static [JSPropertySpec]>],
108    pub(crate) consts: &'static [Guard<&'static [ConstantSpec]>],
109    pub(crate) unscopables: &'static [&'static CStr],
110    pub(crate) prototype_id: PrototypeList::ID,
111}
112
113/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
114pub(crate) unsafe fn create_namespace_interface_objects<D: DomTypes>(
115    cx: &mut js::context::JSContext,
116    init: NamespaceInit,
117    global: HandleObject,
118    cache: *mut ProtoOrIfaceArray,
119) {
120    rooted!(&in(cx) let mut proto: *mut JSObject = std::ptr::null_mut());
121    unsafe {
122        if init.is_proto_hack {
123            proto.set(GetRealmObjectPrototype(cx))
124        } else {
125            proto.set(JS_NewPlainObject(cx))
126        };
127    }
128
129    assert!(!proto.is_null());
130    rooted!(&in(cx) let mut namespace = ptr::null_mut::<JSObject>());
131    create_namespace_object::<D>(
132        cx,
133        global,
134        proto.handle(),
135        init.namespace_object_class,
136        init.static_methods,
137        init.attributes,
138        init.constants,
139        init.name,
140        namespace.handle_mut(),
141    );
142    assert!(!namespace.is_null());
143
144    unsafe {
145        post_barrier(init.constructor_name as usize, cache, namespace);
146    }
147}
148
149/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
150pub(crate) unsafe fn create_callback_interface_objects<D: DomTypes>(
151    cx: &mut js::context::JSContext,
152    init: CallbackInit,
153    global: HandleObject,
154    cache: *mut ProtoOrIfaceArray,
155) {
156    rooted!(&in(cx) let mut interface = ptr::null_mut::<JSObject>());
157    create_callback_interface_object::<D>(
158        cx,
159        global,
160        init.constants,
161        init.name,
162        interface.handle_mut(),
163    );
164    unsafe {
165        post_barrier(init.constructor_name as usize, cache, interface);
166    }
167}
168
169/// SAFETY: cache is a non-null pointer to a valid ProtoOrIfaceArray object.
170/// The returned object needs to be rooted.
171pub(crate) unsafe fn create_interface<D: DomTypes>(
172    cx: &mut js::context::JSContext,
173    init: InterfaceInit,
174    global: HandleObject,
175    cache: *mut ProtoOrIfaceArray,
176) -> *mut JSObject {
177    rooted!(&in(cx) let mut prototype_proto = ptr::null_mut::<JSObject>());
178    match init.init_type {
179        InitType::RealmErrorPrototype => unsafe {
180            prototype_proto.set(GetRealmErrorPrototype(cx));
181        },
182        InitType::RealmIteratorPrototype => unsafe {
183            prototype_proto.set(GetRealmIteratorPrototype(cx));
184        },
185        InitType::RealmObjectPrototype => unsafe {
186            prototype_proto.set(GetRealmObjectPrototype(cx));
187        },
188        InitType::Parent(f) => f(cx, global, prototype_proto.handle_mut()),
189    };
190    assert!(!prototype_proto.is_null());
191
192    if let Some(f) = init.has_named_properties_object {
193        rooted!(&in(cx) let mut prototype_proto_proto = prototype_proto.get());
194        f(
195            cx,
196            prototype_proto_proto.handle(),
197            prototype_proto.handle_mut(),
198        );
199        assert!(!prototype_proto_proto.is_null());
200    }
201
202    rooted!(&in(cx) let mut prototype = ptr::null_mut::<JSObject>());
203    create_interface_prototype_object::<D>(
204        cx,
205        global,
206        prototype_proto.handle(),
207        init.prototype_class,
208        init.methods,
209        init.attrs,
210        init.consts,
211        init.unscopables,
212        prototype.handle_mut(),
213    );
214    assert!(!prototype.is_null());
215    unsafe { *post_barrier(init.prototype_id as usize, cache, prototype) }
216}