Skip to content

Commit 5ec418c

Browse files
feat(splicer): add destructor metadata (#345)
1 parent 12c2b4a commit 5ec418c

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
@@ -31,6 +31,7 @@ pub enum Resource {
3131
Constructor(String),
3232
Static(String),
3333
Method(String),
34+
Destructor(String),
3435
}
3536

3637
impl Resource {
@@ -40,6 +41,7 @@ impl Resource {
4041
Resource::Constructor(name) => format!("[constructor]{name}"),
4142
Resource::Static(name) => format!("[static]{name}.{fn_name}"),
4243
Resource::Method(name) => format!("[method]{name}.{fn_name}"),
44+
Resource::Destructor(name) => format!("[dtor]{name}"),
4345
}
4446
}
4547

@@ -67,6 +69,9 @@ impl Resource {
6769
fn_name.to_lower_camel_case()
6870
)
6971
}
72+
Resource::Destructor(name) => {
73+
format!("{}$dtor", name.to_lower_camel_case())
74+
}
7075
}
7176
}
7277
}
@@ -193,7 +198,8 @@ pub fn componentize_bindgen(
193198
Resource::None => None,
194199
Resource::Method(name)
195200
| Resource::Static(name)
196-
| Resource::Constructor(name) => Some(name),
201+
| Resource::Constructor(name)
202+
| Resource::Destructor(name) => Some(name),
197203
})
198204
.or_default()
199205
.push(item);
@@ -439,10 +445,12 @@ impl JsBindgen<'_> {
439445
id, stability: _, ..
440446
} => {
441447
let iface = &self.resolve.interfaces[*id];
442-
for id in iface.types.values() {
443-
if let TypeDefKind::Resource = &self.resolve.types[*id].kind {
448+
let iface_name = interface_name(self.resolve, *id);
449+
for ty_id in iface.types.values() {
450+
if let TypeDefKind::Resource = &self.resolve.types[*ty_id].kind {
444451
self.resource_directions
445-
.insert(*id, AbiVariant::GuestExport);
452+
.insert(*ty_id, AbiVariant::GuestExport);
453+
self.resource_dtor_bindgen(name.clone(), iface_name.clone(), *ty_id);
446454
}
447455
}
448456
for (func_name, func) in &iface.functions {
@@ -1030,6 +1038,59 @@ impl JsBindgen<'_> {
10301038
));
10311039
}
10321040

1041+
fn resource_dtor_bindgen(
1042+
&mut self,
1043+
export_name: String,
1044+
iface_name: Option<String>,
1045+
resource: TypeId,
1046+
) {
1047+
let resource_name = self.resolve.types[resource].name.as_ref().unwrap();
1048+
let resource_name_camel = resource_name.to_lower_camel_case();
1049+
let prefix = iface_name
1050+
.as_deref()
1051+
.map(|name| format!("{name}$"))
1052+
.unwrap_or_default();
1053+
let symbol_dispose = self.intrinsic(Intrinsic::SymbolDispose);
1054+
let symbol_resource_handle = self.intrinsic(Intrinsic::SymbolResourceHandle);
1055+
let binding_name = format!(
1056+
"export_{}",
1057+
binding_name(&format!("{resource_name_camel}$dtor"), &iface_name)
1058+
);
1059+
1060+
uwriteln!(
1061+
self.src,
1062+
"
1063+
async function {binding_name}(rep) {{
1064+
const entry = repTable.get(rep);
1065+
if (!entry) return;
1066+
repTable.delete(rep);
1067+
const resource = entry.rep;
1068+
delete resource[{symbol_resource_handle}];
1069+
finalizationRegistry_export${prefix}{resource_name_camel}.unregister(resource);
1070+
if (resource[{symbol_dispose}]) resource[{symbol_dispose}]();
1071+
}}
1072+
"
1073+
);
1074+
1075+
self.exports.push((
1076+
export_name,
1077+
BindingItem {
1078+
iface: true,
1079+
iface_name,
1080+
binding_name,
1081+
resource: Resource::Destructor(resource_name.clone()),
1082+
name: resource_name.clone(),
1083+
func: CoreFn {
1084+
params: vec![CoreTy::I32],
1085+
ret: None,
1086+
retptr: false,
1087+
retsize: 0,
1088+
paramptr: false,
1089+
},
1090+
},
1091+
));
1092+
}
1093+
10331094
fn core_fn(&self, func: &Function, sig: &WasmSignature) -> CoreFn {
10341095
CoreFn {
10351096
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
@@ -193,7 +193,11 @@ pub fn splice_bindings(
193193
} else {
194194
export_name.clone()
195195
};
196-
exports.push((expt, map_core_fn(func)));
196+
exports.push((
197+
expt,
198+
map_core_fn(func),
199+
matches!(resource, bindgen::Resource::Destructor(_)),
200+
));
197201
}
198202

199203
let mut imports = Vec::new();
@@ -334,7 +338,7 @@ pub fn splice_bindings(
334338
pub fn splice(
335339
engine: Vec<u8>,
336340
imports: Vec<(String, String, CoreFn, Option<i32>)>,
337-
exports: Vec<(String, CoreFn)>,
341+
exports: Vec<(String, CoreFn, bool)>,
338342
features: Vec<Feature>,
339343
debug: bool,
340344
) -> Result<Vec<u8>> {
@@ -381,13 +385,13 @@ pub fn splice(
381385

382386
fn remove_if_exported_by_js(
383387
module: &mut Module,
384-
content_exports: &[(String, CoreFn)],
388+
content_exports: &[(String, CoreFn, bool)],
385389
name_start: &str,
386390
name_end: &str,
387391
) {
388392
let content_exports_run = content_exports
389393
.iter()
390-
.any(|(name, _)| name.starts_with(name_start) && name.ends_with(name_end));
394+
.any(|(name, _, _)| name.starts_with(name_start) && name.ends_with(name_end));
391395
if content_exports_run {
392396
let exported_run_fn = module
393397
.exports
@@ -859,7 +863,10 @@ fn synthesize_import_functions(
859863
Ok(())
860864
}
861865

862-
fn synthesize_export_functions(module: &mut Module, exports: &[(String, CoreFn)]) -> Result<()> {
866+
fn synthesize_export_functions(
867+
module: &mut Module,
868+
exports: &[(String, CoreFn, bool)],
869+
) -> Result<()> {
863870
let cabi_realloc = get_export_fid(
864871
module,
865872
&module
@@ -880,7 +887,7 @@ fn synthesize_export_functions(module: &mut Module, exports: &[(String, CoreFn)]
880887

881888
let memory = 0;
882889
// (2) Export call function synthesis
883-
for (export_num, (expt_name, expt_sig)) in exports.iter().enumerate() {
890+
for (export_num, (expt_name, expt_sig, inline_post_call)) in exports.iter().enumerate() {
884891
// Export function synthesis
885892
{
886893
// add the function type
@@ -1044,10 +1051,19 @@ fn synthesize_export_functions(module: &mut Module, exports: &[(String, CoreFn)]
10441051
}
10451052
}
10461053

1054+
if *inline_post_call {
1055+
func.i32_const(export_num as i32);
1056+
func.call(post_call);
1057+
}
1058+
10471059
let fid = func.finish_module(module);
10481060
module.exports.add_export_func((*expt_name).clone(), *fid);
10491061
}
10501062

1063+
if *inline_post_call {
1064+
continue;
1065+
}
1066+
10511067
// Post export function synthesis
10521068
// We always define a post-export since we use a bulk deallocation strategy
10531069
// 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)