diff --git a/Cargo.lock b/Cargo.lock index c78dab1..26aa90b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1568,11 +1568,9 @@ dependencies = [ name = "wac-loader" version = "0.1.0" dependencies = [ - "anyhow", "indexmap", "wac-graph", "wac-parser", - "wasmparser 0.261.0", "wat", "wit-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index dd4f573..6e3da71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,14 @@ wit-bindgen = "0.62.0" wit-component = "0.261.0" wit-parser = "0.261.0" +# release builds are only published as components, smaller beats faster. "s" over "z" keeps the +# parsers in wac-loader and wit-tools fast. +[profile.release] +opt-level = "s" +lto = true +codegen-units = 1 +strip = "debuginfo" + # wasmtime compiles components in the native tests, an unoptimized cranelift is very slow. These # crates are only built for the host, the components are unaffected. [profile.dev.package.cranelift-codegen] diff --git a/Makefile b/Makefile index e3e6890..96847fc 100644 --- a/Makefile +++ b/Makefile @@ -24,13 +24,21 @@ relpath = $(if $(filter $(CURDIR),$(abspath $(1))),.,$(patsubst $(CURDIR)/%,%,$( all: tools wit components test .PHONY: clean -clean: clean-wit +clean: clean-components clean-wit + @: + +.PHONY: clean-all +clean-all: clean-components clean-tools clean-wit cargo clean .PHONY: clean-components clean-components: clean-wit rm -rf ${COMPONENTS_DIR} +.PHONY: clean-tools +clean-tools: + rm -rf ${TOOLS_DIR} + .PHONY: clean-wit ## Remove the fetched wit dependencies, fetched again by `make wit` clean-wit: rm -rf $(WIT_DEPS) diff --git a/components/wac-loader/Cargo.toml b/components/wac-loader/Cargo.toml index 6e18402..7879738 100644 --- a/components/wac-loader/Cargo.toml +++ b/components/wac-loader/Cargo.toml @@ -8,11 +8,9 @@ license = "Apache-2.0" crate-type = ["cdylib"] [dependencies] -anyhow = { workspace = true } indexmap = { workspace = true } wac-graph = { workspace = true } wac-parser = { workspace = true } -wasmparser = { workspace = true } wit-bindgen = { workspace = true } [dev-dependencies] diff --git a/components/wac-loader/src/lib.rs b/components/wac-loader/src/lib.rs index b1e3735..c750b5e 100644 --- a/components/wac-loader/src/lib.rs +++ b/components/wac-loader/src/lib.rs @@ -1,7 +1,7 @@ use indexmap::IndexMap; use wac_graph::{ CompositionGraph, EncodeOptions, - types::{BorrowedPackageKey, Package}, + types::{BorrowedPackageKey, Package, Types}, }; use wac_parser::Document; @@ -17,19 +17,13 @@ impl Guest for WacLoader { async fn plug(socket: Wasm, plugs: Vec) -> Result { let mut graph = CompositionGraph::new(); - let socket = Package::from_bytes( - "socket", - None, - component("socket", socket)?, - graph.types_mut(), - )?; + let socket = package("socket", socket, graph.types_mut())?; let socket = graph.register_package(socket)?; let mut graph_plugs = Vec::new(); for (i, plug) in plugs.into_iter().enumerate() { let name = format!("plug:{i}"); - let plug = component(&name, plug)?; - let plug = Package::from_bytes(&name, None, plug, graph.types_mut())?; + let plug = package(&name, plug, graph.types_mut())?; let plug = graph.register_package(plug)?; graph_plugs.push(plug); } @@ -46,11 +40,15 @@ impl Guest for WacLoader { Plan::Wac(script) => { let document = Document::parse(&script)?; + // the resolution only parses the packages the script uses, each dependency is + // parsed up front so a malformed one is reported even when it is unused + let mut types = Types::default(); let (names, components): (Vec, Vec) = deps.into_iter().unzip(); let mut dependencies = IndexMap::new(); for (pkg, wasm) in names.iter().zip(components) { let key = BorrowedPackageKey::from_name_and_version(pkg, None); - dependencies.insert(key, component(pkg, wasm)?); + let package = package(pkg, wasm, &mut types)?; + dependencies.insert(key, package.bytes().to_vec()); } let resolution = document.resolve(dependencies)?; let component = resolution.encode(EncodeOptions::default())?; @@ -61,27 +59,22 @@ impl Guest for WacLoader { } } -/// The wasm when it is a valid component, otherwise a `not-component` or `malformed` error naming -/// it. -fn component(name: &str, wasm: Wasm) -> Result { - if !wasmparser::Parser::is_component(&wasm) { - return Err(ErrorCode::NotComponent(Some(name.to_string()))); - } - wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::all()) - .validate_all(&wasm) - .map_err(|error| { - ErrorCode::Malformed(Malformed { - name: Some(name.to_string()), - message: error.to_string(), - }) - })?; - Ok(wasm) -} +/// The header of a binary-encoded component, the wasm magic number followed by the component +/// version and layer. +const COMPONENT_HEADER: [u8; 8] = *b"\0asm\x0d\x00\x01\x00"; -impl From for ErrorCode { - fn from(err: anyhow::Error) -> Self { - Self::Other(Some(err.to_string())) +/// Parses and validates the wasm as a package, otherwise a `not-component` or `malformed` error +/// naming it. +fn package(name: &str, wasm: Wasm, types: &mut Types) -> Result { + if !wasm.starts_with(&COMPONENT_HEADER) { + return Err(ErrorCode::NotComponent(Some(name.to_string()))); } + Package::from_bytes(name, None, wasm, types).map_err(|error| { + ErrorCode::Malformed(Malformed { + name: Some(name.to_string()), + message: format!("{error:#}"), + }) + }) } impl From for ErrorCode { diff --git a/components/wac-loader/src/tests.rs b/components/wac-loader/src/tests.rs index f04311d..5d223e5 100644 --- a/components/wac-loader/src/tests.rs +++ b/components/wac-loader/src/tests.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::{ - WacLoader, + COMPONENT_HEADER, WacLoader, componentized::component::types::{ErrorCode, Malformed}, exports::componentized::component::wac_loader::{Guest, Plan}, }; @@ -73,7 +73,7 @@ fn compose_components() { )) .expect("composes"); - assert!(wasmparser::Parser::is_component(&composed)); + assert!(composed.starts_with(&COMPONENT_HEADER)); } /// A component header followed by an invalid section.