Skip to content

Commit e6e9fc7

Browse files
refactor: splicer WIT and generated bindings (#252)
* refactor: splicer WIT and generated bindings This commit refactors the WIT for the splicer, and moves the generated bindings to their own module file which is likely a bit easier to grok for most, and a good place to put extra functionality (e.g. custom impls for the generated types). * fix: address code review comments
1 parent bf09b7b commit e6e9fc7

File tree

9 files changed

+181
-182
lines changed

9 files changed

+181
-182
lines changed

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

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use anyhow::{Context, Result};
2-
use clap::{Parser, Subcommand};
31
use std::fs;
42
use std::path::PathBuf;
3+
use std::str::FromStr;
54

6-
use spidermonkey_embedding_splicer::wit::Features;
5+
use anyhow::{Context, Result};
6+
use clap::{Parser, Subcommand};
7+
8+
use spidermonkey_embedding_splicer::wit::exports::local::spidermonkey_embedding_splicer::splicer::Features;
79
use spidermonkey_embedding_splicer::{splice, stub_wasi};
810

911
#[derive(Parser, Debug)]
@@ -62,27 +64,6 @@ enum Commands {
6264
},
6365
}
6466

65-
/// Maps the list of features passed as strings into the list of features as given by the enum
66-
///
67-
/// enum features {
68-
/// stdio,
69-
/// clocks,
70-
/// random,
71-
/// http,
72-
///}
73-
fn map_features(features: &Vec<String>) -> Vec<Features> {
74-
features
75-
.iter()
76-
.map(|f| match f.as_str() {
77-
"stdio" => Features::Stdio,
78-
"clocks" => Features::Clocks,
79-
"random" => Features::Random,
80-
"http" => Features::Http,
81-
_ => panic!("Unknown feature: {}", f),
82-
})
83-
.collect()
84-
}
85-
8667
fn main() -> Result<()> {
8768
let cli = Cli::parse();
8869

@@ -98,7 +79,10 @@ fn main() -> Result<()> {
9879
.with_context(|| format!("Failed to read input file: {}", input.display()))?;
9980

10081
let wit_path_str = wit_path.as_ref().map(|p| p.to_string_lossy().to_string());
101-
let features = map_features(&features);
82+
let features = features
83+
.iter()
84+
.map(|v| Features::from_str(v))
85+
.collect::<Result<Vec<_>>>()?;
10286

10387
let result = stub_wasi::stub_wasi(wasm, features, None, wit_path_str, world_name)
10488
.map_err(|e| anyhow::anyhow!(e))?;
@@ -131,13 +115,13 @@ fn main() -> Result<()> {
131115

132116
let result = splice::splice_bindings(engine, world_name, wit_path_str, None, debug)
133117
.map_err(|e| anyhow::anyhow!(e))?;
134-
fs::write(&out_dir.join("component.wasm"), result.wasm).with_context(|| {
118+
fs::write(out_dir.join("component.wasm"), result.wasm).with_context(|| {
135119
format!(
136120
"Failed to write output file: {}",
137121
out_dir.join("component.wasm").display()
138122
)
139123
})?;
140-
fs::write(&out_dir.join("initializer.js"), result.js_bindings).with_context(|| {
124+
fs::write(out_dir.join("initializer.js"), result.js_bindings).with_context(|| {
141125
format!(
142126
"Failed to write output file: {}",
143127
out_dir.join("initializer.js").display()

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

Lines changed: 54 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
191191
for (specifier, by_resource) in by_specifier_by_resource {
192192
let mut specifier_list = Vec::new();
193193
for (resource, items) in by_resource {
194-
let item = items.iter().next().unwrap();
194+
let item = items.first().unwrap();
195195
if let Some(resource) = resource {
196196
let export_name = resource.to_upper_camel_case();
197197
let binding_name = binding_name(&export_name, &item.iface_name);
@@ -241,7 +241,7 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
241241
if let TypeDefKind::Resource = &ty.kind {
242242
let iface_prefix = interface_name(resolve, *iface_id)
243243
.map(|s| format!("{s}$"))
244-
.unwrap_or_else(String::new);
244+
.unwrap_or_default();
245245
let resource_name_camel = ty.name.as_ref().unwrap().to_lower_camel_case();
246246
let resource_name_kebab = ty.name.as_ref().unwrap().to_kebab_case();
247247
let module_name = format!("[export]{key_name}");
@@ -295,11 +295,8 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
295295
WorldItem::Function(_) => {}
296296
WorldItem::Type(id) => {
297297
let ty = &resolve.types[*id];
298-
match ty.kind {
299-
TypeDefKind::Resource => {
300-
imported_resource_modules.insert(*id, key_name.clone());
301-
}
302-
_ => {}
298+
if ty.kind == TypeDefKind::Resource {
299+
imported_resource_modules.insert(*id, key_name.clone());
303300
}
304301
}
305302
}
@@ -390,7 +387,7 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
390387
impl JsBindgen<'_> {
391388
fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
392389
self.all_intrinsics.insert(intrinsic);
393-
return intrinsic.name().to_string();
390+
intrinsic.name().to_string()
394391
}
395392

396393
fn exports_bindgen(
@@ -448,14 +445,7 @@ impl JsBindgen<'_> {
448445
match export {
449446
WorldItem::Function(func) => {
450447
let local_name = self.local_names.create_once(&func.name).to_string();
451-
self.export_bindgen(
452-
name.into(),
453-
false,
454-
None,
455-
&local_name,
456-
StringEncoding::UTF8,
457-
func,
458-
);
448+
self.export_bindgen(name, false, None, &local_name, StringEncoding::UTF8, func);
459449
self.esm_bindgen.add_export_func(
460450
None,
461451
local_name.to_string(),
@@ -484,7 +474,7 @@ impl JsBindgen<'_> {
484474
interface_name(self.resolve, *id),
485475
&local_name,
486476
StringEncoding::UTF8,
487-
&func,
477+
func,
488478
);
489479
self.esm_bindgen.add_export_func(
490480
Some(name),
@@ -501,7 +491,7 @@ impl JsBindgen<'_> {
501491
let local_name = self
502492
.local_names
503493
.get_or_create(
504-
&format!("resource:{resource_name}"),
494+
format!("resource:{resource_name}"),
505495
&resource_name,
506496
)
507497
.0
@@ -512,10 +502,10 @@ impl JsBindgen<'_> {
512502
interface_name(self.resolve, *id),
513503
&local_name,
514504
StringEncoding::UTF8,
515-
&func,
505+
func,
516506
);
517507
self.esm_bindgen.ensure_exported_resource(
518-
Some(&name),
508+
Some(name),
519509
local_name,
520510
resource_name,
521511
);
@@ -547,7 +537,7 @@ impl JsBindgen<'_> {
547537
.as_ref()
548538
.unwrap()
549539
.to_upper_camel_case(),
550-
&iface_name,
540+
iface_name,
551541
);
552542

553543
uwriteln!(self.src, "\nclass import_{name} {{");
@@ -568,7 +558,7 @@ impl JsBindgen<'_> {
568558
let prefix = iface_name
569559
.as_deref()
570560
.map(|s| format!("{s}$"))
571-
.unwrap_or(String::new());
561+
.unwrap_or_default();
572562

573563
let resource_symbol = self.intrinsic(Intrinsic::SymbolResourceHandle);
574564
let dispose_symbol = self.intrinsic(Intrinsic::SymbolDispose);
@@ -646,44 +636,38 @@ impl JsBindgen<'_> {
646636
}
647637
WorldItem::Type(id) => {
648638
let ty = &self.resolve.types[*id];
649-
match ty.kind {
650-
TypeDefKind::Resource => {
651-
self.resource_directions
652-
.insert(*id, AbiVariant::GuestImport);
653-
654-
let resource_name = ty.name.as_ref().unwrap();
655-
656-
let mut resource_fns = Vec::new();
657-
for (_, impt) in &self.resolve.worlds[self.world].imports {
658-
match impt {
659-
WorldItem::Function(function) => {
660-
let stripped = if let Some(stripped) =
661-
function.name.strip_prefix("[constructor]")
662-
{
663-
stripped
664-
} else if let Some(stripped) =
665-
function.name.strip_prefix("[method]")
666-
{
667-
stripped
668-
} else if let Some(stripped) =
669-
function.name.strip_prefix("[static]")
670-
{
671-
stripped
672-
} else {
673-
continue;
674-
};
675-
676-
if stripped.starts_with(resource_name) {
677-
resource_fns.push((function.name.as_str(), function));
678-
}
679-
}
680-
_ => {}
639+
if ty.kind == TypeDefKind::Resource {
640+
self.resource_directions
641+
.insert(*id, AbiVariant::GuestImport);
642+
643+
let resource_name = ty.name.as_ref().unwrap();
644+
645+
let mut resource_fns = Vec::new();
646+
for (_, impt) in &self.resolve.worlds[self.world].imports {
647+
if let WorldItem::Function(function) = impt {
648+
let stripped = if let Some(stripped) =
649+
function.name.strip_prefix("[constructor]")
650+
{
651+
stripped
652+
} else if let Some(stripped) =
653+
function.name.strip_prefix("[method]")
654+
{
655+
stripped
656+
} else if let Some(stripped) =
657+
function.name.strip_prefix("[static]")
658+
{
659+
stripped
660+
} else {
661+
continue;
662+
};
663+
664+
if stripped.starts_with(resource_name) {
665+
resource_fns.push((function.name.as_str(), function));
681666
}
682667
}
683-
684-
self.resource_bindgen(*id, "$root", &None, resource_fns);
685668
}
686-
_ => {}
669+
670+
self.resource_bindgen(*id, "$root", &None, resource_fns);
687671
}
688672
}
689673
};
@@ -1112,8 +1096,7 @@ impl EsmBindgen {
11121096
iface = match iface.get_mut(&iface_id_or_kebab).unwrap() {
11131097
Binding::Interface(iface) => iface,
11141098
Binding::Resource(_) | Binding::Local(_) => panic!(
1115-
"Exported interface {} cannot be both a function and an interface or resource",
1116-
iface_id_or_kebab
1099+
"Exported interface {iface_id_or_kebab} cannot be both a function and an interface or resource"
11171100
),
11181101
};
11191102
}
@@ -1143,8 +1126,7 @@ impl EsmBindgen {
11431126
iface = match iface.get_mut(&iface_id_or_kebab).unwrap() {
11441127
Binding::Interface(iface) => iface,
11451128
Binding::Resource(_) | Binding::Local(_) => panic!(
1146-
"Exported interface {} cannot be both a function and an interface or resource",
1147-
iface_id_or_kebab
1129+
"Exported interface {iface_id_or_kebab} cannot be both a function and an interface or resource"
11481130
),
11491131
};
11501132
}
@@ -1158,7 +1140,7 @@ impl EsmBindgen {
11581140
let expt_name_sans_version = if let Some(version_idx) = expt_name.find('@') {
11591141
&expt_name[0..version_idx]
11601142
} else {
1161-
&expt_name
1143+
expt_name
11621144
};
11631145
if let Some(alias) = interface_name_from_string(expt_name_sans_version) {
11641146
if !self.exports.contains_key(&alias)
@@ -1178,7 +1160,7 @@ impl EsmBindgen {
11781160
) {
11791161
// TODO: bring back these validations of imports
11801162
// including using the flattened bindings
1181-
if self.exports.len() > 0 {
1163+
if !self.exports.is_empty() {
11821164
// error handling
11831165
uwriteln!(output, "
11841166
class BindingsError extends Error {{
@@ -1328,12 +1310,9 @@ fn interface_name_from_string(name: &str) -> Option<String> {
13281310
let path_idx = name.rfind('/')?;
13291311
let name = &name[path_idx + 1..];
13301312
let at_idx = name.rfind('@');
1331-
let alias = name[..at_idx.unwrap_or_else(|| name.len())].to_lower_camel_case();
1313+
let alias = name[..at_idx.unwrap_or(name.len())].to_lower_camel_case();
13321314
let iface_name = Some(if let Some(at_idx) = at_idx {
1333-
format!(
1334-
"{alias}_{}",
1335-
name[at_idx + 1..].replace('.', "_").replace('-', "_")
1336-
)
1315+
format!("{alias}_{}", name[at_idx + 1..].replace(['.', '-'], "_"))
13371316
} else {
13381317
alias
13391318
});
@@ -1343,23 +1322,21 @@ fn interface_name_from_string(name: &str) -> Option<String> {
13431322
fn binding_name(func_name: &str, iface_name: &Option<String>) -> String {
13441323
match iface_name {
13451324
Some(iface_name) => format!("{iface_name}${func_name}"),
1346-
None => format!("{func_name}"),
1325+
None => func_name.to_string(),
13471326
}
13481327
}
13491328

13501329
/// Extract success and error types from a given optional type, if it is a Result
1351-
pub fn get_result_types<'a>(
1352-
resolve: &'a Resolve,
1330+
pub fn get_result_types(
1331+
resolve: &Resolve,
13531332
return_type: Option<Type>,
1354-
) -> Option<(Option<&'a Type>, Option<&'a Type>)> {
1333+
) -> Option<(Option<&Type>, Option<&Type>)> {
13551334
match return_type {
13561335
None => None,
1357-
Some(ty) => match ty {
1358-
Type::Id(id) => match &resolve.types[id].kind {
1359-
TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
1360-
_ => None,
1361-
},
1336+
Some(Type::Id(id)) => match &resolve.types[id].kind {
1337+
TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
13621338
_ => None,
13631339
},
1340+
_ => None,
13641341
}
13651342
}

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

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1-
use anyhow::{bail, Context, Result};
21
use std::path::Path;
32

3+
use anyhow::{bail, Context, Result};
4+
use wit_parser::{PackageId, Resolve};
5+
46
pub mod bindgen;
57
pub mod splice;
68
pub mod stub_wasi;
9+
pub mod wit;
710

8-
use crate::wit::{CoreFn, CoreTy};
9-
use wit_parser::{PackageId, Resolve};
10-
11-
pub mod wit {
12-
wit_bindgen::generate!({
13-
world: "spidermonkey-embedding-splicer",
14-
pub_export_macro: true
15-
});
16-
}
11+
use wit::exports::local::spidermonkey_embedding_splicer::splicer::{CoreFn, CoreTy};
1712

1813
/// Calls [`write!`] with the passed arguments and unwraps the result.
1914
///
@@ -60,10 +55,7 @@ fn map_core_fn(cfn: &bindgen::CoreFn) -> CoreFn {
6055
} = cfn;
6156
CoreFn {
6257
params: params.iter().map(&map_core_ty).collect(),
63-
ret: match ret {
64-
Some(ref core_ty) => Some(map_core_ty(core_ty)),
65-
None => None,
66-
},
58+
ret: ret.as_ref().map(map_core_ty),
6759
retptr: *retptr,
6860
retsize: *retsize,
6961
paramptr: *paramptr,
@@ -75,17 +67,17 @@ fn parse_wit(path: impl AsRef<Path>) -> Result<(Resolve, PackageId)> {
7567
let path = path.as_ref();
7668
let id = if path.is_dir() {
7769
resolve
78-
.push_dir(&path)
70+
.push_dir(path)
7971
.with_context(|| format!("resolving WIT in {}", path.display()))?
8072
.0
8173
} else {
8274
let contents =
83-
std::fs::read(&path).with_context(|| format!("reading file {}", path.display()))?;
75+
std::fs::read(path).with_context(|| format!("reading file {}", path.display()))?;
8476
let text = match std::str::from_utf8(&contents) {
8577
Ok(s) => s,
8678
Err(_) => bail!("input file is not valid utf-8"),
8779
};
88-
resolve.push_str(&path, text)?
80+
resolve.push_str(path, text)?
8981
};
9082
Ok((resolve, id))
9183
}

0 commit comments

Comments
 (0)