Skip to content
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

Merged
merged 20 commits into from
Oct 17, 2016
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
175 changes: 161 additions & 14 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,15 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMA-262
text: %ErrorPrototype%
text: %ObjProto_toString%
text: %IteratorPrototype%
url: sec-object-internal-methods-and-internal-slots
text: [[GetOwnProperty]]
text: [[OwnPropertyKeys]]
text: %ObjectPrototype%; url: sec-properties-of-the-object-prototype-object
text: %FunctionPrototype%; url: sec-properties-of-the-function-prototype-object
text: %Promise%; url: sec-promise-constructor
text: Property Descriptor; url: sec-property-descriptor-specification-type
url: sec-property-descriptor-specification-type
text: Property Descriptor
text: [[Enumerable]]
text: array index; url: sec-array-exotic-objects
text: OrdinaryGetOwnProperty; url: sec-ordinarygetownproperty
text: OrdinaryDefineOwnProperty; url: sec-ordinarydefineownproperty
Expand Down Expand Up @@ -106,6 +111,8 @@ 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: DefinePropertyOrThrow; url: sec-definepropertyorthrow
url: sec-code-realms
text: Realm
Expand All @@ -120,6 +127,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
Expand Down Expand Up @@ -1358,8 +1366,10 @@ The type of the attribute, after resolving typedefs, must not be a

* a [=sequence type=]
* a [=dictionary type=]
* a [=record type=]
* a [=union type=]
that has a nullable or non-nullable sequence type or dictionary
that has a nullable or non-nullable sequence type, dictionary,
or record
as one of its [=flattened member types=]

The attribute is <dfn id="dfn-read-only" export>read only</dfn> if the
Expand Down Expand Up @@ -1750,15 +1760,15 @@ corresponding argument omitted.
conversion of <emu-val>undefined</emu-val> to be used (i.e., <emu-val>false</emu-val>).
</p>

If the type of an argument is a [=dictionary type=]
If the type of an argument is a [=dictionary type=] or [=record type=]
or a [=union type=] that has a
dictionary type as one of its [=flattened member types=],
dictionary or record type as one of its [=flattened member types=],
and that dictionary type and its ancestors have no [=required dictionary member|required members=],
and the argument is either the final argument or is followed only by
[=optional arguments=], then
the argument must be specified as optional.
Such arguments are always considered to have a
[=optional argument/default value=] of an empty dictionary,
[=optional argument/default value=] of an empty dictionary or record, as appropriate,
unless otherwise specified.

<div class="note">
Expand Down Expand Up @@ -3249,7 +3259,6 @@ extended attribute’s [=takes a named argument list|named argument list=].

Two types are <dfn id="dfn-distinguishable" export>distinguishable</dfn> if
at most one of the two [=includes a nullable type=]
or is a [=dictionary type=],
and at least one of the following three conditions is true:

