Skip to content

Commit 44ba223

Browse files
authored
Interface version canonicalization (#536)
* Add canonical interface name * Add 🔗 emoji-gate for canonical interface names * Adjust ebnf whitespace for alignment
1 parent d3b15cd commit 44ba223

File tree

2 files changed

+132
-54
lines changed

2 files changed

+132
-54
lines changed

design/mvp/Binary.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ rules, but rather merge the minimal need-to-know elements of both, with just
1212
enough detail to create a prototype. A complete definition of the binary format
1313
and validation will be present in the [formal specification](../../spec/).
1414

15-
See [Gated Features](Explainer.md#gated-features) for an explanation of 🪙 and 🔧.
15+
See [Gated Features](Explainer.md#gated-features) for an explanation of emoji
16+
annotations like 🪙 and 🔧.
1617

1718

1819
## Component Definitions
@@ -371,10 +372,13 @@ flags are set.
371372
(See [Import and Export Definitions](Explainer.md#import-and-export-definitions)
372373
in the explainer.)
373374
```ebnf
374-
import ::= in:<importname'> ed:<externdesc> => (import in ed)
375-
export ::= en:<exportname'> si:<sortidx> ed?:<externdesc>? => (export en si ed?)
376-
importname' ::= 0x00 len:<u32> in:<importname> => in (if len = |in|)
377-
exportname' ::= 0x00 len:<u32> en:<exportname> => en (if len = |en|)
375+
import ::= in:<importname'> ed:<externdesc> => (import in ed)
376+
export ::= en:<exportname'> si:<sortidx> ed?:<externdesc>? => (export en si ed?)
377+
importname' ::= 0x00 len:<u32> in:<importname> => in (if len = |in|)
378+
| 0x01 len:<u32> in:<importname> vs:<versionsuffix'> => in vs (if len = |in|) 🔗
379+
exportname' ::= 0x00 len:<u32> en:<exportname> => en (if len = |en|)
380+
| 0x01 len:<u32> en:<exportname> vs:<versionsuffix'> => in vs (if len = |in|) 🔗
381+
versionsuffix' ::= len:<u32> vs:<semversuffix> => (versionsuffix vs) (if len = |vs|) 🔗
378382
```
379383

380384
Notes:
@@ -399,7 +403,11 @@ Notes:
399403
`(result (own $R))`, where `$R` is the resource labeled `r`.
400404
* Validation of `[method]` names requires the first parameter of the function
401405
to be `(param "self" (borrow $R))`, where `$R` is the resource labeled `r`.
402-
* `<valid semver>` is as defined by [https://semver.org](https://semver.org/)
406+
* 🔗 Validation requires that `versionsuffix` is preceded by an `interfaceversion`
407+
matching `canonversion` and that the concatenation of the `canonversion` and
408+
the `versionsuffix` results in a `valid semver` as defined by
409+
[https://semver.org](https://semver.org/). A `versionsuffix` is otherwise
410+
ignored for validation except to improve diagnostic messages.
403411
* `<integrity-metadata>` is as defined by the
404412
[SRI](https://www.w3.org/TR/SRI/#dfn-integrity-metadata) spec.
405413

@@ -494,7 +502,9 @@ named once.
494502

495503
* The opcodes (for types, canon built-ins, etc) should be re-sorted
496504
* The two `list` type codes should be merged into one with an optional immediate.
497-
* The `0x00` prefix byte of `importname'` and `exportname'` will be removed or repurposed.
505+
* The `0x00` variant of `importname'` and `exportname'` will be removed. Any
506+
remaining variant(s) will be renumbered or the prefix byte will be removed or
507+
repurposed.
498508

499509

500510
[`core:byte`]: https://webassembly.github.io/spec/core/binary/values.html#binary-byte

design/mvp/Explainer.md

Lines changed: 115 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ more user-focused explanation, take a look at the
3333
* [Start definitions](#-start-definitions)
3434
* [Import and export definitions](#import-and-export-definitions)
3535
* [Name uniqueness](#name-uniqueness)
36+
* [Canonical interface name](#-canonical-interface-name)
3637
* [Component invariants](#component-invariants)
3738
* [JavaScript embedding](#JavaScript-embedding)
3839
* [JS API](#JS-API)
@@ -55,6 +56,7 @@ implemented, considered stable and included in a future milestone:
5556
* 🧵: threading built-ins
5657
* 🔧: fixed-length lists
5758
* 📝: the `error-context` type
59+
* 🔗: canonical interface names
5860

5961
(Based on the previous [scoping and layering] proposal to the WebAssembly CG,
6062
this repo merges and supersedes the [module-linking] and [interface-types]
@@ -294,7 +296,8 @@ sort ::= core <core:sort>
294296
| type
295297
| component
296298
| instance
297-
inlineexport ::= (export <exportname> <sortidx>)
299+
inlineexport ::= (export "<exportname>" <sortidx>)
300+
| (export "<exportname>" <versionsuffix> <sortidx>) 🔗
298301
```
299302
Because component-level function, type and instance definitions are different
300303
than core-level function, type and instance definitions, they are put into
@@ -574,8 +577,10 @@ instancedecl ::= core-prefix(<core:type>)
574577
| <alias>
575578
| <exportdecl>
576579
| <value> 🪙
577-
importdecl ::= (import <importname> bind-id(<externdesc>))
578-
exportdecl ::= (export <exportname> bind-id(<externdesc>))
580+
importdecl ::= (import "<importname>" bind-id(<externdesc>))
581+
| (import "<importname>" <versionsuffix> bind-id(<externdesc>)) 🔗
582+
exportdecl ::= (export "<exportname>" bind-id(<externdesc>))
583+
| (export "<exportname>" <versionsuffix> bind-id(<externdesc>)) 🔗
579584
externdesc ::= (<sort> (type <u32>) )
580585
| core-prefix(<core:moduletype>)
581586
| <functype>
@@ -988,6 +993,10 @@ and `$C1` is a subtype of `$C2`:
988993
)
989994
```
990995

996+
🔗 Note that [canonical interface names](#-canonical-interface-name) may be
997+
annotated with a `versionsuffix` which is ignored for type checking except to
998+
improve diagnostic messages.
999+
9911000
When we next consider type imports and exports, there are two distinct
9921001
subcases of `typebound` to consider: `eq` and `sub`.
9931002

@@ -2264,8 +2273,11 @@ the identifier `$x`). In the case of exports, the `<id>?` right after the
22642273
preceding definition being exported (e.g., `(export $x "x" (func $f))` binds a
22652274
new identifier `$x`).
22662275
```ebnf
2267-
import ::= (import "<importname>" bind-id(<externdesc>))
2268-
export ::= (export <id>? "<exportname>" <sortidx> <externdesc>?)
2276+
import ::= (import "<importname>" bind-id(<externdesc>))
2277+
| (import "<importname>" <versionsuffix> bind-id(<externdesc>)) 🔗
2278+
export ::= (export <id>? "<exportname>" <sortidx> <externdesc>?)
2279+
| (export <id>? "<exportname>" <versionsuffix> <sortidx> <externdesc>?) 🔗
2280+
versionsuffix ::= (versionsuffix "<semversuffix>") 🔗
22692281
```
22702282
All import names are required to be [strongly-unique]. Separately, all export
22712283
names are also required to be [strongly-unique]. The rest of the grammar for
@@ -2279,47 +2291,53 @@ interpreted as a *lexical* grammar defining a single token and thus whitespace
22792291
is not automatically inserted, all terminals are single-quoted, and everything
22802292
unquoted is a meta-character.
22812293
```ebnf
2282-
exportname ::= <plainname>
2283-
| <interfacename>
2284-
importname ::= <exportname>
2285-
| <depname>
2286-
| <urlname>
2287-
| <hashname>
2288-
plainname ::= <label>
2289-
| '[async]' <label> 🔀
2290-
| '[constructor]' <label>
2291-
| '[method]' <label> '.' <label>
2292-
| '[async method]' <label> '.' <label> 🔀
2293-
| '[static]' <label> '.' <label>
2294-
| '[async static]' <label> '.' <label> 🔀
2295-
label ::= <fragment>
2296-
| <label> '-' <fragment>
2297-
fragment ::= <word>
2298-
| <acronym>
2299-
word ::= [a-z] [0-9a-z]*
2300-
acronym ::= [A-Z] [0-9A-Z]*
2301-
interfacename ::= <namespace> <label> <projection> <version>?
2302-
| <namespace>+ <label> <projection>+ <version>? 🪺
2303-
namespace ::= <words> ':'
2304-
words ::= <word>
2305-
| <words> '-' <word>
2306-
projection ::= '/' <label>
2307-
version ::= '@' <valid semver>
2308-
depname ::= 'unlocked-dep=<' <pkgnamequery> '>'
2309-
| 'locked-dep=<' <pkgname> '>' ( ',' <hashname> )?
2310-
pkgnamequery ::= <pkgpath> <verrange>?
2311-
pkgname ::= <pkgpath> <version>?
2312-
pkgpath ::= <namespace> <words>
2313-
| <namespace>+ <words> <projection>* 🪺
2314-
verrange ::= '@*'
2315-
| '@{' <verlower> '}'
2316-
| '@{' <verupper> '}'
2317-
| '@{' <verlower> ' ' <verupper> '}'
2318-
verlower ::= '>=' <valid semver>
2319-
verupper ::= '<' <valid semver>
2320-
urlname ::= 'url=<' <nonbrackets> '>' (',' <hashname>)?
2321-
nonbrackets ::= [^<>]*
2322-
hashname ::= 'integrity=<' <integrity-metadata> '>'
2294+
exportname ::= <plainname>
2295+
| <interfacename>
2296+
importname ::= <exportname>
2297+
| <depname>
2298+
| <urlname>
2299+
| <hashname>
2300+
plainname ::= <label>
2301+
| '[async]' <label> 🔀
2302+
| '[constructor]' <label>
2303+
| '[method]' <label> '.' <label>
2304+
| '[async method]' <label> '.' <label> 🔀
2305+
| '[static]' <label> '.' <label>
2306+
| '[async static]' <label> '.' <label> 🔀
2307+
label ::= <fragment>
2308+
| <label> '-' <fragment>
2309+
fragment ::= <word>
2310+
| <acronym>
2311+
word ::= [a-z] [0-9a-z]*
2312+
acronym ::= [A-Z] [0-9A-Z]*
2313+
interfacename ::= <namespace> <label> <projection> <interfaceversion>?
2314+
| <namespace>+ <label> <projection>+ <interfaceversion>? 🪺
2315+
namespace ::= <words> ':'
2316+
words ::= <word>
2317+
| <words> '-' <word>
2318+
projection ::= '/' <label>
2319+
interfaceversion ::= '@' <valid semver>
2320+
| '@' <canonversion> 🔗
2321+
canonversion ::= [1-9] [0-9]* 🔗
2322+
| '0.' [1-9] [0-9]* 🔗
2323+
| '0.0.' [1-9] [0-9]* 🔗
2324+
semversuffix ::= [0-9A-Za-z.+-]* 🔗
2325+
depname ::= 'unlocked-dep=<' <pkgnamequery> '>'
2326+
| 'locked-dep=<' <pkgname> '>' ( ',' <hashname> )?
2327+
pkgnamequery ::= <pkgpath> <verrange>?
2328+
pkgname ::= <pkgpath> <pkgversion>?
2329+
pkgversion ::= '@' <valid semver>
2330+
pkgpath ::= <namespace> <words>
2331+
| <namespace>+ <words> <projection>* 🪺
2332+
verrange ::= '@*'
2333+
| '@{' <verlower> '}'
2334+
| '@{' <verupper> '}'
2335+
| '@{' <verlower> ' ' <verupper> '}'
2336+
verlower ::= '>=' <valid semver>
2337+
verupper ::= '<' <valid semver>
2338+
urlname ::= 'url=<' <nonbrackets> '>' (',' <hashname>)?
2339+
nonbrackets ::= [^<>]*
2340+
hashname ::= 'integrity=<' <integrity-metadata> '>'
23232341
```
23242342
Components provide six options for naming imports:
23252343
* a **plain name** that leaves it up to the developer to "read the docs"
@@ -2394,7 +2412,9 @@ tooling as "registries":
23942412
parameter of [`WebAssembly.instantiate()`])
23952413

23962414
The `valid semver` production is as defined by the [Semantic Versioning 2.0]
2397-
spec and is meant to be interpreted according to that specification. The
2415+
spec and is meant to be interpreted according to that specification. The use of
2416+
`valid semver` in `interfaceversion` is temporary for backward compatibility;
2417+
see [Canonical interface name](#-canonical-interface-name) below (🔗). The
23982418
`verrange` production embeds a minimal subset of the syntax for version ranges
23992419
found in common package managers like `npm` and `cargo` and is meant to be
24002420
interpreted with the same [semantics][SemVerRange]. (Mostly this
@@ -2561,6 +2581,53 @@ annotations. For example, the validation rules for `[constructor]foo` require
25612581
for details.
25622582

25632583

2584+
### 🔗 Canonical Interface Name
2585+
2586+
An `interfacename` (as defined above) is **canonical** iff it either:
2587+
2588+
- has no `interfaceversion`
2589+
- has an `interfaceversion` matching the `canonversion` production
2590+
2591+
The purpose of `canonversion` is to simplify the matching of compatible import
2592+
and export versions. For example, if a guest imports some interface from
2593+
`wasi:http/[email protected]` and a host provides the (subtype-compatible) interface
2594+
`wasi:http/[email protected]`, we'd like to make it easy for the host to link with the
2595+
guest. The `canonversion` for both of these interfaces would be `0.2`, so this
2596+
linking could be done by matching canonical interface names literally.
2597+
Symmetrically, if a host provides `wasi:http/[email protected]` and a guest imports
2598+
`wasi:http/[email protected]`, so long as the guest only uses the subset of
2599+
functions defined in `wasi:http/[email protected]` (which is checked by normal
2600+
component type validation), linking succeeds. Thus, including only the
2601+
canonicalized version in the name allows both backwards and (limited)
2602+
forwards compatibility using only trivial string equality (as well as the
2603+
type checking already required).
2604+
2605+
Any `valid semver` (as used in WIT) can be canonicalized by splitting it into
2606+
two parts - the `canonversion` prefix and the remaining `semversuffix`. Using
2607+
the `<major>.<minor>.<patch>` syntax of [Semantic Versioning 2.0], the split
2608+
point is chosen as follows:
2609+
2610+
- if `major` > 0, split immediately after `major`
2611+
- `1.2.3` &rarr; `1` / `.2.3`
2612+
- otherwise if `minor` > 0, split immediately after `minor`
2613+
- `0.2.6-rc.1` &rarr; `0.2` / `.6-rc.1`
2614+
- otherwise, split immediately after `patch`
2615+
- `0.0.1-alpha` &rarr; `0.0.1` / `-alpha`
2616+
2617+
When a version is canonicalized, any `semversuffix` that was split off of the
2618+
version should be preserved in the `versionsuffix` field of any resulting
2619+
`import`s and `export`s. This gives component runtimes and other tools access to
2620+
the original version for error messages, documentation, and other development
2621+
purposes. Where a `versionsuffix` is present the preceding `interfacename` must
2622+
have a `canonversion`, and the concatenation of the `canonversion` and
2623+
`versionsuffix` must be a `valid semver`.
2624+
2625+
For compatibility with older versions of this spec, non-canonical
2626+
`interfacename`s (with `interfaceversion`s matching any `valid semver`) are
2627+
temporarily permitted. These non-canonical names may trigger warnings and will
2628+
start being rejected some time after after [WASI Preview 3] is released.
2629+
2630+
25642631
## Component Invariants
25652632

25662633
As a consequence of the shared-nothing design described above, all calls into
@@ -2916,6 +2983,7 @@ For some use-case-focused, worked examples, see:
29162983
[`rectype`]: https://webassembly.github.io/gc/core/text/types.html#text-rectype
29172984
[shared-everything-threads]: https://github.com/WebAssembly/shared-everything-threads
29182985
[WASI Preview 2]: https://github.com/WebAssembly/WASI/tree/main/wasip2#readme
2986+
[WASI Preview 3]: https://github.com/WebAssembly/WASI/tree/main/wasip2#looking-forward-to-preview-3
29192987
[reference types]: https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md
29202988

29212989
[Strongly-unique]: #name-uniqueness

0 commit comments

Comments
 (0)