Skip to content

Commit

Permalink
Merge parse_wit_from_path directly as methods on Resolve
Browse files Browse the repository at this point in the history
This commit moves the `wit_component::parse_wit_from_path` function to
being a suite of methods directly on `Resolve`. The direct equivalent is
now `Resolve::push_path`.
  • Loading branch information
alexcrichton committed Feb 9, 2024
1 parent aec4ae9 commit 2fa04a1
Show file tree
Hide file tree
Showing 39 changed files with 498 additions and 274 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ jobs:
- run: cargo check --no-default-features --features wit-smith
- run: cargo check --no-default-features --features addr2line
- run: cargo check --no-default-features -p wit-parser
- run: cargo check --no-default-features -p wit-parser --features wit
- run: cargo check --no-default-features -p wit-parser --features serde
- run: cargo check --no-default-features -p wit-parser --features decoding
- run: cargo check --no-default-features -p wit-parser --features serde,decoding,wit
- run: |
if cargo tree -p wasm-smith --no-default-features -e no-dev | grep wasmparser; then
echo wasm-smith without default features should not depend on wasmparser
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ cpp_demangle = { version = "0.4.0", optional = true }

# Dependencies of `component`
wit-component = { workspace = true, optional = true, features = ['dummy-module', 'wat'] }
wit-parser = { workspace = true, optional = true }
wit-parser = { workspace = true, optional = true, features = ['decoding', 'wat', 'serde'] }
wast = { workspace = true, optional = true }

