Skip to content

Commit d0a3eba

Browse files
fix(splicer): add destructor metadata
1 parent 044c63a commit d0a3eba

5 files changed

Lines changed: 138 additions & 10 deletions

File tree

crates/spidermonkey-embedding-splicer/src/bindgen.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub enum Resource {
2929
Constructor(String),
3030
Static(String),
3131
Method(String),
32+
Destructor(String),
3233
}
3334

3435
impl Resource {
@@ -38,6 +39,7 @@ impl Resource {
3839
Resource::Constructor(name) => format!("[constructor]{name}"),
3940
Resource::Static(name) => format!("[static]{name}.{fn_name}"),
4041
Resource::Method(name) => format!("[method]{name}.{fn_name}"),
42+
Resource::Destructor(name) => format!("[dtor]{name}"),
4143
}
4244
}
4345

@@ -65,6 +67,9 @@ impl Resource {
6567
fn_name.to_lower_camel_case()
6668
)
6769
}
70+
Resource::Destructor(name) => {
71+
format!("{}$dtor", name.to_lower_camel_case())
72+
}
6873
}
6974
}
7075
}
@@ -190,7 +195,8 @@ pub fn componentize_bindgen(
190195
Resource::None => None,
191196
Resource::Method(name)
192197
| Resource::Static(name)
193-
| Resource::Constructor(name) => Some(name),
198+
| Resource::Constructor(name)
199+
| Resource::Destructor(name) => Some(name),
194200
})
195201
.or_default()
196202
.push(item);
@@ -426,10 +432,12 @@ impl JsBindgen<'_> {
426432
}
427433
WorldItem::Interface { id, stability: _ } => {
428434
let iface = &self.resolve.interfaces[*id];
429-
for id in iface.types.values() {
430-
if let TypeDefKind::Resource = &self.resolve.types[*id].kind {
435+
let iface_name = interface_name(self.resolve, *id);
436+
for ty_id in iface.types.values() {
437+
if let TypeDefKind::Resource = &self.resolve.types[*ty_id].kind {
431438
self.resource_directions
432-
.insert(*id, AbiVariant::GuestExport);
439+
.insert(*ty_id, AbiVariant::GuestExport);
440+
self.resource_dtor_bindgen(name.clone(), iface_name.clone(), *ty_id);
433441
}
434442
}
435443
for (func_name, func) in &iface.functions {
@@ -991,6 +999,59 @@ impl JsBindgen<'_> {
991999
));
9921000
}
9931001

1002+
fn resource_dtor_bindgen(
1003+
&mut self,
1004+
export_name: String,
1005+
iface_name: Option<String>,
1006+
resource: TypeId,
1007+
) {
1008+
let resource_name = self.resolve.types[resource].name.as_ref().unwrap();
1009+
let resource_name_camel = resource_name.to_lower_camel_case();
1010+
let prefix = iface_name
1011+
.as_deref()
1012+
.map(|name| format!("{name}$"))
1013+
.unwrap_or_default();
1014+
let symbol_dispose = self.intrinsic(Intrinsic::SymbolDispose);
1015+
let symbol_resource_handle = self.intrinsic(Intrinsic::SymbolResourceHandle);
1016+
let binding_name = format!(
1017+
"export_{}",
1018+
binding_name(&format!("{resource_name_camel}$dtor"), &iface_name)
1019+
);
1020+
1021+
uwriteln!(
1022+
self.src,
1023+
"
1024+
async function {binding_name}(rep) {{
1025+
const entry = repTable.get(rep);
1026+
if (!entry) return;
1027+
repTable.delete(rep);
1028+
const resource = entry.rep;
1029+
delete resource[{symbol_resource_handle}];
1030+
finalizationRegistry_export${prefix}{resource_name_camel}.unregister(resource);
1031+
if (resource[{symbol_dispose}]) resource[{symbol_dispose}]();
1032+
}}
1033+
"
1034+
);
1035+
1036+
self.exports.push((
1037+
export_name,
1038+
BindingItem {
1039+
iface: true,
1040+
iface_name,
1041+
binding_name,
1042+
resource: Resource::Destructor(resource_name.clone()),
1043+
name: resource_name.clone(),
1044+
func: CoreFn {
1045+
params: vec![CoreTy::I32],
1046+
ret: None,
1047+
retptr: false,
1048+
retsize: 0,
1049+
paramptr: false,
1050+
},
1051+
},
1052+
));
1053+
}
1054+
9941055
fn core_fn(&self, func: &Function, sig: &WasmSignature) -> CoreFn {
9951056
CoreFn {
9961057
retsize: if sig.retptr {

crates/spidermonkey-embedding-splicer/src/splice.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,11 @@ pub fn splice_bindings(
199199
} else {
200200
export_name.clone()
201201
};
202-
exports.push((expt, map_core_fn(func)));
202+
exports.push((
203+
expt,
204+
map_core_fn(func),
205+
matches!(resource, bindgen::Resource::Destructor(_)),
206+
));
203207
}
204208

205209
let mut imports = Vec::new();
@@ -340,7 +344,7 @@ pub fn splice_bindings(
340344
pub fn splice(
341345
engine: Vec<u8>,
342346
imports: Vec<(String, String, CoreFn, Option<i32>)>,
343-
exports: Vec<(String, CoreFn)>,
347+
exports: Vec<(String, CoreFn, bool)>,
344348
features: Vec<Feature>,
345349
debug: bool,
346350
) -> Result<Vec<u8>> {
@@ -383,13 +387,13 @@ pub fn splice(
383387

384388
fn remove_if_exported_by_js(
385389
module: &mut Module,
386-
content_exports: &[(String, CoreFn)],
390+
content_exports: &[(String, CoreFn, bool)],
387391
name_start: &str,
388392
name_end: &str,
389393
) {
390394
let content_exports_run = content_exports
391395
.iter()
392-
.any(|(name, _)| name.starts_with(name_start) && name.ends_with(name_end));
396+
.any(|(name, _, _)| name.starts_with(name_start) && name.ends_with(name_end));
393397
if content_exports_run {
394398
let exported_run_fn = module
395399
.exports
@@ -846,7 +850,10 @@ fn synthesize_import_functions(
846850
Ok(())
847851
}
848852

849-
fn synthesize_export_functions(module: &mut Module, exports: &[(String, CoreFn)]) -> Result<()> {
853+
fn synthesize_export_functions(
854+
module: &mut Module,
855+
exports: &[(String, CoreFn, bool)],
856+
) -> Result<()> {
850857
let cabi_realloc = get_export_fid(
851858
module,
852859
&module
@@ -867,7 +874,7 @@ fn synthesize_export_functions(module: &mut Module, exports: &[(String, CoreFn)]
867874

868875
let memory = 0;
869876
// (2) Export call function synthesis
870-
for (export_num, (expt_name, expt_sig)) in exports.iter().enumerate() {
877+
for (export_num, (expt_name, expt_sig, inline_post_call)) in exports.iter().enumerate() {
871878
// Export function synthesis
872879
{
873880
// add the function type
@@ -1031,10 +1038,19 @@ fn synthesize_export_functions(module: &mut Module, exports: &[(String, CoreFn)]
10311038
}
10321039
}
10331040

1041+
if *inline_post_call {
1042+
func.i32_const(export_num as i32);
1043+
func.call(post_call);
1044+
}
1045+
10341046
let fid = func.finish_module(module);
10351047
module.exports.add_export_func((*expt_name).clone(), *fid);
10361048
}
10371049

1050+
if *inline_post_call {
1051+
continue;
1052+
}
1053+
10381054
// Post export function synthesis
10391055
// We always define a post-export since we use a bulk deallocation strategy
10401056
// add the function type
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
let disposeCount = 0;
2+
const disposeSymbol = Symbol.dispose || Symbol.for('dispose');
3+
4+
class Example {
5+
constructor(id) {
6+
this.id = id;
7+
}
8+
9+
getId() {
10+
return this.id;
11+
}
12+
13+
[disposeSymbol]() {
14+
disposeCount += 1;
15+
}
16+
}
17+
18+
export const resources = {
19+
Example,
20+
disposeCount() {
21+
return disposeCount;
22+
},
23+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { strictEqual } from 'node:assert';
2+
3+
const disposeSymbol = Symbol.dispose || Symbol.for('dispose');
4+
5+
export function test(instance) {
6+
const resource = new instance.resources.Example(42);
7+
8+
strictEqual(resource.getId(), 42);
9+
resource[disposeSymbol]();
10+
strictEqual(instance.resources.disposeCount(), 1);
11+
12+
resource[disposeSymbol]();
13+
strictEqual(instance.resources.disposeCount(), 1);
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package test:test;
2+
3+
interface resources {
4+
resource example {
5+
constructor(id: u32);
6+
get-id: func() -> u32;
7+
}
8+
9+
dispose-count: func() -> u32;
10+
}
11+
12+
world test {
13+
export resources;
14+
}

0 commit comments

Comments
 (0)