Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

treeshake imports based on guest content #117

Merged
merged 9 commits into from
Jul 15, 2024
45 changes: 40 additions & 5 deletions crates/spidermonkey-embedding-splicer/src/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,13 @@ pub struct Componentization {
pub resource_imports: Vec<(String, String, u32)>,
}

pub fn componentize_bindgen(resolve: &Resolve, id: WorldId, name: &str) -> Componentization {
pub fn componentize_bindgen(
resolve: &Resolve,
id: WorldId,
name: &str,
guest_imports: &Vec<String>,
guest_exports: &Vec<String>,
) -> Componentization {
let mut bindgen = JsBindgen {
src: Source::default(),
esm_bindgen: EsmBindgen::default(),
Expand All @@ -147,9 +153,9 @@ pub fn componentize_bindgen(resolve: &Resolve, id: WorldId, name: &str) -> Compo
.local_names
.exclude_globals(Intrinsic::get_global_names());

bindgen.imports_bindgen();
bindgen.imports_bindgen(&guest_imports);

bindgen.exports_bindgen();
bindgen.exports_bindgen(&guest_exports);
bindgen.esm_bindgen.populate_export_aliases();

// consolidate import specifiers and generate wrappers
Expand Down Expand Up @@ -342,6 +348,7 @@ pub fn componentize_bindgen(resolve: &Resolve, id: WorldId, name: &str) -> Compo
"$source_mod",
&mut bindgen.local_names,
name,
&guest_exports,
);

let js_intrinsics = render_intrinsics(&mut bindgen.all_intrinsics, false, true);
Expand All @@ -363,9 +370,28 @@ impl JsBindgen<'_> {
return intrinsic.name().to_string();
}

fn exports_bindgen(&mut self) {
fn exports_bindgen(&mut self, guest_exports: &Vec<String>) {
for (key, export) in &self.resolve.worlds[self.world].exports {
let name = self.resolve.name_world_key(key);
// TODO: figure out how to go from "run" -> wasi:cli/[email protected] and
// "incomingHandler" -> wasi:http/[email protected] and in
// general go from the sugared up names to explicit name. This is
// just to make sure some random export does not mess up the exports
// that a components to need to export
guybedford marked this conversation as resolved.
Show resolved Hide resolved
if name == "wasi:http/[email protected]"
&& !guest_exports.contains(&"incomingHandler".to_string())
&& !guest_exports.contains(&"wasi:http/[email protected]".to_string())
{
continue;
}

if name == "wasi:cli/[email protected]"
&& !guest_exports.contains(&"run".to_string())
&& !guest_exports.contains(&"wasi:cli/[email protected]".to_string())
{
continue;
}

match export {
WorldItem::Function(func) => {
let local_name = self.local_names.create_once(&func.name).to_string();
Expand Down Expand Up @@ -451,9 +477,12 @@ impl JsBindgen<'_> {
}
}

fn imports_bindgen(&mut self) {
fn imports_bindgen(&mut self, guest_imports: &Vec<String>) {
for (key, impt) in &self.resolve.worlds[self.world].imports {
let import_name = self.resolve.name_world_key(key);
if !guest_imports.contains(&import_name) {
continue;
}
match &impt {
WorldItem::Function(f) => {
self.import_bindgen(import_name, f, false, None);
Expand Down Expand Up @@ -1001,6 +1030,7 @@ impl EsmBindgen {
imports_object: &str,
_local_names: &mut LocalNames,
source_name: &str,
guest_exports: &Vec<String>,
) {
// TODO: bring back these validations of imports
// including using the flattened bindings
Expand Down Expand Up @@ -1043,6 +1073,11 @@ impl EsmBindgen {
");
}
for (export_name, binding) in &self.exports {
if export_name == "wasi:http/[email protected]"
&& !guest_exports.contains(&"incomingHandler".to_string())
{
continue;
}
guybedford marked this conversation as resolved.
Show resolved Hide resolved
match binding {
Binding::Interface(bindings) => {
uwrite!(output, "const ");
Expand Down
22 changes: 19 additions & 3 deletions crates/spidermonkey-embedding-splicer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use anyhow::{bail, Context, Result};
use bindgen::BindingItem;
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
vec,
};

mod bindgen;
mod splice;
Expand Down Expand Up @@ -110,6 +113,8 @@ impl Guest for SpidermonkeyEmbeddingSplicerComponent {
wit_source: Option<String>,
wit_path: Option<String>,
world_name: Option<String>,
mut guest_imports: Vec<String>,
guest_exports: Vec<String>,
debug: bool,
) -> Result<SpliceResult, String> {
let source_name = source_name.unwrap_or("source.js".to_string());
Expand All @@ -131,7 +136,6 @@ impl Guest for SpidermonkeyEmbeddingSplicerComponent {
.map_err(|e| e.to_string())?;

let mut wasm_bytes = wit_component::dummy_module(&resolve, world);
let componentized = bindgen::componentize_bindgen(&resolve, world, &source_name);

// merge the engine world with the target world, retaining the engine producers
let producers = if let Ok((
Expand All @@ -144,6 +148,11 @@ impl Guest for SpidermonkeyEmbeddingSplicerComponent {
},
)) = decode(&engine)
{
// merge the imports from the engine with the imports from the guest content.
for (k, _) in &engine_resolve.worlds[engine_world].imports {
guest_imports.push(engine_resolve.name_world_key(k));
}
guybedford marked this conversation as resolved.
Show resolved Hide resolved
Comment on lines +152 to +154
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was taking a stab at this afternoon and found that this returned all the imports provided in the wit definition and not just the ones actively used by the engine who's list I was able to get with the following code snippet.

// Componentize the Starlingmonkey engine so that we can pull in the imports that are actually used.
let bytes = include_bytes!("../../../StarlingMonkey/host-apis/wasi-0.2.0/preview1-adapter-release/wasi_snapshot_preview1.wasm");
let component = wit_component::ComponentEncoder::default()
    .adapter("wasi_snapshot_preview1", bytes)
    .map_err(|e| e.to_string())?
    .module(&engine)
    .map_err(|e| e.to_string())?
    .encode()
    .map_err(|e| e.to_string())?;
let decoded = wit_component::decode(&component).unwrap();
let resolve = decoded.resolve();
let packages = decoded.package();
let world_id = resolve.select_world(packages, None).unwrap();
let world = &resolve.worlds[world_id];
// merge the imports actually used by the engine with the imports from the guest content.
for (import_key, _) in world.imports.iter() {
    guest_imports.push(resolve.name_world_key(import_key));
}


// we disable the engine run and incoming handler as we recreate these exports
// when needed, so remove these from the world before initiating the merge
let maybe_run = engine_resolve.worlds[engine_world]
Expand Down Expand Up @@ -187,6 +196,14 @@ impl Guest for SpidermonkeyEmbeddingSplicerComponent {
None
};

let componentized = bindgen::componentize_bindgen(
&resolve,
world,
&source_name,
&guest_imports,
&guest_exports,
);

let encoded = wit_component::metadata::encode(
&resolve,
world,
Expand Down Expand Up @@ -327,7 +344,6 @@ impl Guest for SpidermonkeyEmbeddingSplicerComponent {
));
}

// println!("{:?}", &imports);
// println!("{:?}", &componentized.imports);
// println!("{:?}", &exports);
let mut wasm =
Expand Down
34 changes: 22 additions & 12 deletions crates/spidermonkey-embedding-splicer/src/splice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,30 @@ pub fn splice(
let mut module = config.parse(&engine)?;

// since StarlingMonkey implements CLI Run and incoming handler,
// we override these in ComponentizeJS, removing them from the
// core function exports
if let Ok(run) = module.exports.get_func("wasi:cli/[email protected]#run") {
let expt = module.exports.get_exported_func(run).unwrap();
module.exports.delete(expt.id());
module.funcs.delete(run);
// we override them only if the guest content exports those functions
if exports
.iter()
.any(|(name, _)| name == "wasi:cli/[email protected]#run")
{
if let Ok(run) = module.exports.get_func("wasi:cli/[email protected]#run") {
let expt = module.exports.get_exported_func(run).unwrap();
module.exports.delete(expt.id());
module.funcs.delete(run);
}
}
if let Ok(serve) = module
.exports
.get_func("wasi:http/[email protected]#handle")

if exports
.iter()
.any(|(name, _)| name == "wasi:http/[email protected]#handle")
{
let expt = module.exports.get_exported_func(serve).unwrap();
module.exports.delete(expt.id());
module.funcs.delete(serve);
if let Ok(serve) = module
.exports
.get_func("wasi:http/[email protected]#handle")
{
let expt = module.exports.get_exported_func(serve).unwrap();
module.exports.delete(expt.id());
module.funcs.delete(serve);
}
}

// we reencode the WASI world component data, so strip it out from the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ world spidermonkey-embedding-splicer {

export stub-wasi: func(engine: list<u8>, features: list<features>, wit-world: option<string>, wit-path: option<string>, world-name: option<string>) -> result<list<u8>, string>;

export splice-bindings: func(source-name: option<string>, spidermonkey-engine: list<u8>, wit-world: option<string>, wit-path: option<string>, world-name: option<string>, debug: bool) -> result<splice-result, string>;
export splice-bindings: func(source-name: option<string>, spidermonkey-engine: list<u8>, wit-world: option<string>, wit-path: option<string>, world-name: option<string>, guest-imports: list<string>, guest-exports: list<string>, debug: bool) -> result<splice-result, string>;
}
28 changes: 21 additions & 7 deletions src/componentize.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,33 @@ export async function componentize(jsSource, witWorld, opts) {
enableFeatures = [],
} = opts || {};

await lexerInit;
let jsImports = [];
let jsExports = [];
try {
[jsImports, jsExports] = parse(jsSource);
} catch {
// ignore parser errors - will show up as engine parse errors shortly
}

let guestImports = []
jsImports.map(k => {
guestImports.push(k.n)
})

let guestExports = []
jsExports.map(k => {
guestExports.push(k.n)
})

let { wasm, jsBindings, importWrappers, exports, imports } = spliceBindings(
sourceName,
await readFile(engine),
witWorld,
maybeWindowsPath(witPath),
worldName,
guestImports,
guestExports,
false
);

Expand Down Expand Up @@ -103,13 +124,6 @@ export async function componentize(jsSource, witWorld, opts) {
await writeFile(input, Buffer.from(wasm));

// rewrite the JS source import specifiers to reference import wrappers
await lexerInit;
let jsImports = [];
try {
[jsImports] = parse(jsSource);
} catch {
// ignore parser errors - will show up as engine parse errors shortly
}
let source = '',
curIdx = 0;
for (const jsImpt of jsImports) {
Expand Down
Loading