-
Notifications
You must be signed in to change notification settings - Fork 129
Add getters and setters (#235) #701
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
e6ab489
9639973
f2aa864
cb43e7e
f230fc0
b08dd0b
56a2d23
417e0ea
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 | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -420,9 +420,11 @@ Notes: | |||||||
| be [strongly-unique]; attributes are ignored. | ||||||||
| * The `externname`s of all exports in a given component, instance, component- | ||||||||
| type or instance-type must be [strongly-unique]; attributes are ignored. | ||||||||
| * Validation requires that `[constructor]`, `[method]` and `[static]` annotated | ||||||||
| `plainname`s only occur on `func` imports or exports and that the first label | ||||||||
| of a `[constructor]`, `[method]` or `[static]` matches the `plainname` of a | ||||||||
| * Validation requires that `plainname`s annotated with `[constructor]`, | ||||||||
| `[method]`, `[static]`, 📡 `[get]`, or 📡 `[set]` only occur on `func` imports | ||||||||
| or exports. | ||||||||
| * Validation requires that, for `plainname`s annotated with `[constructor]`, | ||||||||
| `[method]`, or `[static]`, the first `label` matches the `plainname` of a | ||||||||
| preceding `resource` import or export, respectively, in the same scope | ||||||||
| (component, component type or instance type). | ||||||||
| * 🏷️ Validation requires that `implements`-annotated imports or exports are | ||||||||
|
|
@@ -433,10 +435,28 @@ Notes: | |||||||
| `vec(<attribute>)`, this list is entirely ignored when validating the types | ||||||||
| of components and instances. | ||||||||
| * Validation of `[constructor]` names requires a `func` type whose result type | ||||||||
| is either `(own $R)` or `(result (own $R) E?)` where `$R` is a resource type | ||||||||
| labeled `r`. | ||||||||
| is either `(own $R)` or `(result (own $R) (error $E)?)`, where `$R` is the | ||||||||
| named resource type. | ||||||||
| * Validation of `[method]` names requires the first parameter of the function | ||||||||
| to be `(param "self" (borrow $R))`, where `$R` is the resource labeled `r`. | ||||||||
| to be `(param "self" (borrow $R))`, where `$R` is the named resource type. | ||||||||
| * 📡 Validation of `[get]` names requires that the function have no parameters, | ||||||||
| unless the name is also annotated with `[method]`, in which case `self` must | ||||||||
| be the only parameter. | ||||||||
| * 📡 Validation of `[get]` names requires that the function have a result type. | ||||||||
| * 📡 Validation of `[set]` names requires that the function have exactly one | ||||||||
| parameter, unless the name is also annotated with `[method]`, in which case | ||||||||
| there must be two parameters, the first of which is `self`. | ||||||||
|
Member
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.
Suggested change
|
||||||||
| * 📡 Validation of `[set]` names requires that the function have either no | ||||||||
| result type or a result type of `(result (error $E)?)`. | ||||||||
| * 📡 If a name with `[set]` is defined as an import or export within a | ||||||||
| particular scope, the equivalent name with `[get]` must have already been | ||||||||
| defined as an import or export respectively in that same scope—that is, all | ||||||||
| labels must be equal (before canonicalization), and all annotations must be | ||||||||
| the same except that `[set]` is replaced with `[get]`, and the `[get]` | ||||||||
| import/export must precede the `[set]` import/export. For example, | ||||||||
| `[set]prop` requires `[get]prop`, and `[method][set]foo.bar` requires | ||||||||
| `[method][get]foo.bar`. | ||||||||
| * 🔀/📡 Functions with `[get]` or `[set]` names must not be `async`. | ||||||||
| * 🔗 Validation requires that `versionsuffix` is preceded by an `interfaceversion` | ||||||||
| matching `canonversion` and that the concatenation of the `canonversion` and | ||||||||
| the `versionsuffix` results in a `valid semver` as defined by | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,7 @@ shipped as part of a future WASI Developer Preview release: | |
| * 📝: the `error-context` type | ||
| * 🔗: canonical interface names | ||
| * 🐘: [memory64] | ||
| * 📡: getters and setters | ||
|
|
||
|
|
||
| ## Grammar | ||
|
|
@@ -2708,9 +2709,11 @@ externnamelit ::= '"' <externname> '"' | |
| externname ::= <plainname> | ||
| | <interfacename> | ||
| plainname ::= <label> | ||
| | '[get]' <label> 📡 | ||
| | '[set]' <label> 📡 | ||
|
Comment on lines
+2712
to
+2713
Member
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. Are these 2 cases still intended and, if so, do they have a 3rd meaning distinct from
Collaborator
Author
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. Yes, these are the interface-level getters and setters we discussed for things like |
||
| | '[constructor]' <label> | ||
| | '[method]' <label> '.' <label> | ||
| | '[static]' <label> '.' <label> | ||
| | '[method]' (📡 '[get]' | '[set]')? <label> '.' <label> | ||
| | '[static]' (📡 '[get]' | '[set]')? <label> '.' <label> | ||
| labellit ::= '"' <label> '"' | ||
| label ::= <first-fragment> ( '-' <fragment> )* | ||
| first-fragment ::= <first-word> | ||
|
|
@@ -2745,11 +2748,12 @@ The names of component imports and exports provide two options: | |
|
|
||
| The `plainname` production captures several language-neutral syntactic hints | ||
| that allow bindings generators to produce more idiomatic bindings in their | ||
| target language. At the top-level, a `plainname` allows functions to be | ||
| annotated as being a constructor, method or static function of a preceding | ||
| resource. | ||
| target language. At the top level, a `plainname` allows functions to be | ||
| annotated as being a constructor, method, 📡 property getter, 📡 property setter, | ||
| or static function of a preceding resource, as well as a 📡 property getter or | ||
| 📡 property setter not attached to a resource. | ||
|
|
||
| When a function is annotated with `constructor`, `method` or `static`, the | ||
| When a function is annotated with `constructor`, `method`, or `static`, the | ||
| first `label` is the name of the resource and the second `label` is the logical | ||
| field name of the function. This additional nesting information allows bindings | ||
| generators to insert the function into the nested scope of a class, abstract | ||
|
|
@@ -2759,16 +2763,21 @@ member function `foo` in a class `C`. The JS API [below](#JS-API) describes how | |
| the native JavaScript bindings could look. | ||
|
|
||
| To restrict the set of cases that bindings generators need to consider, these | ||
| annotations trigger additional type-validation rules (listed in | ||
| [Binary.md](Binary.md)) such as: | ||
| * An import or export named `[static]R.foo` must be a function and `R` must | ||
| be the name of an imported or exported resource type in the same `instance` | ||
| or `component` type. | ||
| * Similarly, an import or export named `[constructor]R` must be a function | ||
| whose return type must be `(own $R)` or `(result (own $R) (error <valtype>)?)` | ||
| where `$R` is the type-index of the resource type named `R`. | ||
| * Similarly, an import or export named `[method]R.foo` must be a function whose | ||
| first parameter must be `(param "self" (borrow $R))`. | ||
| annotations trigger additional type-validation rules (listed in detail in | ||
| [Binary.md](Binary.md)): | ||
| * A `[constructor]` import or export named `R` must be a function whose result | ||
| type is `(own $R)` or `(result (own $R) (error <valtype>)?)`, where `$R` is | ||
| the index of the resource type named `R`. | ||
| * A `[method]` import or export named `R.foo` must be a function whose first | ||
| parameter is `(param "self" (borrow $R))`, where `$R` is the index of the | ||
| resource type named `R`. | ||
| * A `[static]` import or export named `R.foo` must be a function, and `R` must | ||
| be the name of a resource type. | ||
| * 📡 A `[get]` import or export must have no parameters (besides the required | ||
| `self` parameter from `[method]`), and must have a result type. | ||
| * 📡 A `[set]` import or export must have exactly one parameter (besides the | ||
| required `self` parameter from `[method]`), and must have either no result | ||
| type or a result type of `(result (error <valtype>)?)`. | ||
|
|
||
| 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 use of | ||
|
|
@@ -2832,28 +2841,66 @@ within the same scope. | |
|
|
||
| To determine whether two names (defined as sequences of [Unicode Scalar | ||
| Values]) are **strongly-unique**: | ||
|
|
||
| 1. Canonicalize each name: | ||
| 1. Lowercase all the `acronym`s (uppercase letters) in the name. | ||
| 2. If the name is `[method]l.l` or `[static]l.l` for some `label` `l`, replace | ||
| the name with `l` (e.g. `[method]foo.foo` becomes `foo`). | ||
| 3. If the name has any `[...]` annotation prefix other than `[constructor]`, | ||
| strip it from the name. | ||
| 1. Lowercase all the `acronym`s (uppercase letters) in the name. | ||
| 2. If the name is `[...]*l.l` for any annotations `[...]*` and some `label` | ||
| `l`, replace the name with `l` (e.g. `[method]foo.foo` becomes `foo`). | ||
| 3. Strip all `[...]` annotations from the name besides `[constructor]` | ||
| and 📡 `[set]`. | ||
| 2. The names are strongly-unique if the resulting canonicalized strings are | ||
| unequal. | ||
| unequal. | ||
|
|
||
| Thus, the following set of names are strongly-unique and can thus all be | ||
| imports (or exports) of the same component (or component type or instance | ||
| type): | ||
|
|
||
| Thus, the following set of names are strongly-unique and can thus all be imports (or exports) of the same component (or component type or instance type): | ||
| * `foo`, `foo-bar`, `[constructor]foo`, `[method]foo.bar`, `[static]foo.baz`, `foo:bar/baz` | ||
| * `foo`, `foo-bar`, `[constructor]foo`, `[method]foo.bar`, `[static]foo.baz`, | ||
| `foo:bar/baz`, 📡 `[get]prop`, 📡 `[set]prop`, 📡 `[method][get]foo.prop`, | ||
| 📡 `[method][set]foo.prop`, 📡 `[static][get]foo.prop-2`, | ||
| 📡 `[static][set]foo.prop-2`, `[method]foo.get-prop`, `[method]foo.set-prop` | ||
|
|
||
| but attempting to add *any* of the following names would be a validation error: | ||
| * `foo`, `FOO`, `foo-BAR`, `[constructor]FOO`, `[method]foo.BAR`, `[static]foo.bar`, `[method]foo.baz`, `[method]foo.foo`, `[static]foo-BAR.FOO-bar`, `foo:bar/BAZ` | ||
|
|
||
| The purpose of steps 1.2 and 1.3 is to play nice with constructors: in many languages, constructors are the only item that can have the same name as the class. | ||
| * `foo`, `FOO`, `[method]foo.foo`, 📡 `[get]foo`, 📡 `[method][get]foo.foo`, | ||
| 📡 `[static][set]foo.FOO` (conflicts with `foo`) | ||
| * `foo-BAR`, `[static]foo-BAR.FOO-bar` (conflicts with `foo-bar`) | ||
| * `[constructor]FOO` (conflicts with `[constructor]foo`) | ||
| * `[method]foo.BAR`, `[static]foo.bar` (conflicts with `[method]foo.bar`) | ||
| * `[method]foo.baz` (conflicts with `[static]foo.baz`) | ||
| * `foo:bar/BAZ` (conflicts with `foo:bar/baz`) | ||
| * 📡 `prop` (conflicts with `[get]prop`) | ||
| * 📡 `[set]PROP` (conflicts with `[set]prop`) | ||
| * 📡 `[method]foo.prop`, 📡 `[static]foo.PROP`, 📡 `[method][get]foo.PROP`, | ||
| 📡 `[static][get]foo.prop` (conflicts with `[method][get]foo.prop`) | ||
| * 📡 `[method][set]foo.PROP`, 📡 `[static][set]foo.prop` (conflicts with | ||
| `[method][set]foo.prop`) | ||
|
|
||
| Note that additional validation rules involving types apply to names with | ||
| annotations. For example, the validation rules for `[constructor]foo` require | ||
| `foo` to be a resource type. See [Binary.md](Binary.md#import-and-export-definitions) | ||
| for details. | ||
|
|
||
| Note that these rules prevent having a method or static function (📡 or getter | ||
| or setter) with the same name as its resource type, leaving room for a | ||
| constructor. This is to satisfy the naming rules around constructors in many | ||
| languages. | ||
|
|
||
| 📡 Also note that, although `[get]foo` and `foo` are not considered | ||
| strongly-unique, `[set]foo` and `foo` are. However, since `[set]foo` separately | ||
| requires `[get]foo` to be present, there is no practical concern of a bindings | ||
| conflict between `[set]foo` and `foo`. The same goes for other uses of `[get]` | ||
| and `[set]` annotations. | ||
|
|
||
| 📡 Note also that `[method][get]foo.prop` and `[method]foo.get-prop` are | ||
| considered strongly-unique even though in practice they may conflict. Likewise, | ||
| `[method][set]foo.prop` and `[method]foo.set-prop` are considered | ||
| strongly-unique. If this results in a conflict for a bindings generator, the | ||
| bindings generator may choose one of the two and ignore the other. This is a | ||
| temporary measure: in a later release, it is expected that both of these | ||
| examples will not be considered strongly-unique, so API designers are | ||
| encouraged to avoid this case. | ||
|
|
||
|
|
||
| #### 🔗 Canonical Interface Name | ||
|
|
||
|
|
@@ -3051,7 +3098,8 @@ object*] steps need to be expanded to cover them: | |
| For type exports, each type definition would export a JS constructor function. | ||
| This function would be callable iff a `[constructor]`-annotated function was | ||
| also exported. All `[method]`- and `[static]`-annotated functions would be | ||
| dynamically installed on the constructor's prototype chain. In the case of | ||
| dynamically installed on the constructor's prototype chain, making sure to | ||
| register `[get]` and `[set]` functions as getters and setters. In the case of | ||
| re-exports and multiple exports of the same definition, the same constructor | ||
| function object would be exported (following the same rules as WebAssembly | ||
| Exported Functions today). In pathological cases (which, importantly, don't | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,7 @@ document, a pseudo-formal [grammar specification][lexical-structure], and | |
| additionally a specification of the [package format][package-format] of a WIT | ||
| package suitable for distribution. | ||
|
|
||
| See [Gated Features] for an explanation of 🔧 and 🏷️. | ||
| See [Gated Features] for an explanation of emoji like 🔧 and 🏷️. | ||
|
|
||
| [IDL]: https://en.wikipedia.org/wiki/Interface_description_language | ||
| [components]: https://github.com/webassembly/component-model | ||
|
|
@@ -1069,6 +1069,7 @@ keyword ::= 'as' | |
| | 'from' | ||
| | 'func' | ||
| | 'future' | ||
| | 'get' 📡 | ||
|
Collaborator
Author
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. It's not clear to me that these actually need to be added to a global keyword list, given that it will likely cause breakage and they can only appear in a specific place. |
||
| | 'import' | ||
| | 'include' | ||
| | 'interface' | ||
|
|
@@ -1084,6 +1085,7 @@ keyword ::= 'as' | |
| | 's32' | ||
| | 's64' | ||
| | 's8' | ||
| | 'set' 📡 | ||
| | 'static' | ||
| | 'stream' | ||
| | 'string' | ||
|
|
@@ -1557,6 +1559,8 @@ typedef-item ::= resource-item | |
| func-item ::= id ':' func-type ';' | ||
|
|
||
| func-type ::= 'async'? 'func' param-list result-list | ||
| | 'get' param-list result-list 📡 | ||
| | 'set' param-list result-list 📡 | ||
|
|
||
| param-list ::= '(' named-type-list ')' | ||
|
|
||
|
|
@@ -1575,6 +1579,29 @@ source-language bindings (e.g., `async` functions in JS, Python, C# or Rust) if | |
| concurrency execution is desired. For more details, see the [concurrency | ||
| explainer](Concurrency.md#summary). | ||
|
|
||
| 📡 As syntactic sugar, functions can be declared as *getters* or *setters* by | ||
| replacing the `func` keyword with `get` or `set`. Such functions cannot be | ||
| async and have additional restrictions on their parameters and results. A `get` | ||
| function must have no parameters and must return a value. A `set` function | ||
| must have exactly one parameter and must not return a value unless that value | ||
| is of type `result<_, error?>`. Every setter must have a corresponding getter | ||
| with the same name. To simplify validation, the getter must be defined before | ||
| the setter. For example, the following definitions: | ||
|
|
||
| ```wit | ||
| foo: func(x: u32); | ||
| bar: get() -> u64; | ||
| bar: set(v: u64); | ||
| ``` | ||
|
|
||
| desugar into: | ||
|
|
||
| ```wit | ||
| foo: func(x: u32); | ||
| [get]bar: func() -> u64; | ||
| [set]bar: func(v: u64); | ||
| ``` | ||
|
|
||
| 🏷️ As with `import`s and `export`s in worlds, and for the same use cases, | ||
| interface items can be annotated with `@external-id`: | ||
| ```wit | ||
|
|
@@ -1770,32 +1797,53 @@ functions*, which do not have an implicit `self` parameter but are meant to be | |
| lexically nested in the scope of the resource type. Lastly, a resource | ||
| statement can contain at most one *constructor* function, which is syntactic | ||
| sugar for a function returning a handle of the containing resource type. | ||
|
|
||
| Constructors can be fallible or infallible. Fallible constructors have an | ||
| explicitly-written return type which must be of the form `result<r, ...>` | ||
| where `r` is the name of the containing `resource`. Infallible constructors | ||
| have no written return type and are given the implicit return type `r`. | ||
| explicitly-written return type which must be of the form `result<r, ...>` where | ||
| `r` is the name of the containing `resource`. Infallible constructors have no | ||
| written return type and are given the implicit return type `r`. | ||
|
|
||
| 📡 A resource statement can also contain any number of *getters* and | ||
| *setters*, which may or may not be static. Non-static getters and setters also | ||
| implicitly take a `self` parameter. Getters take no parameters (besides the | ||
| implicit `self` parameter) and must return a value. Setters take exactly one | ||
| parameter (besides the implicit `self` parameter) and must not return a value | ||
| unless that value is of type `result<_, error?>`. Every setter must have a | ||
| corresponding getter with the same name and same static-ness. | ||
|
|
||
| For example, the following resource definitions: | ||
|
|
||
| For example, the following resource definition: | ||
| ```wit | ||
| resource blob { | ||
| constructor(init: list<u8>); | ||
| write: func(bytes: list<u8>); | ||
| read: func(n: u32) -> list<u8>; | ||
| merge: static func(lhs: borrow<blob>, rhs: borrow<blob>) -> blob; | ||
| position: get() -> u64; // 📡 | ||
| position: set(value: u64); // 📡 | ||
| max-size: static get() -> u64; // 📡 | ||
| max-size: static set(value: u64) -> result<_, string>; // 📡 | ||
| } | ||
| resource blob2 { | ||
| constructor(init: list<u8>) -> result<blob2>; | ||
| } | ||
| ``` | ||
| desugars into: | ||
|
|
||
| desugar into: | ||
|
|
||
| ```wit | ||
| resource blob; | ||
| %[constructor]blob: func(init: list<u8>) -> blob; | ||
| %[constructor]blob2: func(init: list<u8>) -> result<blob2>; | ||
| %[method]blob.write: func(self: borrow<blob>, bytes: list<u8>); | ||
| %[method]blob.read: func(self: borrow<blob>, n: u32) -> list<u8>; | ||
| %[static]blob.merge: func(lhs: borrow<blob>, rhs: borrow<blob>) -> blob; | ||
| %[method][get]blob.position: func(self: borrow<blob>) -> u64; | ||
| %[method][set]blob.position: func(self: borrow<blob>, value: u64); | ||
| %[static][get]blob.max-size: func() -> u64; | ||
| %[static][set]blob.max-size: func(value: u64) -> result<_, string>; | ||
| %[constructor]blob2: func(init: list<u8>) -> result<blob2>; | ||
| ``` | ||
|
|
||
| These `%`-prefixed strings embed the resource type name so that bindings | ||
| generators can generate idiomatic syntax for the target language or (for | ||
| languages like C) fall back to an appropriately-prefixed free function name. | ||
|
|
||
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.