1. The two types (taking their [=inner types=]
Expand Down Expand Up @@ -3280,7 +3289,7 @@ and at least one of the following three conditions is true:
<span>callback function</span>
</div></th>
<th><div>
<span>dictionary</span>
<span>dictionary/record</span>
</div></th>
<th><div>
<span>sequence&lt;|T|&gt;</span>
Expand Down Expand Up @@ -3389,7 +3398,7 @@ and at least one of the following three conditions is true:
<td>●</td>
</tr>
<tr>
<th>dictionary</th>
<th>dictionary/record</th>
<td class="belowdiagonal"></td>
<td class="belowdiagonal"></td>
<td class="belowdiagonal"></td>
Expand Down Expand Up @@ -5160,9 +5169,7 @@ type.
NonAnyType :
PrimitiveType Null
PromiseType Null
"ByteString" Null
"DOMString" Null
"USVString" Null
StringType Null
identifier Null
"sequence" "&lt;" Type "&gt;" Null
"object" Null
Expand All @@ -5171,6 +5178,7 @@ type.
"DOMException" Null
BufferRelatedType Null
"FrozenArray" "&lt;" Type "&gt;" Null
RecordType Null
</pre>

<div data-fill-with="grammar-ConstType"></div>
Expand Down Expand Up @@ -5214,11 +5222,23 @@ type.
ε
</pre>

<pre class="grammar" id="prod-StringType">
StringType :
"ByteString"
"DOMString"
"USVString"
</pre>

<pre class="grammar" id="prod-PromiseType">
PromiseType :
"Promise" "&lt;" ReturnType "&gt;"
</pre>

<pre class="grammar" id="prod-RecordType">
RecordType :
"record" "&lt;" StringType "," Type "&gt;"
</pre>

<pre class="grammar" id="prod-Null">
Null :
"?"
Expand Down Expand Up @@ -5664,10 +5684,11 @@ character after an existing type. The inner type must not
be {{any}},
another nullable type, or a [=union type=]
that itself has [=includes a nullable type=]
or has a dictionary type as one of its
or has a dictionary or record type as one of its
[=flattened member types=].

Note: Although dictionary types can in general be nullable, they cannot when used
Note: Although dictionary and record types can in general be nullable,
they cannot when used
as the type of an operation argument or a dictionary member.

Nullable type constant values in IDL are represented in the same way that
Expand Down Expand Up @@ -5737,6 +5758,32 @@ is the concatenation of the type name for |T| and
the string “Sequence”.


<h4 id="idl-record" dictionary lt="record">Record types — record&lt;|K|, |V|&gt;</h4>

A <dfn export>record type</dfn> is a parameterized type
whose values are ordered associative arrays mapping instances of |K| to
instances of |V|. The (key, value) pairs are called
<dfn for="record">mappings</dfn>.
The order of a record's mappings is determined when the record value is created.

|K| must be one of {{DOMString}}, {{USVString}}, or {{ByteString}}.

Records are always passed by value. In language bindings where a record
is represented by an object of some kind, passing a record
to a [=platform object=] will not result in a reference to the record
being kept by that object. Similarly, any record 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 record value in IDL.

Records must not be used as the type of an [=attribute=] or
[=constant=].

The [=type name=] of a record type is the concatenation of the type
name for |K|, the type name for |V| and the string “Record”.


<h4 oldids="dom-promise" id="idl-promise" interface lt="Promise|Promise&lt;T&gt;">Promise types — Promise&lt;|T|&gt;</h4>

A <dfn id="dfn-promise-type" export>promise type</dfn> is a parameterized type
Expand Down Expand Up @@ -5833,7 +5880,7 @@ be used as a [=member types|union member type=].
The [=number of nullable member types=]
of a [=union type=] must
be 0 or 1, and if it is 1 then the union type must also not have
a [=dictionary type=] in its
a [=dictionary type=] or [=record type=] in its
[=flattened member types=].

A type <dfn id="dfn-includes-a-nullable-type" export>includes a nullable type</dfn> if:
Expand Down Expand Up @@ -7529,6 +7576,101 @@ iterable |iterable| and an iterator getter
</div>


<h4 id="es-record">Records — record&lt;|K|, |V|&gt;</h4>

IDL {{record}}&lt;|K|, |V|&gt; values are represented by
ECMAScript <emu-val>Object</emu-val> values.

<p id="es-to-record">
An ECMAScript value |O| is [=converted to an IDL value|converted=] to an IDL <code>{{record}}&lt;|K|, |V|></code> value as follows:
</p>

<ol class="algorithm">
1. Let |result| be a new empty instance of <code>{{record}}&lt;|K|, |V|></code>.
1. If [=Type=](|O|) is <emu-val>Undefined</emu-val> or <emu-val>Null</emu-val>,
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. Is there documentation for that?

Copy link
Member

Choose a reason for hiding this comment

The 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|.
Copy link
Member

@domenic domenic Oct 1, 2016

Choose a reason for hiding this comment

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

This should throw a TypeError for consistency with closed dictionaries.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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."

Copy link
Member

Choose a reason for hiding this comment

The 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>.
Copy link
Member

Choose a reason for hiding this comment

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

This check should not be there, for consistency with closed dictionaries.

Copy link
Member Author

Choose a reason for hiding this comment

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

Again, closed dictionaries do throw a TypeError for non-Object,Null,Undefined arguments: https://heycam.github.io/webidl/#es-to-dictionary

Copy link
Member

Choose a reason for hiding this comment

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

Again, can you quote? There's no such step that I can see; only for RegExp, which we are planning to remove (#148 #145).

Copy link
Member

Choose a reason for hiding this comment

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

As above, my bad; disregard this.

1. Let |keys| be [=?=] |O|.[=[[OwnPropertyKeys]]=]().
Copy link
Member

Choose a reason for hiding this comment

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

IMO the links for the internal methods and [[enumerable]] are not good to add here. If we want to do that more generally, we can, but we shouldn't do it just in this section.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

1. Repeat, for each element |key| of |keys| in [=List=] order:
1. Let |desc| be [=?=] |O|.[=[[GetOwnProperty]]=](|key|).
1. If |desc| is not <emu-val>undefined</emu-val>
and |desc|.[=[[Enumerable]]=] is <emu-val>true</emu-val>:
1. Let |typedKey| be |key| [=converted to an IDL value=] of type |K|.
1. Let |value| be [=?=] [=Get=](|O|, |key|).
Copy link
Member Author

Choose a reason for hiding this comment

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

@domenic Done. Note that it's a little weird to call Get() when we already have the descriptor, but that's what Object.assign() does so oh well.

1. Let |typedValue| be |value| [=converted to an IDL value=] of type |V|.
1. If |typedKey| is already a key in |result|, set its value to |typedValue|.

Note: This can happen when |O| is a proxy object.
1. Otherwise, append to |result| a mapping from |typedKey| to |typedValue|.
Copy link
Member

Choose a reason for hiding this comment

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

Link mapping? And use (|typedKey|, |typedValue|) notation?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

1. Return |result|.
</ol>

<p id="record-to-es">
An IDL <code>{{record}}&lt;…></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 [=record/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 <emu-val>true</emu-val>.
1. Return |result|.
</ol>

<div class="example" id="example-es-record">
Passing the ECMAScript value <code>{b: 3, a: 4}</code> to a
Copy link
Member

Choose a reason for hiding this comment

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

s/to/as/

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

<code>{{record}}&lt;DOMString, double></code> argument
would result in the IDL value <code>[ ("b", 3), ("a", 4) ]</code>.
Copy link
Member

Choose a reason for hiding this comment

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

I don't know about this new array-ish notation. I'd instead say something like "a record containing the two mappings ("b", 3) and ("a", 4), in that order." Hmm, I guess it makes an appearance later in the table too, where it's more necessary for space reasons... not sure what to do. At least, remove the <code> from this one, IMO.

I think this also contradicts the statement "There is no way to represent a constant record value in IDL."

Copy link
Member Author

Choose a reason for hiding this comment

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

I think

There is no way to represent a constant record value in IDL.

means that we don't have syntax for it in the WebIDL grammar. The phrase,

a record containing the two mappings ("b", 3) and ("a", 4), in that order.

would still "represent a constant record value", but I'd like to have a more concise way to do it.

Copy link
Member

Choose a reason for hiding this comment

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

I'm still hesitant about using notation that's reminiscent of JS arrays, and is not defined anywhere. Maybe use some exotic type of bracket (such as the french double quotes that ES uses), and define it in the same place you define the mapping notation?

Copy link
Member Author

Choose a reason for hiding this comment

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

Perfect, thanks!


Records only consider [=own property|own=] [=enumerable=]
properties, so given an IDL operation
<code>record&lt;DOMString, double>
identity(record&lt;DOMString, 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>

Record 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>{{record}}&lt;ByteString, double></code></td>
<td><emu-val>TypeError</emu-val></td>
</tr>
<tr>
<td><code>{"\uD83D": 1}</code></td>
<td><code>{{record}}&lt;USVString, double></code></td>
<td>[ ("\uFFFD", 1) ]</td>
</tr>
<tr>
<td><code>{"\uD83D": {hello: "world"}}</code></td>
<td><code>{{record}}&lt;DOMString, double></code></td>
<td>[ ("\uD83D", 0) ]</td>
</tr>
</table>
</div>


<h4 id="es-promise">Promise types — Promise&lt;|T|&gt;</h4>

IDL [=promise type=] values are
Expand Down Expand Up @@ -7695,6 +7837,9 @@ represented by ECMAScript values that correspond to the union’s
1. If |types| includes a [=dictionary type=], then return the
result of [=converted to an IDL value|converting=]
|V| to that dictionary type.
1. If |types| includes a [=record type=], then return the
result of [=converted to an IDL value|converting=]
|V| to that dictionary type.
Copy link
Member

Choose a reason for hiding this comment

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

s/dictionary/record

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops, done.

1. If |types| includes a [=callback interface=]
type, then return the result of
[=converted to an IDL value|converting=]
Expand Down Expand Up @@ -9843,6 +9988,7 @@ and a list of IDL values or the special value “missing”. The algorithm beha
and there is an entry in |S| that has one of the following types at position |i| of its type list,
* a [=nullable type=]
* a [=dictionary type=]
* a [=record type=]
* a [=union type=] that
[=includes a nullable type=] or that
has a [=dictionary type=] in its [=flattened member types|flattened members=]
Expand Down Expand Up @@ -9955,6 +10101,7 @@ and a list of IDL values or the special value “missing”. The algorithm beha
there is an entry in |S| that has one of the following types at position |i| of its type list,
* a [=callback interface=] type
* a [=dictionary type=]
* a [=record type=]
* {{object}}
* a [=nullable type|nullable=] version of any of the above types
* a [=union type=] or [=nullable type|nullable=] union type
Expand Down