Skip to content

Conversation

@macovedj
Copy link
Contributor

This PR can serve as an implementation of what is proposed here.

@macovedj macovedj marked this pull request as ready for review August 18, 2023 16:44
alexcrichton added a commit to alexcrichton/wasm-tools that referenced this pull request Aug 21, 2023
I noticed that currently in bytecodealliance#1146 the CI there is green but some files
are unformatted which got me wondering why that was the case. Turns out
that `cargo fmt` won't find files that are referenced through
macro-expanded macros, meaning that most of `wast` isn't actually
checked for formatting on CI. This commit fixes that.
Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

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

Thanks! I think it would be good to add a suite of tests for this too. Adding files to tests/local/component-model/*.wast should be good enough. It'll be good to exercise the text parsing, binary encoding, binary validation, and text printing for all of this.

@alexcrichton
Copy link
Member

Oh one other comment I'd recommend is to configure your text editor to rustfmt-on-save since there's some un-formatted files here which will get flagged on CI once #1174 merges

alexcrichton added a commit that referenced this pull request Aug 21, 2023
I noticed that currently in #1146 the CI there is green but some files
are unformatted which got me wondering why that was the case. Turns out
that `cargo fmt` won't find files that are referenced through
macro-expanded macros, meaning that most of `wast` isn't actually
checked for formatting on CI. This commit fixes that.
@macovedj macovedj force-pushed the impl-imports branch 2 times, most recently from e932d23 to a10792e Compare August 22, 2023 20:46
pub trait AsComponentExternName {
/// Converts this receiver into a `ComponentExternName`.
fn as_component_extern_name(&self) -> ComponentExternName<'_>;
pub trait AsComponentImportName {
Copy link
Member

Choose a reason for hiding this comment

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

This made sense beforehand since the kebab/interface distinction was easy to differentiate between. Now though it's slightly more complicated where a single-string as an import could either be Kebab, Interface, or Unlocked. While / or : means it's definitely not Kebab I don't believe there's any means, in isolation, to tell the difference between Unlocked and Interface. For example a:b/c could be either I believe.

In that sense I think this trait should be removed and consumers relying on it are going to have to use the more verbose form to select which import form they intended to use.

Comment on lines 344 to 413
pub fn from_export(name: ComponentExportName<'_>, offset: usize) -> Result<KebabName> {
let validate_kebab = |s: &str| {
if KebabStr::new(s).is_none() {
bail!(offset, "`{s}` is not in kebab case")
} else {
Ok(())
}
};
let find = |s: &str, c: char| match s.find(c) {
Some(i) => Ok(i),
None => bail!(offset, "failed to find `{c}` character"),
};

let parsed = match name {
ComponentExportName::Kebab(s) => {
if let Some(s) = s.strip_prefix(CONSTRUCTOR) {
validate_kebab(s)?;
ParsedKebabName::Constructor
} else if let Some(s) = s.strip_prefix(METHOD) {
let dot = find(s, '.')?;
validate_kebab(&s[..dot])?;
validate_kebab(&s[dot + 1..])?;
ParsedKebabName::Method { dot: dot as u32 }
} else if let Some(s) = s.strip_prefix(STATIC) {
let dot = find(s, '.')?;
validate_kebab(&s[..dot])?;
validate_kebab(&s[dot + 1..])?;
ParsedKebabName::Static { dot: dot as u32 }
} else {
validate_kebab(s)?;
ParsedKebabName::Normal
}
}
ComponentExportName::Interface(s) => {
let colon = find(s, ':')?;
validate_kebab(&s[..colon])?;
let slash = find(s, '/')?;
let at = s[slash..].find('@').map(|i| i + slash);
validate_kebab(&s[colon + 1..slash])?;
validate_kebab(&s[slash + 1..at.unwrap_or(s.len())])?;
if let Some(at) = at {
let version = &s[at + 1..];
let version = version.parse::<Version>();
match version {
Ok(ver) => ParsedKebabName::RegistryId {
colon: colon as u32,
slash: Some(slash as u32),
at: Some(At {
index: at as u32,
semver: Semver::Semver(ver),
}),
},
Err(e) => {
bail!(offset, "failed to parse version: {e}")
}
}
} else {
ParsedKebabName::RegistryId {
colon: colon as u32,
slash: Some(slash as u32),
at: None,
}
}
}
};
Ok(KebabName {
raw: name.as_str().to_string(),
parsed,
})
}
Copy link
Member

Choose a reason for hiding this comment

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

I believe this method is largely duplicated with the one below, but could the duplication be removed?

package: &'a KebabStr,
interface: &'a KebabStr,
version: Option<Version>,
interface: Option<&'a KebabStr>,
Copy link
Member

Choose a reason for hiding this comment

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

To keep things somewhat simpler for now could this be left as &'a KebabStr and effectively only a:b/c works today?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The primary motivator here was that when actually using the new implementation imports, there is no / generally. For example, unlocked imports tend to look something like

(import (unlocked-dep "foo:five@{>=2.0.0 <3.0.0}") (instance (;0;) (type 1)))

It definitely would be nice to simplify if possible, as I added a lot of checks elsewhere to enable the optionality here, but I'm not sure the best way to go about that while simultaneously supporting this use case.

Comment on lines +605 to 650
ComponentImportName::Unlocked(name) => {
let colon = find(name, ':')?;
validate_kebab(&name[..colon])?;
let slash = maybe_find(name, '/');
if let Some(sl) = slash {
let at = name[sl..].find('@').map(|i| i + sl);
validate_kebab(&name[colon + 1..sl])?;
if let Some(at) = at {
validate_kebab(&name[sl + 1..at])?;
let version_string = validate_version_range(&name[at + 1..])?;
ParsedKebabName::RegistryId {
colon: colon as u32,
slash: Some(sl as u32),
at: Some(At {
index: at as u32,
semver: Semver::SemverRange(version_string),
}),
}
} else {
ParsedKebabName::RegistryId {
colon: colon as u32,
slash: Some(sl as u32),
at: None,
}
}
} else {
let at = name[colon..].find('@').map(|i| i + colon);
if let Some(at) = at {
validate_kebab(&name[colon + 1..at])?;
let version_string = validate_version_range(&name[at + 1..])?;
ParsedKebabName::RegistryId {
colon: colon as u32,
slash: None,
at: Some(At {
index: at as u32,
semver: Semver::SemverRange(version_string),
}),
}
} else {
ParsedKebabName::RegistryId {
colon: colon as u32,
slash: None,
at: None,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I believe this is the more-or-less the same parsing code as Locked, so could those be deduplicated?

Comment on lines +240 to +253
impl PartialEq for Semver {
fn eq(&self, other: &Semver) -> bool {
match self {
Self::Semver(version) => match other {
Semver::Semver(other_version) => version == other_version,
Semver::SemverRange(_) => false,
},
Self::SemverRange(version_range) => match other {
Semver::Semver(_) => false,
Semver::SemverRange(other_range) => version_range == other_range,
},
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be replaced with #[derive(PartialEq)] on the type?

@alexcrichton
Copy link
Member

If you'd like I don't mind taking it from here and landing this as well. Implementing these sorts of changes can be pretty tricky so I don't want to put too much on you by accident

* Fold the `Naked` case into the preexisting `Kebab` case with a new
  payload.
* Parse `integrity` with surrounding parens
* Make the `integrity` field on `Locked` optional
@alexcrichton
Copy link
Member

If it's ok with you @macovedj I'd prefer to hold off on this until WebAssembly/component-model#253 is resolved since that will change the binary and textual syntax of imports again. Additionally it will likely change the internals of representation within wasm-tools crates, so I think it would be best to handle that all at once rather than having intermediate states to deal with.

@macovedj
Copy link
Contributor Author

If it's ok with you @macovedj I'd prefer to hold off on this until WebAssembly/component-model#253 is resolved since that will change the binary and textual syntax of imports again. Additionally it will likely change the internals of representation within wasm-tools crates, so I think it would be best to handle that all at once rather than having intermediate states to deal with.

Makes sense to me!

@alexcrichton
Copy link
Member

I've opened up #1262 which is intended to be the successor to this PR which catches up with other component model developments as well

@macovedj macovedj closed this Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants