1use 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
56unsafe 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
113pub(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
149pub(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
169pub(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}