# Dependencies of `metadata`
Expand Down
12 changes: 10 additions & 2 deletions crates/wat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ fn _parse_str(wat: &str) -> Result<Vec<u8>> {

/// Result of [`Detect::from_bytes`] to indicate what some input bytes look
/// like.
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Detect {
/// The input bytes look like the WebAssembly text format.
WasmText,
Expand Down Expand Up @@ -253,7 +253,7 @@ impl Detect {
/// nop
/// )
/// )
/// "#, Detect::WasmText));
/// "#), Detect::WasmText);
/// ```
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Detect {
if bytes.as_ref().starts_with(b"\0asm") {
Expand All @@ -279,6 +279,14 @@ impl Detect {

Detect::Unknown
}

/// Returns whether this is either binary or textual wasm.
pub fn is_wasm(&self) -> bool {
match self {
Detect::WasmText | Detect::WasmBinary => true,
Detect::Unknown => false,
}
}
}

/// A convenience type definition for `Result` where the error is [`Error`]
Expand Down
83 changes: 0 additions & 83 deletions crates/wit-component/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,89 +82,6 @@ pub(crate) fn base_producers() -> wasm_metadata::Producers {
producer
}

/// Parse a WIT package from the input `path`.
///
/// The input `path` can be one of:
///
/// * A directory containing a WIT package with an optional `deps` directory for
/// any dependent WIT packages it references.
/// * A single standalone WIT file with no dependencies.
/// * A wasm-encoded WIT package as a single file in the wasm binary format.
/// * A wasm-encoded WIT package as a single file in the wasm text format.
///
/// The `Resolve` containing WIT information along with the `PackageId` of what
/// was parsed is returned if successful.
pub fn parse_wit_from_path(
path: impl AsRef<std::path::Path>,
) -> Result<(Resolve, wit_parser::PackageId)> {
use anyhow::Context;

let mut resolver = Resolve::default();
let id = match path.as_ref() {
// Directories can be directly fed into the resolver
p if p.is_dir() => {
resolver
.push_dir(p)
.with_context(|| {
format!(
"failed to resolve directory while parsing WIT for path [{}]",
p.display()
)
})?
.0
}
// Non-directory files (including symlinks) can be either:
// - Wasm modules (binary or WAT) that are WIT packages
// - WIT files
#[cfg(not(feature = "wat"))]
p => {
let file_contents = std::fs::read(p)
.with_context(|| format!("failed to parse WIT from path [{}]", p.display()))?;
match decode(&file_contents)? {
DecodedWasm::Component(..) => {
bail!("specified path is a component, not a wit package")
}
DecodedWasm::WitPackage(resolve, pkg) => return Ok((resolve, pkg)),
}
}
#[cfg(feature = "wat")]
p => {
use wat::Detect;
use wit_parser::UnresolvedPackage;

let file_contents = std::fs::read(p)
.with_context(|| format!("failed to parse WIT from path [{}]", p.display()))?;

// Check if the bytes represent a Wasm module (either binary or WAT encoded)
match wat::Detect::from_bytes(&file_contents) {
Detect::WasmText | Detect::WasmBinary => {
let bytes = wat::parse_bytes(&file_contents).map_err(|mut e| {
e.set_path(p);
e
})?;
match decode(&bytes)? {
DecodedWasm::Component(..) => {
bail!("specified path is a component, not a wit package")
}
DecodedWasm::WitPackage(resolve, pkg) => return Ok((resolve, pkg)),
}
}
Detect::Unknown => {
// If the bytes are not a WASM module, they should be WIT that can be parsed
// into a package by the resolver
let text = match std::str::from_utf8(&file_contents) {
Ok(s) => s,
Err(_) => bail!("input file is not valid utf-8"),
};
let pkg = UnresolvedPackage::parse(p, text)?;
resolver.push(pkg)?
}
}
}
};
Ok((resolver, id))
}

/// Embed component metadata in a buffer of bytes that contains a Wasm module
pub fn embed_component_metadata(
bytes: &mut Vec<u8>,
Expand Down
32 changes: 9 additions & 23 deletions crates/wit-component/tests/wit.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
#![cfg(feature = "wat")]

use anyhow::Result;
use wit_component::{is_wasm_binary_or_wat, parse_wit_from_path};

const EXAMPLE_MODULE_WAT: &str = r#"
(module
(type (;0;) (func))
(func (;0;) (type 0)
nop
)
)
"#;
use wit_parser::Resolve;

/// Ensure that parse_wit_from_path works with directories
#[test]
fn parse_wit_dir() -> Result<()> {
drop(env_logger::try_init());

let (resolver, package_id) = parse_wit_from_path("tests/wit/parse-dir/wit")?;
let mut resolver = Resolve::default();
let package_id = resolver.push_path("tests/wit/parse-dir/wit")?.0;
assert!(resolver
.select_world(package_id, "foo-world".into())
.is_ok());
Expand All @@ -30,7 +22,10 @@ fn parse_wit_dir() -> Result<()> {
fn parse_wit_file() -> Result<()> {
drop(env_logger::try_init());

let (resolver, package_id) = parse_wit_from_path("tests/wit/parse-dir/wit/deps/bar/bar.wit")?;
let mut resolver = Resolve::default();
let package_id = resolver
.push_path("tests/wit/parse-dir/wit/deps/bar/bar.wit")?
.0;
resolver.select_world(package_id, "bar-world".into())?;
assert!(resolver
.interfaces
Expand All @@ -45,17 +40,8 @@ fn parse_wit_file() -> Result<()> {
fn parse_wit_missing_path() -> Result<()> {
drop(env_logger::try_init());

assert!(parse_wit_from_path("tests/nonexistent/path").is_err());

Ok(())
}

/// Ensure that is_wasm_binary_or_wat works for binaries
#[test]
fn check_wasm_from_bytes() -> Result<()> {
drop(env_logger::try_init());

assert!(is_wasm_binary_or_wat(wat::parse_str(EXAMPLE_MODULE_WAT)?));
let mut resolver = Resolve::default();
assert!(resolver.push_path("tests/nonexistent/path").is_err());

Ok(())
}
16 changes: 13 additions & 3 deletions crates/wit-parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,29 @@ serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
wasmparser = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
wat = { workspace = true, optional = true }

[features]
# default = ['decoding']
default = ['serde', 'decoding']

# Enables support for `derive(Serialize, Deserialize)` on many structures, such
# as `Resolve`, which can assist when encoding `Resolve` as JSON for example.
serde = ['dep:serde', 'dep:serde_derive', 'indexmap/serde', 'serde_json']

# Enables support for decoding WIT from WebAssembly. This can be done to support
# decoding a WIT package encoded as wasm automatically.
decoding = ['dep:wasmparser']
# encoding = ['dep:wasm-encoder']

# Enables support for parsing the wasm text format in conjunction with the
# `decoding` feature.
wat = ['decoding', 'dep:wat']

[dev-dependencies]
rayon = "1"
env_logger = { workspace = true }
pretty_assertions = { workspace = true }
serde_json = { workspace = true }
wit-parser = { path = '.', features = ['serde'] }
wit-parser = { path = '.', features = ['serde', 'wat'] }

[[test]]
name = "all"
Expand Down
8 changes: 4 additions & 4 deletions crates/wit-parser/src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl ComponentInfo {
let mut externs = Vec::new();
let mut depth = 1;
let mut types = None;
let mut package_docs = None;
let mut _package_docs = None;
let mut cur = Parser::new(0);
let mut eof = false;
let mut stack = Vec::new();
Expand Down Expand Up @@ -113,10 +113,10 @@ impl ComponentInfo {
}
#[cfg(feature = "serde")]
Payload::CustomSection(s) if s.name() == PackageDocs::SECTION_NAME => {
if package_docs.is_some() {
if _package_docs.is_some() {
bail!("multiple {:?} sections", PackageDocs::SECTION_NAME);
}
package_docs = Some(PackageDocs::decode(s.data())?);
_package_docs = Some(PackageDocs::decode(s.data())?);
}
Payload::ModuleSection { parser, .. }
| Payload::ComponentSection { parser, .. } => {
Expand All @@ -141,7 +141,7 @@ impl ComponentInfo {
Ok(Self {
types: types.unwrap(),
externs,
package_docs,
package_docs: _package_docs,
})
}

Expand Down
Loading

0 comments on commit 2fa04a1

Please sign in to comment.