-
Notifications
You must be signed in to change notification settings - Fork 157
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
Specify open dictionaries. #180
Changes from 11 commits
2e084f4
2008d7f
2b5f2a8
3dc8ca9
1a8ae76
05252bf
747ba1e
6646a17
7c5c25a
756f55d
399fc10
59c34ac
06ca8e2
84d1bc3
0386c5d
fc878b0
fe44f9c
665d408
9e22b41
20c46cc
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 |
---|---|---|
|
@@ -106,6 +106,10 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMA-262 | |
text: Set; url: sec-set-o-p-v-throw | ||
text: IsConstructor; url: sec-isconstructor | ||
text: Construct; url: sec-construct | ||
text: own property; url: sec-own-property | ||
text: enumerable; url: sec-property-attributes | ||
text: HasOwnProperty; url: sec-hasownproperty | ||
text: EnumerableOwnProperties; url: sec-enumerableownproperties | ||
text: DefinePropertyOrThrow; url: sec-definepropertyorthrow | ||
url: sec-code-realms | ||
text: Realm | ||
|
@@ -120,6 +124,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMA-262 | |
text: ECMA-262 section 9.1.8; url: sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver | ||
text: ECMA-262 section 19.2.2.3; url: sec-function-@@create | ||
text: ECMA-262 section 19.2.3.8; url: sec-function.prototype-@@hasinstance | ||
text: List; url: sec-list-and-record-specification-type | ||
text: Array methods; url: sec-properties-of-the-array-prototype-object | ||
text: typed arrays; url: sec-typedarray-objects | ||
text: GetMethod; url: sec-getmethod | ||
|
@@ -5171,6 +5176,7 @@ type. | |
"DOMException" Null | ||
BufferRelatedType Null | ||
"FrozenArray" "<" Type ">" Null | ||
OpenDictionaryType Null | ||
</pre> | ||
|
||
<div data-fill-with="grammar-ConstType"></div> | ||
|
@@ -5219,6 +5225,17 @@ type. | |
"Promise" "<" ReturnType ">" | ||
</pre> | ||
|
||
<pre class="grammar" id="prod-OpenDictionaryType"> | ||
OpenDictionaryType : | ||
"OpenDictionary" "<" Type OpenDictionaryTypeMaybeValue | ||
</pre> | ||
|
||
<pre class="grammar" id="prod-OpenDictionaryTypeMaybeValue"> | ||
OpenDictionaryTypeMaybeValue : | ||
">" | ||
"," Type ">" | ||
</pre> | ||
|
||
<pre class="grammar" id="prod-Null"> | ||
Null : | ||
"?" | ||
|
@@ -5737,6 +5754,35 @@ is the concatenation of the type name for |T| and | |
the string “Sequence”. | ||
|
||
|
||
<h4 id="idl-open-dictionary" dictionary lt="OpenDictionary">Open dictionary types — OpenDictionary<[|K|,] |V|></h4> | ||
|
||
An <dfn export>open dictionary type</dfn> is a parameterized [=dictionary type=] | ||
whose values are ordered associative arrays mapping instances of |K| to | ||
instances of |V|. The (key, value) pairs are called | ||
<dfn for="OpenDictionary">mappings</dfn>. | ||
The order of an open dictionary's mappings is determined when the open | ||
dictionary value is created. | ||
|
||
|K| must be one of {{DOMString}}, {{USVString}}, or {{ByteString}}. In a type | ||
name of the form <code>OpenDictionary<|V|></code>, which omits |K|, |K| defaults | ||
to {{DOMString}}. | ||
|
||
Open dictionaries are always passed by value. In language bindings where an open | ||
dictionary is represented by an object of some kind, passing an open dictionary | ||
to a [=platform object=] will not result in a reference to the open dictionary | ||
being kept by that object. Similarly, any open dictionary returned from a | ||
platform object will be a copy and modifications made to it will not be visible | ||
to the platform object. | ||
|
||
There is no way to represent a constant open dictionary value in IDL. | ||
|
||
Open dictionaries must not be used as the type of an [=attribute=] or | ||
[=constant=]. | ||
|
||
The [=type name=] of an open dictionary type is the concatenation of the type | ||
name for |K|, the type name for |V| and the string “OpenDictionary”. | ||
|
||
|
||
<h4 oldids="dom-promise" id="idl-promise" interface lt="Promise|Promise<T>">Promise types — Promise<|T|></h4> | ||
|
||
A <dfn id="dfn-promise-type" export>promise type</dfn> is a parameterized type | ||
|
@@ -7529,6 +7575,95 @@ iterable |iterable| and an iterator getter | |
</div> | ||
|
||
|
||
<h4 id="es-open-dictionary">Open dictionaries — OpenDictionary<[|K|,] |V|></h4> | ||
|
||
IDL {{OpenDictionary}}<|K|, |V|> values are represented by | ||
ECMAScript <emu-val>Object</emu-val> values. | ||
|
||
<p id="es-to-open-dictionary"> | ||
An ECMAScript value |O| is [=converted to an IDL value|converted=] to an IDL <code>{{OpenDictionary}}<|K|, |V|></code> value as follows: | ||
</p> | ||
|
||
<ol class="algorithm"> | ||
1. Let |result| be a new empty instance of <code>{{OpenDictionary}}<|K|, |V|></code>. | ||
1. If [=Type=](|O|) is <emu-val>Undefined</emu-val> or <emu-val>Null</emu-val>, | ||
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. Nit: types (Undefined, Null, and Object) are not emu-vals. That is reserved for language values, not spec values. 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. Done. Is there documentation for that? 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. https://bterlson.github.io/ecmarkup/#editorial-conventions I guess, although that's not the most obvious place to look given that we're basically only using emu-val. |
||
return |result|. | ||
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 should throw a TypeError for consistency with closed dictionaries. 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 was in response to @bzbarsky's «Do we want to allow passing null or undefined to mean "empty open dictionary" the way we do for dictionaries?», and he's right that closed dictionaries treat null and undefined as empty. 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. Uh, what are you reading? Can you quote? I see there a very clear step 1 that says "If Type(V) is not Undefined, Null or Object, then throw a TypeError." 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. Oh, wow, I totally misread the sentence that I quoted. Sorry. This seems good then. |
||
1. If [=Type=](|O|) is not <emu-val>Object</emu-val>, | ||
<a lt="es throw">throw a <emu-val>TypeError</emu-val></a>. | ||
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 check should not be there, for consistency with closed dictionaries. 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. Again, closed dictionaries do throw a 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. 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. As above, my bad; disregard this. |
||
1. Let |keys| be [=?=] [=EnumerableOwnProperties=](|O|, "key"). | ||
1. Repeat, for each element |key| of |keys| in [=List=] order: | ||
1. If [=!=] [=HasOwnProperty=](|O|, |key|) is <code>true</code>, then: | ||
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. Interesting. So this comes in to play if enumerating/getting causes side effects that delete the property. I don't think that's great semantics though. I think instead you want to inline the EnumerableOwnProperties algorithm here yourself, instead of separating the enumeration pass from the conversion/Get pass. 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. I'd like you, @annevk, and @bzbarsky to come to some conclusion about this. So far, @annevk wondered if we should match StructuredClone(), and @bzbarsky said that MozMap is like StructuredClone but omits the HasOwnProperty() check (so, I think we'd include the key in the map but initialize it from 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. 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. Maybe @ajklein can tell us? Would rather we make an informed decision here. 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. I doubt there is much in the way of legacy stuff around structured clone here, honestly. I think structured clone was created back before there were proxies in ES (but I could totally be wrong about that!). If that were the case, then the [[Get]] calls in that algorithm would have been the only thing that could have changed whether props exist, so it would have made sense to double-check that a previos [[Get]] has not removed the property or something. In the proxy world, where HasOwnProperty can itself lie and remove the property or whatnot, and where [[GetOwnProperty]] can do likewise, it's not clear to me that there is any sort of sanity that can be provided in the face of a "malicious" input object that just tries to mess with you. So the only question is whether there are sane cases which would benefit from the extra double-checking. In EnumerableOwnProperties(), the double-checking that exists is just a side-effect of checking for enumerability. That happens to also check that the property is still own, because it uses [[GetOwnProperty]]. But that looks to me like an accidental byproduct of the enumerability check. Note that structured clone, as specified, is buggy: if one of those [[GetOwnProperty]] calls in step 22.3.2.1.1 returns undefined, what happens? Also, it's not clear to me whether UAs actually match the structured clone spec. Might be worth checking that... I guess all of that is to say that I see no obvious problem with removing the HasOwnProperty check in structured clone, with the understanding that this may cause the clone to have own props where the clonee doesn't have any by the time cloning is done... but that's possible now anyway, afaict. 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. I think what you're missing is that structured clone fails on proxies in step 19. |
||
1. Let |typedKey| be |key| [=converted to an IDL value=] of type |K|. | ||
1. Let |value| be [=?=] [=Get=](|O|, |key|). | ||
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. @domenic Done. Note that it's a little weird to call |
||
1. Let |typedValue| be |value| [=converted to an IDL value=] of type |V|. | ||
1. Assert: |typedKey| is not yet a key in |result|. | ||
1. Append to |result| a mapping from |typedKey| to |typedValue|. | ||
1. Return |result|. | ||
</ol> | ||
|
||
<p id="open-dictionary-to-es"> | ||
An IDL <code>{{OpenDictionary}}<…></code> value |D| is [=converted to an | ||
ECMAScript value|converted=] to an ECMAScript value as follows: | ||
</p> | ||
|
||
<ol class="algorithm"> | ||
1. Let |result| be [=!=] [=ObjectCreate=]([=%ObjectPrototype%=]). | ||
1. Repeat, for each [=OpenDictionary/mapping=] (|key|, |value|) in |D|: | ||
1. Let |esKey| be |key| [=converted to an ECMAScript value=]. | ||
1. Let |esValue| be |value| [=converted to an ECMAScript value=]. | ||
1. Let |created| be [=!=] [=CreateDataProperty=](|result|, |esKey|, |esValue|). | ||
1. Assert: |created| is <code>true</code>. | ||
1. Return |result|. | ||
</ol> | ||
|
||
<div class="example" id="example-es-open-dictionary"> | ||
Passing the ECMAScript value <code>{b: 3, a: 4}</code> to a | ||
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. s/to/as/ 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. Done. |
||
<code>{{OpenDictionary}}<DOMString, double></code> argument | ||
would result in the IDL value <code>[ "b" ⇒ 3, "a" ⇒ 4 ]</code>. | ||
|
||
Open dictionaries only consider [=own property|own=] [=enumerable=] properties, so given an IDL operation | ||
<code>OpenDictionary<double> identity(OpenDictionary<double> arg)</code> which | ||
returns its argument, the following code passes its assertions: | ||
|
||
<pre highlight="js"> | ||
let proto = {a: 3, b: 4}; | ||
let obj = {__proto__: proto, d: 5, c: 6} | ||
Object.defineProperty(obj, "e", {value: 7, enumerable: false}); | ||
let result = identity(obj); | ||
console.assert(result.a === undefined); | ||
console.assert(result.b === undefined); | ||
console.assert(result.e === undefined); | ||
let entries = Object.entries(result); | ||
console.assert(entries[0][0] === "d"); | ||
console.assert(entries[0][1] === 5); | ||
console.assert(entries[1][0] === "c"); | ||
console.assert(entries[1][1] === 6); | ||
</pre> | ||
|
||
Open dictionary keys and values can be constrained, although keys can only be | ||
constrained among the three string types. | ||
The following conversions have the described results: | ||
<table class="data"> | ||
<thead><th>Value</th><th>Passed to type</th><th>Result</th></thead> | ||
<tr> | ||
<td><code>{"😞": 1}</code></td> | ||
<td><code>{{OpenDictionary}}<ByteString, double></code></td> | ||
<td><emu-val>TypeError</emu-val></td> | ||
</tr> | ||
<tr> | ||
<td><code>{"\uD83D": 1}</code></td> | ||
<td><code>{{OpenDictionary}}<USVString, double></code></td> | ||
<td>[ "\uFFFD" ⇒ 1 ]</td> | ||
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 seems you are inconsistent about notation for the mappings; here you use an arrow, whereas above you use "Repeat, for each mapping (key, value) in D". Both seem reasonable, but it would be good to pick one and maybe mention it in the section that introduces the concept of mappings. 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. Yeah. I don't want to make WebIDL's users type a non-ascii character in order to iterate over a map, so (key, value) is better for that. I can change to that here too. 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. Done. |
||
</tr> | ||
<tr> | ||
<td><code>{"\uD83D": {hello: "world"}}</code></td> | ||
<td><code>{{OpenDictionary}}<DOMString, double></code></td> | ||
<td>[ "\uD83D" ⇒ 0 ]</td> | ||
</tr> | ||
</table> | ||
</div> | ||
|
||
|
||
<h4 id="es-promise">Promise types — Promise<|T|></h4> | ||
|
||
IDL [=promise type=] values are | ||
|
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.
Before switching this to require both arguments, I'd like a couple more people to agree with @tobie.
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.
It seems fine to me if you can also typedef the thing. We don't have lots of these and first argument being optional is weird.
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.
Ok, I've made it require both arguments.