-
Notifications
You must be signed in to change notification settings - Fork 91
Interface version canonicalization #536
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ more user-focused explanation, take a look at the | |
* [Start definitions](#-start-definitions) | ||
* [Import and export definitions](#import-and-export-definitions) | ||
* [Name uniqueness](#name-uniqueness) | ||
* [Canonical interface name](#canonical-interface-name) | ||
* [Component invariants](#component-invariants) | ||
* [JavaScript embedding](#JavaScript-embedding) | ||
* [JS API](#JS-API) | ||
|
@@ -294,7 +295,7 @@ sort ::= core <core:sort> | |
| type | ||
| component | ||
| instance | ||
inlineexport ::= (export <exportname> <sortidx>) | ||
inlineexport ::= (export "<exportname>" <versionsuffix>? <sortidx>) | ||
``` | ||
Because component-level function, type and instance definitions are different | ||
than core-level function, type and instance definitions, they are put into | ||
|
@@ -574,8 +575,8 @@ instancedecl ::= core-prefix(<core:type>) | |
| <alias> | ||
| <exportdecl> | ||
| <value> 🪙 | ||
importdecl ::= (import <importname> bind-id(<externdesc>)) | ||
exportdecl ::= (export <exportname> bind-id(<externdesc>)) | ||
importdecl ::= (import "<importname>" <versionsuffix>? bind-id(<externdesc>)) | ||
exportdecl ::= (export "<exportname>" <versionsuffix>? bind-id(<externdesc>)) | ||
externdesc ::= (<sort> (type <u32>) ) | ||
| core-prefix(<core:moduletype>) | ||
| <functype> | ||
|
@@ -2242,8 +2243,9 @@ the identifier `$x`). In the case of exports, the `<id>?` right after the | |
preceding definition being exported (e.g., `(export $x "x" (func $f))` binds a | ||
new identifier `$x`). | ||
```ebnf | ||
import ::= (import "<importname>" bind-id(<externdesc>)) | ||
export ::= (export <id>? "<exportname>" <sortidx> <externdesc>?) | ||
import ::= (import "<importname>" <versionsuffix>? bind-id(<externdesc>)) | ||
export ::= (export <id>? "<exportname>" <versionsuffix>? <sortidx> <externdesc>?) | ||
versionsuffix ::= (versionsuffix "<semversuffix>") | ||
``` | ||
All import names are required to be [strongly-unique]. Separately, all export | ||
names are also required to be [strongly-unique]. The rest of the grammar for | ||
|
@@ -2276,17 +2278,24 @@ fragment ::= <word> | |
| <acronym> | ||
word ::= [a-z] [0-9a-z]* | ||
acronym ::= [A-Z] [0-9A-Z]* | ||
interfacename ::= <namespace> <label> <projection> <version>? | ||
| <namespace>+ <label> <projection>+ <version>? 🪺 | ||
interfacename ::= <namespace> <label> <projection> <interfaceversion>? | ||
| <namespace>+ <label> <projection>+ <interfaceversion>? 🪺 | ||
namespace ::= <words> ':' | ||
words ::= <word> | ||
| <words> '-' <word> | ||
projection ::= '/' <label> | ||
version ::= '@' <valid semver> | ||
# FIXME: surrounding alignment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO after final review |
||
interfaceversion ::= '@' <valid semver> | ||
| '@' <canonversion> | ||
canonversion ::= [1-9] [0-9]* | ||
| '0.' [1-9] [0-9]* | ||
| '0.0.' [1-9] [0-9]* | ||
semversuffix ::= [0-9A-Za-z.+-]* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This just checks for valid semver characters. The "real" validation is covered in prose below, i.e. I had a version of this as |
||
depname ::= 'unlocked-dep=<' <pkgnamequery> '>' | ||
| 'locked-dep=<' <pkgname> '>' ( ',' <hashname> )? | ||
pkgnamequery ::= <pkgpath> <verrange>? | ||
pkgname ::= <pkgpath> <version>? | ||
pkgname ::= <pkgpath> <pkgversion>? | ||
pkgversion ::= '@' <valid semver> | ||
pkgpath ::= <namespace> <words> | ||
| <namespace>+ <words> <projection>* 🪺 | ||
verrange ::= '@*' | ||
|
@@ -2372,12 +2381,14 @@ tooling as "registries": | |
parameter of [`WebAssembly.instantiate()`]) | ||
|
||
The `valid semver` production is as defined by the [Semantic Versioning 2.0] | ||
spec and is meant to be interpreted according to that specification. The | ||
`verrange` production embeds a minimal subset of the syntax for version ranges | ||
found in common package managers like `npm` and `cargo` and is meant to be | ||
interpreted with the same [semantics][SemVerRange]. (Mostly this | ||
interpretation is the usual SemVer-spec-defined ordering, but note the | ||
particular behavior of pre-release tags.) | ||
spec and is meant to be interpreted according to that specification. The use of | ||
`valid semver` in `interfaceversion` is temporary for backward compatibility; | ||
see [Canonical interface name](#canonical-interface-name) below. The `verrange` | ||
production embeds a minimal subset of the syntax for version ranges found in | ||
common package managers like `npm` and `cargo` and is meant to be interpreted | ||
with the same [semantics][SemVerRange]. (Mostly this interpretation is the usual | ||
SemVer-spec-defined ordering, but note the particular behavior of pre-release | ||
tags.) | ||
|
||
The `plainname` production captures several language-neutral syntactic hints | ||
that allow bindings generators to produce more idiomatic bindings in their | ||
|
@@ -2539,6 +2550,49 @@ annotations. For example, the validation rules for `[constructor]foo` require | |
for details. | ||
|
||
|
||
### Canonical Interface Name | ||
lann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
An `interfacename` (as defined above) is **canonical** iff it either: | ||
|
||
- has no `interfaceversion` | ||
- has an `interfaceversion` matching the `canonversion` production | ||
|
||
The purpose of `canonversion` is to simplify the matching of compatible import | ||
and export versions. For example, if a guest imports some interface from | ||
`wasi:http/[email protected]` and a host provides the (subtype-compatible) interface | ||
`wasi:http/[email protected]`, we'd like to make it easy for the host to link with the | ||
guest. The `canonversion` for both of these interfaces would be `0.2`, so this | ||
linking could be done by matching canonical interface names literally. | ||
lann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Symmetrically, if a host provides `wasi:http/[email protected]` and a guest imports | ||
`wasi:http/[email protected]`, so long as the guest's imported `instancetype` is | ||
subtype-compatible with the host implementation linking should be trivial. | ||
|
||
Any `valid semver` (as used in WIT) can be canonicalized by splitting it into | ||
two parts - the `canonversion` prefix and the remaining `semversuffix`. Using | ||
the `<major>.<minor>.<patch>` syntax of [Semantic Versioning 2.0], the split | ||
point is chosen as follows: | ||
|
||
- if `major` > 0, split immediately after `major` | ||
- `1.2.3` → `1` / `.2.3` | ||
- otherwise if `minor` > 0, split immediately after `minor` | ||
- `0.2.6-rc.1` → `0.2` / `.6-rc.1` | ||
- otherwise, split immediately after `patch` | ||
- `0.0.1-alpha` → `0.0.1` / `-alpha` | ||
|
||
When a version is canonicalized, any `semversuffix` that was split off of the | ||
version should be preserved in the `versionsuffix` field of any resulting | ||
`import`s and `export`s. This gives component runtimes and other tools access to | ||
the original version for error messages, documentation, and other development | ||
purposes. Where a `versionsuffix` is present the preceding `interfacename` must | ||
have a `canonversion`, and the concatenation of the `canonversion` and | ||
`versionsuffix` must be a `valid semver`. | ||
|
||
For compatibility with older versions of this spec, non-canonical | ||
`interfacename`s (with `interfaceversion`s matching any `valid semver`) are | ||
temporarily permitted. These non-canonical names may trigger warnings and will | ||
start being rejected some time after after [WASI Preview 3] is released. | ||
|
||
|
||
## Component Invariants | ||
|
||
As a consequence of the shared-nothing design described above, all calls into | ||
|
@@ -2894,6 +2948,7 @@ For some use-case-focused, worked examples, see: | |
[`rectype`]: https://webassembly.github.io/gc/core/text/types.html#text-rectype | ||
[shared-everything-threads]: https://github.com/WebAssembly/shared-everything-threads | ||
[WASI Preview 2]: https://github.com/WebAssembly/WASI/tree/main/wasip2#readme | ||
[WASI Preview 3]: https://github.com/WebAssembly/WASI/tree/main/wasip2#looking-forward-to-preview-3 | ||
[reference types]: https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md | ||
|
||
[Strongly-unique]: #name-uniqueness | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<valid semver>
wasn't referenced in this file.