Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 11 additions & 27 deletions crates/spidermonkey-embedding-splicer/src/bin/splicer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;

use spidermonkey_embedding_splicer::wit::Features;
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};

use spidermonkey_embedding_splicer::wit::exports::local::spidermonkey_embedding_splicer::splicer::Features;
use spidermonkey_embedding_splicer::{splice, stub_wasi};

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -62,27 +64,6 @@ enum Commands {
},
}

/// Maps the list of features passed as strings into the list of features as given by the enum
///
/// enum features {
/// stdio,
/// clocks,
/// random,
/// http,
///}
fn map_features(features: &Vec<String>) -> Vec<Features> {
features
.iter()
.map(|f| match f.as_str() {
"stdio" => Features::Stdio,
"clocks" => Features::Clocks,
"random" => Features::Random,
"http" => Features::Http,
_ => panic!("Unknown feature: {}", f),
})
.collect()
}

fn main() -> Result<()> {
let cli = Cli::parse();

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

let wit_path_str = wit_path.as_ref().map(|p| p.to_string_lossy().to_string());
let features = map_features(&features);
let features = features
.iter()
.map(|v| Features::from_str(v))
.collect::<Result<Vec<_>>>()?;

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

let result = splice::splice_bindings(engine, world_name, wit_path_str, None, debug)
.map_err(|e| anyhow::anyhow!(e))?;
fs::write(&out_dir.join("component.wasm"), result.wasm).with_context(|| {
fs::write(out_dir.join("component.wasm"), result.wasm).with_context(|| {
format!(
"Failed to write output file: {}",
out_dir.join("component.wasm").display()
)
})?;
fs::write(&out_dir.join("initializer.js"), result.js_bindings).with_context(|| {
fs::write(out_dir.join("initializer.js"), result.js_bindings).with_context(|| {
format!(
"Failed to write output file: {}",
out_dir.join("initializer.js").display()
Expand Down
131 changes: 54 additions & 77 deletions crates/spidermonkey-embedding-splicer/src/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
for (specifier, by_resource) in by_specifier_by_resource {
let mut specifier_list = Vec::new();
for (resource, items) in by_resource {
let item = items.iter().next().unwrap();
let item = items.first().unwrap();
if let Some(resource) = resource {
let export_name = resource.to_upper_camel_case();
let binding_name = binding_name(&export_name, &item.iface_name);
Expand Down Expand Up @@ -241,7 +241,7 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
if let TypeDefKind::Resource = &ty.kind {
let iface_prefix = interface_name(resolve, *iface_id)
.map(|s| format!("{s}$"))
.unwrap_or_else(String::new);
.unwrap_or_default();
let resource_name_camel = ty.name.as_ref().unwrap().to_lower_camel_case();
let resource_name_kebab = ty.name.as_ref().unwrap().to_kebab_case();
let module_name = format!("[export]{key_name}");
Expand Down Expand Up @@ -295,11 +295,8 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
WorldItem::Function(_) => {}
WorldItem::Type(id) => {
let ty = &resolve.types[*id];
match ty.kind {
TypeDefKind::Resource => {
imported_resource_modules.insert(*id, key_name.clone());
}
_ => {}
if ty.kind == TypeDefKind::Resource {
imported_resource_modules.insert(*id, key_name.clone());
}
}
}
Expand Down Expand Up @@ -390,7 +387,7 @@ pub fn componentize_bindgen(resolve: &Resolve, wid: WorldId) -> Result<Component
impl JsBindgen<'_> {
fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
self.all_intrinsics.insert(intrinsic);
return intrinsic.name().to_string();
intrinsic.name().to_string()
}

fn exports_bindgen(
Expand Down Expand Up @@ -448,14 +445,7 @@ impl JsBindgen<'_> {
match export {
WorldItem::Function(func) => {
let local_name = self.local_names.create_once(&func.name).to_string();
self.export_bindgen(
name.into(),
false,
None,
&local_name,
StringEncoding::UTF8,
func,
);
self.export_bindgen(name, false, None, &local_name, StringEncoding::UTF8, func);
self.esm_bindgen.add_export_func(
None,
local_name.to_string(),
Expand Down Expand Up @@ -484,7 +474,7 @@ impl JsBindgen<'_> {
interface_name(self.resolve, *id),
&local_name,
StringEncoding::UTF8,
&func,
func,
);
self.esm_bindgen.add_export_func(
Some(name),
Expand All @@ -501,7 +491,7 @@ impl JsBindgen<'_> {
let local_name = self
.local_names
.get_or_create(
&format!("resource:{resource_name}"),
format!("resource:{resource_name}"),
&resource_name,
)
.0
Expand All @@ -512,10 +502,10 @@ impl JsBindgen<'_> {
interface_name(self.resolve, *id),
&local_name,
StringEncoding::UTF8,
&func,
func,
);
self.esm_bindgen.ensure_exported_resource(
Some(&name),
Some(name),
local_name,
resource_name,
);
Expand Down Expand Up @@ -547,7 +537,7 @@ impl JsBindgen<'_> {
.as_ref()
.unwrap()
.to_upper_camel_case(),
&iface_name,
iface_name,
);

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

let resource_symbol = self.intrinsic(Intrinsic::SymbolResourceHandle);
let dispose_symbol = self.intrinsic(Intrinsic::SymbolDispose);
Expand Down Expand Up @@ -646,44 +636,38 @@ impl JsBindgen<'_> {
}
WorldItem::Type(id) => {
let ty = &self.resolve.types[*id];
match ty.kind {
TypeDefKind::Resource => {
self.resource_directions
.insert(*id, AbiVariant::GuestImport);

let resource_name = ty.name.as_ref().unwrap();

let mut resource_fns = Vec::new();
for (_, impt) in &self.resolve.worlds[self.world].imports {
match impt {
WorldItem::Function(function) => {
let stripped = if let Some(stripped) =
function.name.strip_prefix("[constructor]")
{
stripped
} else if let Some(stripped) =
function.name.strip_prefix("[method]")
{
stripped
} else if let Some(stripped) =
function.name.strip_prefix("[static]")
{
stripped
} else {
continue;
};

if stripped.starts_with(resource_name) {
resource_fns.push((function.name.as_str(), function));
}
}
_ => {}
if ty.kind == TypeDefKind::Resource {
self.resource_directions
.insert(*id, AbiVariant::GuestImport);

let resource_name = ty.name.as_ref().unwrap();

let mut resource_fns = Vec::new();
for (_, impt) in &self.resolve.worlds[self.world].imports {
if let WorldItem::Function(function) = impt {
let stripped = if let Some(stripped) =
function.name.strip_prefix("[constructor]")
{
stripped
} else if let Some(stripped) =
function.name.strip_prefix("[method]")
{
stripped
} else if let Some(stripped) =
function.name.strip_prefix("[static]")
{
stripped
} else {
continue;
};

if stripped.starts_with(resource_name) {
resource_fns.push((function.name.as_str(), function));
}
}

self.resource_bindgen(*id, "$root", &None, resource_fns);
}
_ => {}

self.resource_bindgen(*id, "$root", &None, resource_fns);
}
}
};
Expand Down Expand Up @@ -1112,8 +1096,7 @@ impl EsmBindgen {
iface = match iface.get_mut(&iface_id_or_kebab).unwrap() {
Binding::Interface(iface) => iface,
Binding::Resource(_) | Binding::Local(_) => panic!(
"Exported interface {} cannot be both a function and an interface or resource",
iface_id_or_kebab
"Exported interface {iface_id_or_kebab} cannot be both a function and an interface or resource"
),
};
}
Expand Down Expand Up @@ -1143,8 +1126,7 @@ impl EsmBindgen {
iface = match iface.get_mut(&iface_id_or_kebab).unwrap() {
Binding::Interface(iface) => iface,
Binding::Resource(_) | Binding::Local(_) => panic!(
"Exported interface {} cannot be both a function and an interface or resource",
iface_id_or_kebab
"Exported interface {iface_id_or_kebab} cannot be both a function and an interface or resource"
),
};
}
Expand All @@ -1158,7 +1140,7 @@ impl EsmBindgen {
let expt_name_sans_version = if let Some(version_idx) = expt_name.find('@') {
&expt_name[0..version_idx]
} else {
&expt_name
expt_name
};
if let Some(alias) = interface_name_from_string(expt_name_sans_version) {
if !self.exports.contains_key(&alias)
Expand All @@ -1178,7 +1160,7 @@ impl EsmBindgen {
) {
// TODO: bring back these validations of imports
// including using the flattened bindings
if self.exports.len() > 0 {
if !self.exports.is_empty() {
// error handling
uwriteln!(output, "
class BindingsError extends Error {{
Expand Down Expand Up @@ -1328,12 +1310,9 @@ fn interface_name_from_string(name: &str) -> Option<String> {
let path_idx = name.rfind('/')?;
let name = &name[path_idx + 1..];
let at_idx = name.rfind('@');
let alias = name[..at_idx.unwrap_or_else(|| name.len())].to_lower_camel_case();
let alias = name[..at_idx.unwrap_or(name.len())].to_lower_camel_case();
let iface_name = Some(if let Some(at_idx) = at_idx {
format!(
"{alias}_{}",
name[at_idx + 1..].replace('.', "_").replace('-', "_")
)
format!("{alias}_{}", name[at_idx + 1..].replace(['.', '-'], "_"))
} else {
alias
});
Expand All @@ -1343,23 +1322,21 @@ fn interface_name_from_string(name: &str) -> Option<String> {
fn binding_name(func_name: &str, iface_name: &Option<String>) -> String {
match iface_name {
Some(iface_name) => format!("{iface_name}${func_name}"),
None => format!("{func_name}"),
None => func_name.to_string(),
}
}

/// Extract success and error types from a given optional type, if it is a Result
pub fn get_result_types<'a>(
resolve: &'a Resolve,
pub fn get_result_types(
resolve: &Resolve,
return_type: Option<Type>,
) -> Option<(Option<&'a Type>, Option<&'a Type>)> {
) -> Option<(Option<&Type>, Option<&Type>)> {
match return_type {
None => None,
Some(ty) => match ty {
Type::Id(id) => match &resolve.types[id].kind {
TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
_ => None,
},
Some(Type::Id(id)) => match &resolve.types[id].kind {
TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
_ => None,
},
_ => None,
}
}
26 changes: 9 additions & 17 deletions crates/spidermonkey-embedding-splicer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
use anyhow::{bail, Context, Result};
use std::path::Path;

use anyhow::{bail, Context, Result};
use wit_parser::{PackageId, Resolve};

pub mod bindgen;
pub mod splice;
pub mod stub_wasi;
pub mod wit;

use crate::wit::{CoreFn, CoreTy};
use wit_parser::{PackageId, Resolve};

pub mod wit {
wit_bindgen::generate!({
world: "spidermonkey-embedding-splicer",
pub_export_macro: true
});
}
use wit::exports::local::spidermonkey_embedding_splicer::splicer::{CoreFn, CoreTy};

/// Calls [`write!`] with the passed arguments and unwraps the result.
///
Expand Down Expand Up @@ -60,10 +55,7 @@ fn map_core_fn(cfn: &bindgen::CoreFn) -> CoreFn {
} = cfn;
CoreFn {
params: params.iter().map(&map_core_ty).collect(),
ret: match ret {
Some(ref core_ty) => Some(map_core_ty(core_ty)),
None => None,
},
ret: ret.as_ref().map(map_core_ty),
retptr: *retptr,
retsize: *retsize,
paramptr: *paramptr,
Expand All @@ -75,17 +67,17 @@ fn parse_wit(path: impl AsRef<Path>) -> Result<(Resolve, PackageId)> {
let path = path.as_ref();
let id = if path.is_dir() {
resolve
.push_dir(&path)
.push_dir(path)
.with_context(|| format!("resolving WIT in {}", path.display()))?
.0
} else {
let contents =
std::fs::read(&path).with_context(|| format!("reading file {}", path.display()))?;
std::fs::read(path).with_context(|| format!("reading file {}", path.display()))?;
let text = match std::str::from_utf8(&contents) {
Ok(s) => s,
Err(_) => bail!("input file is not valid utf-8"),
};
resolve.push_str(&path, text)?
resolve.push_str(path, text)?
};
Ok((resolve, id))
}
Loading
Loading